passagemath-combinat 10.6.42__cp314-cp314t-win_amd64.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.42.dist-info/DELVEWHEEL +2 -0
- passagemath_combinat-10.6.42.dist-info/METADATA +160 -0
- passagemath_combinat-10.6.42.dist-info/RECORD +401 -0
- passagemath_combinat-10.6.42.dist-info/WHEEL +5 -0
- passagemath_combinat-10.6.42.dist-info/top_level.txt +3 -0
- passagemath_combinat.libs/libgmp-10-3a5f019e2510aeaad918cab2b57a689d.dll +0 -0
- passagemath_combinat.libs/libsymmetrica-3-7dcf900932804d0df5fd0919b4668720.dll +0 -0
- sage/algebras/affine_nil_temperley_lieb.py +263 -0
- sage/algebras/all.py +24 -0
- sage/algebras/all__sagemath_combinat.py +35 -0
- sage/algebras/askey_wilson.py +935 -0
- sage/algebras/associated_graded.py +345 -0
- sage/algebras/cellular_basis.py +350 -0
- sage/algebras/cluster_algebra.py +2766 -0
- sage/algebras/down_up_algebra.py +860 -0
- sage/algebras/free_algebra.py +1698 -0
- sage/algebras/free_algebra_element.py +345 -0
- sage/algebras/free_algebra_quotient.py +405 -0
- sage/algebras/free_algebra_quotient_element.py +295 -0
- sage/algebras/free_zinbiel_algebra.py +885 -0
- sage/algebras/hall_algebra.py +783 -0
- sage/algebras/hecke_algebras/all.py +4 -0
- sage/algebras/hecke_algebras/ariki_koike_algebra.py +1796 -0
- sage/algebras/hecke_algebras/ariki_koike_specht_modules.py +475 -0
- sage/algebras/hecke_algebras/cubic_hecke_algebra.py +3520 -0
- sage/algebras/hecke_algebras/cubic_hecke_base_ring.py +1473 -0
- sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py +1079 -0
- sage/algebras/iwahori_hecke_algebra.py +3095 -0
- sage/algebras/jordan_algebra.py +1773 -0
- sage/algebras/lie_conformal_algebras/abelian_lie_conformal_algebra.py +113 -0
- sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py +156 -0
- sage/algebras/lie_conformal_algebras/all.py +18 -0
- sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra.py +134 -0
- sage/algebras/lie_conformal_algebras/examples.py +43 -0
- sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra.py +131 -0
- sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py +139 -0
- sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py +174 -0
- sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py +167 -0
- sage/algebras/lie_conformal_algebras/freely_generated_lie_conformal_algebra.py +107 -0
- sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py +135 -0
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py +353 -0
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +236 -0
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py +78 -0
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py +328 -0
- sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra.py +117 -0
- sage/algebras/lie_conformal_algebras/neveu_schwarz_lie_conformal_algebra.py +86 -0
- sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra.py +82 -0
- sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra.py +205 -0
- sage/algebras/nil_coxeter_algebra.py +191 -0
- sage/algebras/q_commuting_polynomials.py +673 -0
- sage/algebras/q_system.py +608 -0
- sage/algebras/quantum_clifford.py +959 -0
- sage/algebras/quantum_groups/ace_quantum_onsager.py +693 -0
- sage/algebras/quantum_groups/all.py +9 -0
- sage/algebras/quantum_groups/fock_space.py +2219 -0
- sage/algebras/quantum_groups/q_numbers.py +207 -0
- sage/algebras/quantum_groups/quantum_group_gap.py +2695 -0
- sage/algebras/quantum_groups/representations.py +591 -0
- sage/algebras/quantum_matrix_coordinate_algebra.py +1006 -0
- sage/algebras/quantum_oscillator.py +623 -0
- sage/algebras/quaternion_algebra.py +20 -0
- sage/algebras/quaternion_algebra_element.py +55 -0
- sage/algebras/rational_cherednik_algebra.py +525 -0
- sage/algebras/schur_algebra.py +670 -0
- sage/algebras/shuffle_algebra.py +1011 -0
- sage/algebras/splitting_algebra.py +779 -0
- sage/algebras/tensor_algebra.py +709 -0
- sage/algebras/yangian.py +1082 -0
- sage/algebras/yokonuma_hecke_algebra.py +1018 -0
- sage/all__sagemath_combinat.py +44 -0
- sage/combinat/SJT.py +255 -0
- sage/combinat/affine_permutation.py +2405 -0
- sage/combinat/algebraic_combinatorics.py +55 -0
- sage/combinat/all.py +53 -0
- sage/combinat/all__sagemath_combinat.py +195 -0
- sage/combinat/alternating_sign_matrix.py +2063 -0
- sage/combinat/baxter_permutations.py +346 -0
- sage/combinat/bijectionist.py +3220 -0
- sage/combinat/binary_recurrence_sequences.py +1180 -0
- sage/combinat/blob_algebra.py +685 -0
- sage/combinat/catalog_partitions.py +27 -0
- sage/combinat/chas/all.py +23 -0
- sage/combinat/chas/fsym.py +1180 -0
- sage/combinat/chas/wqsym.py +2601 -0
- sage/combinat/cluster_complex.py +326 -0
- sage/combinat/colored_permutations.py +2039 -0
- sage/combinat/colored_permutations_representations.py +964 -0
- sage/combinat/composition_signed.py +142 -0
- sage/combinat/composition_tableau.py +855 -0
- sage/combinat/constellation.py +1729 -0
- sage/combinat/core.py +751 -0
- sage/combinat/counting.py +12 -0
- sage/combinat/crystals/affine.py +742 -0
- sage/combinat/crystals/affine_factorization.py +518 -0
- sage/combinat/crystals/affinization.py +331 -0
- sage/combinat/crystals/alcove_path.py +2013 -0
- sage/combinat/crystals/all.py +22 -0
- sage/combinat/crystals/bkk_crystals.py +141 -0
- sage/combinat/crystals/catalog.py +115 -0
- sage/combinat/crystals/catalog_elementary_crystals.py +18 -0
- sage/combinat/crystals/catalog_infinity_crystals.py +33 -0
- sage/combinat/crystals/catalog_kirillov_reshetikhin.py +18 -0
- sage/combinat/crystals/crystals.py +257 -0
- sage/combinat/crystals/direct_sum.py +260 -0
- sage/combinat/crystals/elementary_crystals.py +1251 -0
- sage/combinat/crystals/fast_crystals.py +441 -0
- sage/combinat/crystals/fully_commutative_stable_grothendieck.py +1205 -0
- sage/combinat/crystals/generalized_young_walls.py +1076 -0
- sage/combinat/crystals/highest_weight_crystals.py +436 -0
- sage/combinat/crystals/induced_structure.py +695 -0
- sage/combinat/crystals/infinity_crystals.py +730 -0
- sage/combinat/crystals/kac_modules.py +863 -0
- sage/combinat/crystals/kirillov_reshetikhin.py +4196 -0
- sage/combinat/crystals/kyoto_path_model.py +497 -0
- sage/combinat/crystals/letters.cp314t-win_amd64.pyd +0 -0
- sage/combinat/crystals/letters.pxd +79 -0
- sage/combinat/crystals/letters.pyx +3056 -0
- sage/combinat/crystals/littelmann_path.py +1518 -0
- sage/combinat/crystals/monomial_crystals.py +1262 -0
- sage/combinat/crystals/multisegments.py +462 -0
- sage/combinat/crystals/mv_polytopes.py +467 -0
- sage/combinat/crystals/pbw_crystal.py +511 -0
- sage/combinat/crystals/pbw_datum.cp314t-win_amd64.pyd +0 -0
- sage/combinat/crystals/pbw_datum.pxd +4 -0
- sage/combinat/crystals/pbw_datum.pyx +487 -0
- sage/combinat/crystals/polyhedral_realization.py +372 -0
- sage/combinat/crystals/spins.cp314t-win_amd64.pyd +0 -0
- sage/combinat/crystals/spins.pxd +21 -0
- sage/combinat/crystals/spins.pyx +756 -0
- sage/combinat/crystals/star_crystal.py +290 -0
- sage/combinat/crystals/subcrystal.py +464 -0
- sage/combinat/crystals/tensor_product.py +1177 -0
- sage/combinat/crystals/tensor_product_element.cp314t-win_amd64.pyd +0 -0
- sage/combinat/crystals/tensor_product_element.pxd +35 -0
- sage/combinat/crystals/tensor_product_element.pyx +1870 -0
- sage/combinat/crystals/virtual_crystal.py +420 -0
- sage/combinat/cyclic_sieving_phenomenon.py +204 -0
- sage/combinat/debruijn_sequence.cp314t-win_amd64.pyd +0 -0
- sage/combinat/debruijn_sequence.pyx +355 -0
- sage/combinat/decorated_permutation.py +270 -0
- sage/combinat/degree_sequences.cp314t-win_amd64.pyd +0 -0
- sage/combinat/degree_sequences.pyx +588 -0
- sage/combinat/derangements.py +527 -0
- sage/combinat/descent_algebra.py +1008 -0
- sage/combinat/diagram.py +1551 -0
- sage/combinat/diagram_algebras.py +5886 -0
- sage/combinat/dyck_word.py +4349 -0
- sage/combinat/e_one_star.py +1623 -0
- sage/combinat/enumerated_sets.py +123 -0
- sage/combinat/expnums.cp314t-win_amd64.pyd +0 -0
- sage/combinat/expnums.pyx +148 -0
- sage/combinat/fast_vector_partitions.cp314t-win_amd64.pyd +0 -0
- sage/combinat/fast_vector_partitions.pyx +346 -0
- sage/combinat/fqsym.py +1977 -0
- sage/combinat/free_dendriform_algebra.py +954 -0
- sage/combinat/free_prelie_algebra.py +1141 -0
- sage/combinat/fully_commutative_elements.py +1077 -0
- sage/combinat/fully_packed_loop.py +1523 -0
- sage/combinat/gelfand_tsetlin_patterns.py +1409 -0
- sage/combinat/gray_codes.py +311 -0
- sage/combinat/grossman_larson_algebras.py +667 -0
- sage/combinat/growth.py +4352 -0
- sage/combinat/hall_polynomial.py +188 -0
- sage/combinat/hillman_grassl.py +866 -0
- sage/combinat/integer_matrices.py +329 -0
- sage/combinat/integer_vectors_mod_permgroup.py +1238 -0
- sage/combinat/k_tableau.py +4564 -0
- sage/combinat/kazhdan_lusztig.py +215 -0
- sage/combinat/key_polynomial.py +885 -0
- sage/combinat/knutson_tao_puzzles.py +2286 -0
- sage/combinat/lr_tableau.py +311 -0
- sage/combinat/matrices/all.py +24 -0
- sage/combinat/matrices/hadamard_matrix.py +3790 -0
- sage/combinat/matrices/latin.py +2912 -0
- sage/combinat/misc.py +401 -0
- sage/combinat/multiset_partition_into_sets_ordered.py +3541 -0
- sage/combinat/ncsf_qsym/all.py +21 -0
- sage/combinat/ncsf_qsym/combinatorics.py +317 -0
- sage/combinat/ncsf_qsym/generic_basis_code.py +1427 -0
- sage/combinat/ncsf_qsym/ncsf.py +5637 -0
- sage/combinat/ncsf_qsym/qsym.py +4053 -0
- sage/combinat/ncsf_qsym/tutorial.py +447 -0
- sage/combinat/ncsym/all.py +21 -0
- sage/combinat/ncsym/bases.py +855 -0
- sage/combinat/ncsym/dual.py +593 -0
- sage/combinat/ncsym/ncsym.py +2076 -0
- sage/combinat/necklace.py +551 -0
- sage/combinat/non_decreasing_parking_function.py +634 -0
- sage/combinat/nu_dyck_word.py +1474 -0
- sage/combinat/output.py +861 -0
- sage/combinat/parallelogram_polyomino.py +4326 -0
- sage/combinat/parking_functions.py +1602 -0
- sage/combinat/partition_algebra.py +1998 -0
- sage/combinat/partition_kleshchev.py +1982 -0
- sage/combinat/partition_shifting_algebras.py +584 -0
- sage/combinat/partition_tuple.py +3114 -0
- sage/combinat/path_tableaux/all.py +13 -0
- sage/combinat/path_tableaux/catalog.py +29 -0
- sage/combinat/path_tableaux/dyck_path.py +380 -0
- sage/combinat/path_tableaux/frieze.py +476 -0
- sage/combinat/path_tableaux/path_tableau.py +728 -0
- sage/combinat/path_tableaux/semistandard.py +510 -0
- sage/combinat/perfect_matching.py +779 -0
- sage/combinat/plane_partition.py +3300 -0
- sage/combinat/q_bernoulli.cp314t-win_amd64.pyd +0 -0
- sage/combinat/q_bernoulli.pyx +128 -0
- sage/combinat/quickref.py +81 -0
- sage/combinat/recognizable_series.py +2051 -0
- sage/combinat/regular_sequence.py +4316 -0
- sage/combinat/regular_sequence_bounded.py +543 -0
- sage/combinat/restricted_growth.py +81 -0
- sage/combinat/ribbon.py +20 -0
- sage/combinat/ribbon_shaped_tableau.py +489 -0
- sage/combinat/ribbon_tableau.py +1180 -0
- sage/combinat/rigged_configurations/all.py +46 -0
- sage/combinat/rigged_configurations/bij_abstract_class.py +548 -0
- sage/combinat/rigged_configurations/bij_infinity.py +370 -0
- sage/combinat/rigged_configurations/bij_type_A.py +163 -0
- sage/combinat/rigged_configurations/bij_type_A2_dual.py +338 -0
- sage/combinat/rigged_configurations/bij_type_A2_even.py +218 -0
- sage/combinat/rigged_configurations/bij_type_A2_odd.py +199 -0
- sage/combinat/rigged_configurations/bij_type_B.py +900 -0
- sage/combinat/rigged_configurations/bij_type_C.py +267 -0
- sage/combinat/rigged_configurations/bij_type_D.py +771 -0
- sage/combinat/rigged_configurations/bij_type_D_tri.py +392 -0
- sage/combinat/rigged_configurations/bij_type_D_twisted.py +576 -0
- sage/combinat/rigged_configurations/bij_type_E67.py +402 -0
- sage/combinat/rigged_configurations/bijection.py +143 -0
- sage/combinat/rigged_configurations/kleber_tree.py +1475 -0
- sage/combinat/rigged_configurations/kr_tableaux.py +1898 -0
- sage/combinat/rigged_configurations/rc_crystal.py +461 -0
- sage/combinat/rigged_configurations/rc_infinity.py +540 -0
- sage/combinat/rigged_configurations/rigged_configuration_element.py +2403 -0
- sage/combinat/rigged_configurations/rigged_configurations.py +1918 -0
- sage/combinat/rigged_configurations/rigged_partition.cp314t-win_amd64.pyd +0 -0
- sage/combinat/rigged_configurations/rigged_partition.pxd +15 -0
- sage/combinat/rigged_configurations/rigged_partition.pyx +680 -0
- sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +499 -0
- sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py +428 -0
- sage/combinat/rsk.py +3438 -0
- sage/combinat/schubert_polynomial.py +508 -0
- sage/combinat/set_partition.py +3318 -0
- sage/combinat/set_partition_iterator.cp314t-win_amd64.pyd +0 -0
- sage/combinat/set_partition_iterator.pyx +136 -0
- sage/combinat/set_partition_ordered.py +1590 -0
- sage/combinat/sf/abreu_nigro.py +346 -0
- sage/combinat/sf/all.py +52 -0
- sage/combinat/sf/character.py +576 -0
- sage/combinat/sf/classical.py +319 -0
- sage/combinat/sf/dual.py +996 -0
- sage/combinat/sf/elementary.py +549 -0
- sage/combinat/sf/hall_littlewood.py +1028 -0
- sage/combinat/sf/hecke.py +336 -0
- sage/combinat/sf/homogeneous.py +464 -0
- sage/combinat/sf/jack.py +1428 -0
- sage/combinat/sf/k_dual.py +1458 -0
- sage/combinat/sf/kfpoly.py +447 -0
- sage/combinat/sf/llt.py +789 -0
- sage/combinat/sf/macdonald.py +2019 -0
- sage/combinat/sf/monomial.py +525 -0
- sage/combinat/sf/multiplicative.py +113 -0
- sage/combinat/sf/new_kschur.py +1786 -0
- sage/combinat/sf/ns_macdonald.py +964 -0
- sage/combinat/sf/orthogonal.py +246 -0
- sage/combinat/sf/orthotriang.py +355 -0
- sage/combinat/sf/powersum.py +963 -0
- sage/combinat/sf/schur.py +880 -0
- sage/combinat/sf/sf.py +1653 -0
- sage/combinat/sf/sfa.py +7053 -0
- sage/combinat/sf/symplectic.py +253 -0
- sage/combinat/sf/witt.py +721 -0
- sage/combinat/shifted_primed_tableau.py +2735 -0
- sage/combinat/shuffle.py +830 -0
- sage/combinat/sidon_sets.py +146 -0
- sage/combinat/similarity_class_type.py +1721 -0
- sage/combinat/sine_gordon.py +618 -0
- sage/combinat/six_vertex_model.py +784 -0
- sage/combinat/skew_partition.py +2053 -0
- sage/combinat/skew_tableau.py +2989 -0
- sage/combinat/sloane_functions.py +8935 -0
- sage/combinat/specht_module.py +1403 -0
- sage/combinat/species/all.py +48 -0
- sage/combinat/species/characteristic_species.py +321 -0
- sage/combinat/species/composition_species.py +273 -0
- sage/combinat/species/cycle_species.py +284 -0
- sage/combinat/species/empty_species.py +155 -0
- sage/combinat/species/functorial_composition_species.py +148 -0
- sage/combinat/species/generating_series.py +673 -0
- sage/combinat/species/library.py +148 -0
- sage/combinat/species/linear_order_species.py +169 -0
- sage/combinat/species/misc.py +83 -0
- sage/combinat/species/partition_species.py +290 -0
- sage/combinat/species/permutation_species.py +268 -0
- sage/combinat/species/product_species.py +423 -0
- sage/combinat/species/recursive_species.py +476 -0
- sage/combinat/species/set_species.py +192 -0
- sage/combinat/species/species.py +820 -0
- sage/combinat/species/structure.py +539 -0
- sage/combinat/species/subset_species.py +243 -0
- sage/combinat/species/sum_species.py +225 -0
- sage/combinat/subword.py +564 -0
- sage/combinat/subword_complex.py +2122 -0
- sage/combinat/subword_complex_c.cp314t-win_amd64.pyd +0 -0
- sage/combinat/subword_complex_c.pyx +119 -0
- sage/combinat/super_tableau.py +821 -0
- sage/combinat/superpartition.py +1154 -0
- sage/combinat/symmetric_group_algebra.py +3774 -0
- sage/combinat/symmetric_group_representations.py +1830 -0
- sage/combinat/t_sequences.py +877 -0
- sage/combinat/tableau.py +9506 -0
- sage/combinat/tableau_residues.py +860 -0
- sage/combinat/tableau_tuple.py +5353 -0
- sage/combinat/tiling.py +2432 -0
- sage/combinat/triangles_FHM.py +777 -0
- sage/combinat/tutorial.py +1857 -0
- sage/combinat/vector_partition.py +337 -0
- sage/combinat/words/abstract_word.py +1722 -0
- sage/combinat/words/all.py +59 -0
- sage/combinat/words/alphabet.py +268 -0
- sage/combinat/words/finite_word.py +7201 -0
- sage/combinat/words/infinite_word.py +113 -0
- sage/combinat/words/lyndon_word.py +652 -0
- sage/combinat/words/morphic.py +351 -0
- sage/combinat/words/morphism.py +3878 -0
- sage/combinat/words/paths.py +2932 -0
- sage/combinat/words/shuffle_product.py +278 -0
- sage/combinat/words/suffix_trees.py +1873 -0
- sage/combinat/words/word.py +769 -0
- sage/combinat/words/word_char.cp314t-win_amd64.pyd +0 -0
- sage/combinat/words/word_char.pyx +847 -0
- sage/combinat/words/word_datatypes.cp314t-win_amd64.pyd +0 -0
- sage/combinat/words/word_datatypes.pxd +4 -0
- sage/combinat/words/word_datatypes.pyx +1067 -0
- sage/combinat/words/word_generators.py +2026 -0
- sage/combinat/words/word_infinite_datatypes.py +1218 -0
- sage/combinat/words/word_options.py +99 -0
- sage/combinat/words/words.py +2396 -0
- sage/data_structures/all__sagemath_combinat.py +1 -0
- sage/databases/all__sagemath_combinat.py +13 -0
- sage/databases/findstat.py +4897 -0
- sage/databases/oeis.py +2058 -0
- sage/databases/sloane.py +393 -0
- sage/dynamics/all__sagemath_combinat.py +14 -0
- sage/dynamics/cellular_automata/all.py +7 -0
- sage/dynamics/cellular_automata/catalog.py +34 -0
- sage/dynamics/cellular_automata/elementary.py +612 -0
- sage/dynamics/cellular_automata/glca.py +477 -0
- sage/dynamics/cellular_automata/solitons.py +1463 -0
- sage/dynamics/finite_dynamical_system.py +1249 -0
- sage/dynamics/finite_dynamical_system_catalog.py +382 -0
- sage/games/all.py +7 -0
- sage/games/hexad.py +704 -0
- sage/games/quantumino.py +591 -0
- sage/games/sudoku.py +889 -0
- sage/games/sudoku_backtrack.cp314t-win_amd64.pyd +0 -0
- sage/games/sudoku_backtrack.pyx +189 -0
- sage/groups/all__sagemath_combinat.py +1 -0
- sage/groups/indexed_free_group.py +489 -0
- sage/libs/all__sagemath_combinat.py +6 -0
- sage/libs/lrcalc/__init__.py +1 -0
- sage/libs/lrcalc/lrcalc.py +525 -0
- sage/libs/symmetrica/__init__.py +7 -0
- sage/libs/symmetrica/all.py +101 -0
- sage/libs/symmetrica/kostka.pxi +168 -0
- sage/libs/symmetrica/part.pxi +193 -0
- sage/libs/symmetrica/plet.pxi +42 -0
- sage/libs/symmetrica/sab.pxi +196 -0
- sage/libs/symmetrica/sb.pxi +332 -0
- sage/libs/symmetrica/sc.pxi +192 -0
- sage/libs/symmetrica/schur.pxi +956 -0
- sage/libs/symmetrica/symmetrica.cp314t-win_amd64.pyd +0 -0
- sage/libs/symmetrica/symmetrica.pxi +1172 -0
- sage/libs/symmetrica/symmetrica.pyx +39 -0
- sage/monoids/all.py +13 -0
- sage/monoids/automatic_semigroup.py +1054 -0
- sage/monoids/free_abelian_monoid.py +315 -0
- sage/monoids/free_abelian_monoid_element.cp314t-win_amd64.pyd +0 -0
- sage/monoids/free_abelian_monoid_element.pxd +16 -0
- sage/monoids/free_abelian_monoid_element.pyx +397 -0
- sage/monoids/free_monoid.py +335 -0
- sage/monoids/free_monoid_element.py +431 -0
- sage/monoids/hecke_monoid.py +65 -0
- sage/monoids/string_monoid.py +817 -0
- sage/monoids/string_monoid_element.py +547 -0
- sage/monoids/string_ops.py +143 -0
- sage/monoids/trace_monoid.py +972 -0
- sage/rings/all__sagemath_combinat.py +2 -0
- sage/sat/all.py +4 -0
- sage/sat/boolean_polynomials.py +405 -0
- sage/sat/converters/__init__.py +6 -0
- sage/sat/converters/anf2cnf.py +14 -0
- sage/sat/converters/polybori.py +611 -0
- sage/sat/solvers/__init__.py +5 -0
- sage/sat/solvers/cryptominisat.py +287 -0
- sage/sat/solvers/dimacs.py +783 -0
- sage/sat/solvers/picosat.py +228 -0
- sage/sat/solvers/sat_lp.py +156 -0
- sage/sat/solvers/satsolver.cp314t-win_amd64.pyd +0 -0
- sage/sat/solvers/satsolver.pxd +3 -0
- sage/sat/solvers/satsolver.pyx +405 -0
sage/combinat/rsk.py
ADDED
|
@@ -0,0 +1,3438 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-combinat
|
|
2
|
+
# sage.doctest: needs sage.combinat sage.modules
|
|
3
|
+
r"""
|
|
4
|
+
Robinson-Schensted-Knuth correspondence
|
|
5
|
+
|
|
6
|
+
AUTHORS:
|
|
7
|
+
|
|
8
|
+
- Travis Scrimshaw (2012-12-07): Initial version
|
|
9
|
+
- Chaman Agrawal (2019-06-24): Refactoring on the Rule class
|
|
10
|
+
- Matthew Lancellotti (2018): initial version of super RSK
|
|
11
|
+
- Jianping Pan, Wencin Poh, Anne Schilling (2020-08-31): initial version of RuleStar
|
|
12
|
+
|
|
13
|
+
Introduction
|
|
14
|
+
============
|
|
15
|
+
|
|
16
|
+
The Robinson-Schensted-Knuth (RSK) correspondence is most naturally
|
|
17
|
+
stated as a bijection between generalized permutations (also known
|
|
18
|
+
as two-line arrays, biwords, ...) and pairs of semi-standard Young
|
|
19
|
+
tableaux `(P, Q)` of identical shape.
|
|
20
|
+
|
|
21
|
+
The basic operation in the RSK correspondence is a row insertion
|
|
22
|
+
`P \leftarrow k` (where `P` is a given semi-standard Young tableau,
|
|
23
|
+
and `k` is an integer). Different insertion algorithms have been
|
|
24
|
+
implemented for the RSK correspondence and can be specified as
|
|
25
|
+
an argument in the function call.
|
|
26
|
+
|
|
27
|
+
EXAMPLES:
|
|
28
|
+
|
|
29
|
+
We can perform RSK and its inverse map on a variety of objects::
|
|
30
|
+
|
|
31
|
+
sage: p = Tableau([[1,2,2],[2]]); q = Tableau([[1,3,3],[2]])
|
|
32
|
+
sage: gp = RSK_inverse(p, q); gp
|
|
33
|
+
[[1, 2, 3, 3], [2, 1, 2, 2]]
|
|
34
|
+
sage: RSK(*gp) # RSK of a biword
|
|
35
|
+
[[[1, 2, 2], [2]], [[1, 3, 3], [2]]]
|
|
36
|
+
sage: RSK([2,3,2,1,2,3]) # Robinson-Schensted of a word
|
|
37
|
+
[[[1, 2, 2, 3], [2], [3]], [[1, 2, 5, 6], [3], [4]]]
|
|
38
|
+
sage: RSK([2,3,2,1,2,3], insertion=RSK.rules.EG) # Edelman-Greene
|
|
39
|
+
[[[1, 2, 3], [2, 3], [3]], [[1, 2, 6], [3, 5], [4]]]
|
|
40
|
+
sage: m = RSK_inverse(p, q, 'matrix'); m # output as matrix
|
|
41
|
+
[0 1]
|
|
42
|
+
[1 0]
|
|
43
|
+
[0 2]
|
|
44
|
+
sage: RSK(m) # RSK of a matrix
|
|
45
|
+
[[[1, 2, 2], [2]], [[1, 3, 3], [2]]]
|
|
46
|
+
|
|
47
|
+
Insertions currently available
|
|
48
|
+
------------------------------
|
|
49
|
+
|
|
50
|
+
The following insertion algorithms for RSK correspondence are currently
|
|
51
|
+
available:
|
|
52
|
+
|
|
53
|
+
- RSK insertion (:class:`~sage.combinat.rsk.RuleRSK`).
|
|
54
|
+
- Edelman-Greene insertion (:class:`~sage.combinat.rsk.RuleEG`), an algorithm
|
|
55
|
+
defined in [EG1987]_ Definition 6.20 (where it is referred to as
|
|
56
|
+
Coxeter-Knuth insertion).
|
|
57
|
+
- Hecke RSK algorithm (:class:`~sage.combinat.rsk.RuleHecke`) , defined
|
|
58
|
+
using the Hecke insertion studied in [BKSTY06]_ (but using rows instead
|
|
59
|
+
of columns).
|
|
60
|
+
- Dual RSK insertion (:class:`~sage.combinat.rsk.RuleDualRSK`).
|
|
61
|
+
- CoRSK insertion (:class:`~sage.combinat.rsk.RuleCoRSK`), defined in
|
|
62
|
+
[GR2018v5sol]_.
|
|
63
|
+
- Super RSK insertion (:class:`~sage.combinat.rsk.RuleSuperRSK`), a
|
|
64
|
+
combination of row and column insertions defined in [Muth2019]_.
|
|
65
|
+
- Star insertion (:class:`~sage.combinat.rsk.RuleStar`), defined in [MPPS2020]_.
|
|
66
|
+
|
|
67
|
+
Implementing your own insertion rule
|
|
68
|
+
------------------------------------
|
|
69
|
+
|
|
70
|
+
The functions :func:`RSK()` and :func:`RSK_inverse()` are written so that it
|
|
71
|
+
is easy to implement insertion algorithms you come across in your research.
|
|
72
|
+
|
|
73
|
+
To implement your own insertion algorithm, you first need to import the
|
|
74
|
+
base class for a rule::
|
|
75
|
+
|
|
76
|
+
sage: from sage.combinat.rsk import Rule
|
|
77
|
+
|
|
78
|
+
Using the ``Rule`` class as parent class for your insertion rule,
|
|
79
|
+
first implement the insertion and the reverse insertion algorithm
|
|
80
|
+
for :func:`RSK()` and :func:`RSK_inverse()` respectively (as methods
|
|
81
|
+
``forward_rule`` and ``backward_rule``). If your insertion algorithm
|
|
82
|
+
uses the same forward and backward rules as ``RuleRSK``, differing
|
|
83
|
+
only in how an entry is inserted into a row, then this is not
|
|
84
|
+
necessary, and it suffices to merely implement the
|
|
85
|
+
``insertion`` and ``reverse_insertion`` methods.
|
|
86
|
+
|
|
87
|
+
For more information, see :class:`~sage.combinat.rsk.Rule`.
|
|
88
|
+
|
|
89
|
+
TESTS:
|
|
90
|
+
|
|
91
|
+
Check that it is a correspondence between all types of input and
|
|
92
|
+
the input is preserved::
|
|
93
|
+
|
|
94
|
+
sage: t1 = Tableau([[1, 2, 5], [3], [4]])
|
|
95
|
+
sage: t2 = Tableau([[1, 2, 3], [4], [5]])
|
|
96
|
+
sage: gp = RSK_inverse(t1, t2); gp
|
|
97
|
+
[[1, 2, 3, 4, 5], [1, 4, 5, 3, 2]]
|
|
98
|
+
sage: w = RSK_inverse(t1, t2, 'word'); w
|
|
99
|
+
word: 14532
|
|
100
|
+
sage: m = RSK_inverse(t1, t2, 'matrix'); m
|
|
101
|
+
[1 0 0 0 0]
|
|
102
|
+
[0 0 0 1 0]
|
|
103
|
+
[0 0 0 0 1]
|
|
104
|
+
[0 0 1 0 0]
|
|
105
|
+
[0 1 0 0 0]
|
|
106
|
+
sage: p = RSK_inverse(t1, t2, 'permutation'); p
|
|
107
|
+
[1, 4, 5, 3, 2]
|
|
108
|
+
sage: t1
|
|
109
|
+
[[1, 2, 5], [3], [4]]
|
|
110
|
+
sage: t2
|
|
111
|
+
[[1, 2, 3], [4], [5]]
|
|
112
|
+
sage: RSK(*gp) == [t1, t2]
|
|
113
|
+
True
|
|
114
|
+
sage: RSK(w) == [t1, t2]
|
|
115
|
+
True
|
|
116
|
+
sage: RSK(m) == [t1, t2]
|
|
117
|
+
True
|
|
118
|
+
sage: RSK(p) == [t1, t2]
|
|
119
|
+
True
|
|
120
|
+
sage: gp
|
|
121
|
+
[[1, 2, 3, 4, 5], [1, 4, 5, 3, 2]]
|
|
122
|
+
sage: w
|
|
123
|
+
word: 14532
|
|
124
|
+
sage: m
|
|
125
|
+
[1 0 0 0 0]
|
|
126
|
+
[0 0 0 1 0]
|
|
127
|
+
[0 0 0 0 1]
|
|
128
|
+
[0 0 1 0 0]
|
|
129
|
+
[0 1 0 0 0]
|
|
130
|
+
sage: p
|
|
131
|
+
[1, 4, 5, 3, 2]
|
|
132
|
+
|
|
133
|
+
REFERENCES:
|
|
134
|
+
|
|
135
|
+
.. [Knu1970] Donald E. Knuth.
|
|
136
|
+
*Permutations, matrices, and generalized Young tableaux*.
|
|
137
|
+
Pacific J. Math. Volume 34, Number 3 (1970), pp. 709-727.
|
|
138
|
+
http://projecteuclid.org/euclid.pjm/1102971948
|
|
139
|
+
|
|
140
|
+
.. [EG1987] Paul Edelman, Curtis Greene.
|
|
141
|
+
*Balanced Tableaux*.
|
|
142
|
+
Advances in Mathematics 63 (1987), pp. 42-99.
|
|
143
|
+
:doi:`10.1016/0001-8708(87)90063-6`
|
|
144
|
+
|
|
145
|
+
.. [BKSTY06] \A. Buch, A. Kresch, M. Shimozono, H. Tamvakis, and A. Yong.
|
|
146
|
+
*Stable Grothendieck polynomials and* `K`-*theoretic factor sequences*.
|
|
147
|
+
Math. Ann. **340** Issue 2, (2008), pp. 359--382.
|
|
148
|
+
:arxiv:`math/0601514v1`.
|
|
149
|
+
|
|
150
|
+
.. [GR2018v5sol] Darij Grinberg, Victor Reiner.
|
|
151
|
+
*Hopf Algebras In Combinatorics*,
|
|
152
|
+
:arxiv:`1409.8356v5`, available with solutions at
|
|
153
|
+
https://arxiv.org/src/1409.8356v5/anc/HopfComb-v73-with-solutions.pdf
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
# *****************************************************************************
|
|
157
|
+
# Copyright (C) 2012,2019 Travis Scrimshaw <tcscrims at gmail.com>
|
|
158
|
+
# 2019 Chaman Agrawal <chaman.ag at gmail.com>
|
|
159
|
+
# 2019 Matthew Lancellotti <mareoraft at gmail.com>
|
|
160
|
+
# 2020 Jianping Pan <jppan at math dot ucdavis dot edu>
|
|
161
|
+
# Wencin Poh <wpoh at ucdavis dot edu>
|
|
162
|
+
# Anne Schilling <anne at math dot ucdavis dot edu>
|
|
163
|
+
#
|
|
164
|
+
# This program is free software: you can redistribute it and/or modify
|
|
165
|
+
# it under the terms of the GNU General Public License as published by
|
|
166
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
167
|
+
# (at your option) any later version.
|
|
168
|
+
# https://www.gnu.org/licenses/
|
|
169
|
+
# ****************************************************************************
|
|
170
|
+
|
|
171
|
+
from bisect import bisect_left, bisect_right
|
|
172
|
+
|
|
173
|
+
from sage.misc.lazy_import import lazy_import
|
|
174
|
+
from sage.rings.integer_ring import ZZ
|
|
175
|
+
from sage.structure.element import Matrix
|
|
176
|
+
from sage.structure.unique_representation import UniqueRepresentation
|
|
177
|
+
|
|
178
|
+
lazy_import('sage.matrix.constructor', 'matrix')
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class Rule(UniqueRepresentation):
|
|
182
|
+
r"""
|
|
183
|
+
Generic base class for an insertion rule for an RSK-type correspondence.
|
|
184
|
+
|
|
185
|
+
An instance of this class should implement a method
|
|
186
|
+
:meth:`insertion` (which can be applied to a letter ``j``
|
|
187
|
+
and a list ``r``, and modifies ``r`` in place by "bumping"
|
|
188
|
+
``j`` into it appropriately; it then returns the bumped-out
|
|
189
|
+
entry or ``None`` if no such entry exists) and a method
|
|
190
|
+
:meth:`reverse_insertion` (which does the same but for reverse
|
|
191
|
+
bumping).
|
|
192
|
+
It may also implement :meth:`_backward_format_output` and
|
|
193
|
+
:meth:`_forward_format_output` if the RSK correspondence should
|
|
194
|
+
return something other than (semi)standard tableaux (in the
|
|
195
|
+
forward direction) and matrices or biwords (in the backward
|
|
196
|
+
direction).
|
|
197
|
+
The :meth:`to_pairs` method should also be overridden if
|
|
198
|
+
the input for the (forward) RSK correspondence is not the
|
|
199
|
+
usual kind of biwords (i.e., pairs of two `n`-tuples
|
|
200
|
+
`[a_1, a_2, \ldots, a_n]` and `[b_1, b_2, \ldots, b_n]`
|
|
201
|
+
satisfying `(a_1, b_1) \leq (a_2, b_2) \leq \cdots
|
|
202
|
+
\leq (a_n, b_n)` in lexicographic order).
|
|
203
|
+
Finally, it :meth:`forward_rule` and :meth:`backward_rule`
|
|
204
|
+
have to be overridden if the overall structure of the
|
|
205
|
+
RSK correspondence differs from that of classical RSK (see,
|
|
206
|
+
e.g., the case of Hecke insertion, in which a letter bumped
|
|
207
|
+
into a row may change a different row).
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
def to_pairs(self, obj1=None, obj2=None, check=True):
|
|
211
|
+
r"""
|
|
212
|
+
Given a valid input for the RSK algorithm, such as
|
|
213
|
+
two `n`-tuples ``obj1`` `= [a_1, a_2, \ldots, a_n]`
|
|
214
|
+
and ``obj2`` `= [b_1, b_2, \ldots, b_n]` forming a biword
|
|
215
|
+
(i.e., satisfying
|
|
216
|
+
`a_1 \leq a_2 \leq \cdots \leq a_n`, and if
|
|
217
|
+
`a_i = a_{i+1}`, then `b_i \leq b_{i+1}`),
|
|
218
|
+
or a matrix ("generalized permutation"), or a single word,
|
|
219
|
+
return the array
|
|
220
|
+
`[(a_1, b_1), (a_2, b_2), \ldots, (a_n, b_n)]`.
|
|
221
|
+
|
|
222
|
+
INPUT:
|
|
223
|
+
|
|
224
|
+
- ``obj1``, ``obj2`` -- anything representing a biword
|
|
225
|
+
(see the doc of :meth:`forward_rule` for the
|
|
226
|
+
encodings accepted)
|
|
227
|
+
|
|
228
|
+
- ``check`` -- boolean (default: ``True``); whether to check that
|
|
229
|
+
``obj1`` and ``obj2`` actually define a valid biword
|
|
230
|
+
|
|
231
|
+
EXAMPLES::
|
|
232
|
+
|
|
233
|
+
sage: from sage.combinat.rsk import Rule
|
|
234
|
+
sage: list(Rule().to_pairs([1, 2, 2, 2], [2, 1, 1, 2]))
|
|
235
|
+
[(1, 2), (2, 1), (2, 1), (2, 2)]
|
|
236
|
+
sage: m = Matrix(ZZ, 3, 2, [0,1,1,0,0,2]) ; m
|
|
237
|
+
[0 1]
|
|
238
|
+
[1 0]
|
|
239
|
+
[0 2]
|
|
240
|
+
sage: list(Rule().to_pairs(m))
|
|
241
|
+
[(1, 2), (2, 1), (3, 2), (3, 2)]
|
|
242
|
+
"""
|
|
243
|
+
if obj2 is None:
|
|
244
|
+
try:
|
|
245
|
+
itr = obj1._rsk_iter()
|
|
246
|
+
except AttributeError:
|
|
247
|
+
# If this is (something which looks like) a matrix
|
|
248
|
+
# then build the generalized permutation
|
|
249
|
+
try:
|
|
250
|
+
t = []
|
|
251
|
+
b = []
|
|
252
|
+
for i, row in enumerate(obj1):
|
|
253
|
+
for j, mult in enumerate(row):
|
|
254
|
+
if mult > 0:
|
|
255
|
+
t.extend([i+1]*mult)
|
|
256
|
+
b.extend([j+1]*mult)
|
|
257
|
+
itr = zip(t, b)
|
|
258
|
+
except TypeError:
|
|
259
|
+
itr = zip(range(1, len(obj1)+1), obj1)
|
|
260
|
+
else:
|
|
261
|
+
if check:
|
|
262
|
+
if len(obj1) != len(obj2):
|
|
263
|
+
raise ValueError("the two arrays must be the same length")
|
|
264
|
+
# Check it is a generalized permutation (i.e., biword):
|
|
265
|
+
# that is, the pairs of corresponding entries of obj1
|
|
266
|
+
# and obj2 are weakly increasing in lexicographic order.
|
|
267
|
+
lt = 0
|
|
268
|
+
lb = 0
|
|
269
|
+
for t, b in zip(obj1, obj2):
|
|
270
|
+
if t < lt or (t == lt and b < lb):
|
|
271
|
+
raise ValueError("invalid generalized permutation")
|
|
272
|
+
lt = t
|
|
273
|
+
lb = b
|
|
274
|
+
itr = zip(obj1, obj2)
|
|
275
|
+
return itr
|
|
276
|
+
|
|
277
|
+
def forward_rule(self, obj1, obj2, check_standard=False, check=True):
|
|
278
|
+
r"""
|
|
279
|
+
Return a pair of tableaux obtained by applying forward
|
|
280
|
+
insertion to the generalized permutation ``[obj1, obj2]``.
|
|
281
|
+
|
|
282
|
+
INPUT:
|
|
283
|
+
|
|
284
|
+
- ``obj1``, ``obj2`` -- can be one of the following ways to
|
|
285
|
+
represent a generalized permutation (or, equivalently,
|
|
286
|
+
biword):
|
|
287
|
+
|
|
288
|
+
- two lists ``obj1`` and ``obj2`` of equal length,
|
|
289
|
+
to be interpreted as the top row and the bottom row of
|
|
290
|
+
the biword
|
|
291
|
+
|
|
292
|
+
- a matrix ``obj1`` of nonnegative integers, to be
|
|
293
|
+
interpreted as the generalized permutation in matrix
|
|
294
|
+
form (in this case, ``obj2`` is ``None``)
|
|
295
|
+
|
|
296
|
+
- a word ``obj1`` in an ordered alphabet, to be
|
|
297
|
+
interpreted as the bottom row of the biword (in this
|
|
298
|
+
case, ``obj2`` is ``None``; the top row of the biword
|
|
299
|
+
is understood to be `(1, 2, \ldots, n)` by default)
|
|
300
|
+
|
|
301
|
+
- any object ``obj1`` which has a method ``_rsk_iter()``,
|
|
302
|
+
as long as this method returns an iterator yielding
|
|
303
|
+
pairs of numbers, which then are interpreted as top
|
|
304
|
+
entries and bottom entries in the biword (in this case,
|
|
305
|
+
``obj2`` is ``None``)
|
|
306
|
+
|
|
307
|
+
- ``check_standard`` -- boolean (default: ``False``); check if either
|
|
308
|
+
of the resulting tableaux is a standard tableau, and if so, typecast
|
|
309
|
+
it as such
|
|
310
|
+
|
|
311
|
+
- ``check`` -- boolean (default: ``True``); whether to check
|
|
312
|
+
that ``obj1`` and ``obj2`` actually define a valid
|
|
313
|
+
biword
|
|
314
|
+
|
|
315
|
+
EXAMPLES::
|
|
316
|
+
|
|
317
|
+
sage: from sage.combinat.rsk import RuleRSK
|
|
318
|
+
sage: RuleRSK().forward_rule([3,3,2,4,1], None)
|
|
319
|
+
[[[1, 3, 4], [2], [3]], [[1, 2, 4], [3], [5]]]
|
|
320
|
+
sage: RuleRSK().forward_rule([1, 1, 1, 3, 7], None)
|
|
321
|
+
[[[1, 1, 1, 3, 7]], [[1, 2, 3, 4, 5]]]
|
|
322
|
+
sage: RuleRSK().forward_rule([7, 6, 3, 3, 1], None)
|
|
323
|
+
[[[1, 3], [3], [6], [7]], [[1, 4], [2], [3], [5]]]
|
|
324
|
+
"""
|
|
325
|
+
itr = self.to_pairs(obj1, obj2, check=check)
|
|
326
|
+
p = [] # the "insertion" tableau
|
|
327
|
+
q = [] # the "recording" tableau
|
|
328
|
+
for i, j in itr:
|
|
329
|
+
for r, qr in zip(p, q):
|
|
330
|
+
j1 = self.insertion(j, r)
|
|
331
|
+
if j1 is None:
|
|
332
|
+
r.append(j)
|
|
333
|
+
qr.append(i) # Values are always inserted to the right
|
|
334
|
+
break
|
|
335
|
+
else:
|
|
336
|
+
j = j1
|
|
337
|
+
else:
|
|
338
|
+
# We made through all of the rows of p without breaking
|
|
339
|
+
# so we need to add a new row to p and q.
|
|
340
|
+
p.append([j])
|
|
341
|
+
q.append([i])
|
|
342
|
+
return self._forward_format_output(p, q, check_standard=check_standard)
|
|
343
|
+
|
|
344
|
+
def backward_rule(self, p, q, output):
|
|
345
|
+
r"""
|
|
346
|
+
Return the generalized permutation obtained by applying reverse
|
|
347
|
+
insertion to a pair of tableaux ``(p, q)``.
|
|
348
|
+
|
|
349
|
+
INPUT:
|
|
350
|
+
|
|
351
|
+
- ``p``, ``q`` -- two tableaux of the same shape
|
|
352
|
+
|
|
353
|
+
- ``output`` -- (default: ``'array'``) if ``q`` is semi-standard:
|
|
354
|
+
|
|
355
|
+
- ``'array'`` -- as a two-line array (i.e. generalized permutation
|
|
356
|
+
or biword)
|
|
357
|
+
- ``'matrix'`` -- as an integer matrix
|
|
358
|
+
|
|
359
|
+
and if ``q`` is standard, we can also have the output:
|
|
360
|
+
|
|
361
|
+
- ``'word'`` -- as a word
|
|
362
|
+
|
|
363
|
+
and additionally if ``p`` is standard, we can also have the output:
|
|
364
|
+
|
|
365
|
+
- ``'permutation'`` -- as a permutation
|
|
366
|
+
|
|
367
|
+
EXAMPLES::
|
|
368
|
+
|
|
369
|
+
sage: from sage.combinat.rsk import RuleRSK
|
|
370
|
+
sage: t1 = Tableau([[1, 3, 4], [2], [3]])
|
|
371
|
+
sage: t2 = Tableau([[1, 2, 4], [3], [5]])
|
|
372
|
+
sage: RuleRSK().backward_rule(t1, t2, 'array')
|
|
373
|
+
[[1, 2, 3, 4, 5], [3, 3, 2, 4, 1]]
|
|
374
|
+
sage: t1 = Tableau([[1, 1, 1, 3, 7]])
|
|
375
|
+
sage: t2 = Tableau([[1, 2, 3, 4, 5]])
|
|
376
|
+
sage: RuleRSK().backward_rule(t1, t2, 'array')
|
|
377
|
+
[[1, 2, 3, 4, 5], [1, 1, 1, 3, 7]]
|
|
378
|
+
sage: t1 = Tableau([[1, 3], [3], [6], [7]])
|
|
379
|
+
sage: t2 = Tableau([[1, 4], [2], [3], [5]])
|
|
380
|
+
sage: RuleRSK().backward_rule(t1, t2, 'array')
|
|
381
|
+
[[1, 2, 3, 4, 5], [7, 6, 3, 3, 1]]
|
|
382
|
+
"""
|
|
383
|
+
from sage.combinat.tableau import SemistandardTableaux
|
|
384
|
+
# Make a copy of p since this is destructive to it
|
|
385
|
+
p_copy = [list(row) for row in p]
|
|
386
|
+
|
|
387
|
+
if q.is_standard():
|
|
388
|
+
rev_word = [] # This will be our word in reverse
|
|
389
|
+
d = {qij: i for i, Li in enumerate(q) for qij in Li}
|
|
390
|
+
# d is now a dictionary which assigns to each integer k the
|
|
391
|
+
# number of the row of q containing k.
|
|
392
|
+
|
|
393
|
+
for key in sorted(d, reverse=True):
|
|
394
|
+
# Delete last entry from i-th row of p_copy
|
|
395
|
+
i = d[key]
|
|
396
|
+
x = p_copy[i].pop() # Always the right-most entry
|
|
397
|
+
for row in reversed(p_copy[:i]):
|
|
398
|
+
x = self.reverse_insertion(x, row)
|
|
399
|
+
rev_word.append(x)
|
|
400
|
+
return self._backward_format_output(rev_word, None, output, p.is_standard(), True)
|
|
401
|
+
|
|
402
|
+
if q not in SemistandardTableaux():
|
|
403
|
+
raise ValueError("q(=%s) must be a semistandard tableau" % q)
|
|
404
|
+
|
|
405
|
+
# Thus, q is semistandard but not standard.
|
|
406
|
+
|
|
407
|
+
upper_row = []
|
|
408
|
+
lower_row = []
|
|
409
|
+
# upper_row and lower_row will be the upper and lower rows of the
|
|
410
|
+
# generalized permutation we get as a result, but both reversed.
|
|
411
|
+
d = {}
|
|
412
|
+
for row, Li in enumerate(q):
|
|
413
|
+
for col, val in enumerate(Li):
|
|
414
|
+
if val in d:
|
|
415
|
+
d[val][col] = row
|
|
416
|
+
else:
|
|
417
|
+
d[val] = {col: row}
|
|
418
|
+
# d is now a double family such that for every integers k and j,
|
|
419
|
+
# the value d[k][j] is the row i such that the (i, j)-th cell of
|
|
420
|
+
# q is filled with k.
|
|
421
|
+
for value, row_dict in sorted(d.items(), reverse=True, key=lambda x: x[0]):
|
|
422
|
+
for key in sorted(row_dict, reverse=True):
|
|
423
|
+
i = row_dict[key]
|
|
424
|
+
x = p_copy[i].pop() # Always the right-most entry
|
|
425
|
+
for row in reversed(p_copy[:i]):
|
|
426
|
+
x = self.reverse_insertion(x, row)
|
|
427
|
+
lower_row.append(x)
|
|
428
|
+
upper_row.append(value)
|
|
429
|
+
return self._backward_format_output(lower_row, upper_row, output, p.is_standard(), False)
|
|
430
|
+
|
|
431
|
+
def _forward_format_output(self, p, q, check_standard):
|
|
432
|
+
r"""
|
|
433
|
+
Return final output of the ``RSK`` correspondence from the
|
|
434
|
+
output of the corresponding ``forward_rule``.
|
|
435
|
+
|
|
436
|
+
EXAMPLES::
|
|
437
|
+
|
|
438
|
+
sage: from sage.combinat.rsk import RuleRSK
|
|
439
|
+
sage: isinstance(RuleRSK()._forward_format_output([[1, 2, 3, 4, 5]],
|
|
440
|
+
....: [[1, 2, 3, 4, 5]], True)[0], StandardTableau)
|
|
441
|
+
True
|
|
442
|
+
sage: isinstance(RuleRSK()._forward_format_output([[1, 2, 3, 4, 5]],
|
|
443
|
+
....: [[1, 2, 3, 4, 5]], False)[0], SemistandardTableau)
|
|
444
|
+
True
|
|
445
|
+
sage: isinstance(RuleRSK()._forward_format_output([[1, 1, 1, 3, 7]],
|
|
446
|
+
....: [[1, 2, 3, 4, 5]], True)[0], SemistandardTableau)
|
|
447
|
+
True
|
|
448
|
+
"""
|
|
449
|
+
from sage.combinat.tableau import SemistandardTableau, StandardTableau
|
|
450
|
+
|
|
451
|
+
if check_standard:
|
|
452
|
+
try:
|
|
453
|
+
P = StandardTableau(p)
|
|
454
|
+
except ValueError:
|
|
455
|
+
P = SemistandardTableau(p)
|
|
456
|
+
try:
|
|
457
|
+
Q = StandardTableau(q)
|
|
458
|
+
except ValueError:
|
|
459
|
+
Q = SemistandardTableau(q)
|
|
460
|
+
return [P, Q]
|
|
461
|
+
return [SemistandardTableau(p), SemistandardTableau(q)]
|
|
462
|
+
|
|
463
|
+
def _backward_format_output(self, lower_row, upper_row, output,
|
|
464
|
+
p_is_standard, q_is_standard):
|
|
465
|
+
r"""
|
|
466
|
+
Return the final output of the ``RSK_inverse`` correspondence
|
|
467
|
+
from the output of the corresponding ``backward_rule``.
|
|
468
|
+
|
|
469
|
+
.. NOTE::
|
|
470
|
+
|
|
471
|
+
The default implementation of ``backward_rule`` lists
|
|
472
|
+
bumped-out entries in the order in which the reverse
|
|
473
|
+
bumping happens, which is *opposite* to the order of the
|
|
474
|
+
final output.
|
|
475
|
+
|
|
476
|
+
EXAMPLES::
|
|
477
|
+
|
|
478
|
+
sage: from sage.combinat.rsk import RuleRSK
|
|
479
|
+
sage: RuleRSK()._backward_format_output([1, 2, 3, 4], None, 'array', False, True)
|
|
480
|
+
[[1, 2, 3, 4], [4, 3, 2, 1]]
|
|
481
|
+
sage: RuleRSK()._backward_format_output([1, 2, 3, 4], None, 'matrix', False, True)
|
|
482
|
+
[0 0 0 1]
|
|
483
|
+
[0 0 1 0]
|
|
484
|
+
[0 1 0 0]
|
|
485
|
+
[1 0 0 0]
|
|
486
|
+
sage: RuleRSK()._backward_format_output([1, 2, 3, 4], None, 'word', False, True)
|
|
487
|
+
word: 4321
|
|
488
|
+
sage: RuleRSK()._backward_format_output([3, 2, 1, 1], [2, 1, 1, 1], 'array', False, False)
|
|
489
|
+
[[1, 1, 1, 2], [1, 1, 2, 3]]
|
|
490
|
+
sage: RuleRSK()._backward_format_output([3, 2, 1, 1], [2, 1, 1, 1], 'matrix', False, False)
|
|
491
|
+
[2 1 0]
|
|
492
|
+
[0 0 1]
|
|
493
|
+
sage: RuleRSK()._backward_format_output([1, 2, 3, 4], None, 'word', False, False)
|
|
494
|
+
Traceback (most recent call last):
|
|
495
|
+
...
|
|
496
|
+
TypeError: q must be standard to have a word as valid output
|
|
497
|
+
sage: RuleRSK()._backward_format_output([1, 2, 3, 4], None, 'random_type', False, False)
|
|
498
|
+
Traceback (most recent call last):
|
|
499
|
+
...
|
|
500
|
+
ValueError: invalid output option
|
|
501
|
+
"""
|
|
502
|
+
if q_is_standard:
|
|
503
|
+
if output == 'word':
|
|
504
|
+
from sage.combinat.words.word import Word
|
|
505
|
+
return Word(reversed(lower_row))
|
|
506
|
+
if output == 'matrix':
|
|
507
|
+
return to_matrix(list(range(1, len(lower_row)+1)), list(reversed(lower_row)))
|
|
508
|
+
if output == 'array':
|
|
509
|
+
return [list(range(1, len(lower_row)+1)), list(reversed(lower_row))]
|
|
510
|
+
raise ValueError("invalid output option")
|
|
511
|
+
|
|
512
|
+
else:
|
|
513
|
+
if output == 'matrix':
|
|
514
|
+
return to_matrix(list(reversed(upper_row)), list(reversed(lower_row)))
|
|
515
|
+
if output == 'array':
|
|
516
|
+
return [list(reversed(upper_row)), list(reversed(lower_row))]
|
|
517
|
+
if output in ['permutation', 'word']:
|
|
518
|
+
raise TypeError(
|
|
519
|
+
"q must be standard to have a %s as valid output" % output)
|
|
520
|
+
raise ValueError("invalid output option")
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
class RuleRSK(Rule):
|
|
524
|
+
r"""
|
|
525
|
+
Rule for the classical Robinson-Schensted-Knuth insertion.
|
|
526
|
+
|
|
527
|
+
See :func:`RSK` for the definition of this operation.
|
|
528
|
+
|
|
529
|
+
EXAMPLES::
|
|
530
|
+
|
|
531
|
+
sage: RSK([1, 2, 2, 2], [2, 1, 1, 2], insertion=RSK.rules.RSK)
|
|
532
|
+
[[[1, 1, 2], [2]], [[1, 2, 2], [2]]]
|
|
533
|
+
sage: p = Tableau([[1,2,2],[2]]); q = Tableau([[1,3,3],[2]])
|
|
534
|
+
sage: RSK_inverse(p, q, insertion=RSK.rules.RSK)
|
|
535
|
+
[[1, 2, 3, 3], [2, 1, 2, 2]]
|
|
536
|
+
"""
|
|
537
|
+
|
|
538
|
+
def insertion(self, j, r):
|
|
539
|
+
r"""
|
|
540
|
+
Insert the letter ``j`` from the second row of the biword
|
|
541
|
+
into the row `r` using classical Schensted insertion,
|
|
542
|
+
if there is bumping to be done.
|
|
543
|
+
|
|
544
|
+
The row `r` is modified in place if bumping occurs. The bumped-out
|
|
545
|
+
entry, if it exists, is returned.
|
|
546
|
+
|
|
547
|
+
EXAMPLES::
|
|
548
|
+
|
|
549
|
+
sage: from sage.combinat.rsk import RuleRSK
|
|
550
|
+
sage: qr, r = [1,2,3,4,5], [3,3,2,4,8]
|
|
551
|
+
sage: j = RuleRSK().insertion(9, r)
|
|
552
|
+
sage: j is None
|
|
553
|
+
True
|
|
554
|
+
sage: qr, r = [1,2,3,4,5], [3,3,2,4,8]
|
|
555
|
+
sage: j = RuleRSK().insertion(3, r)
|
|
556
|
+
sage: j
|
|
557
|
+
4
|
|
558
|
+
"""
|
|
559
|
+
if r[-1] <= j:
|
|
560
|
+
return None # j needs to be added at the end of the row.
|
|
561
|
+
# Figure out where to insert j into the row r. The
|
|
562
|
+
# bisect command returns the position of the least
|
|
563
|
+
# element of r greater than j. We will call it y.
|
|
564
|
+
y_pos = bisect_right(r, j)
|
|
565
|
+
# Switch j and y
|
|
566
|
+
j, r[y_pos] = r[y_pos], j
|
|
567
|
+
return j
|
|
568
|
+
|
|
569
|
+
def reverse_insertion(self, x, row):
|
|
570
|
+
r"""
|
|
571
|
+
Reverse bump the row ``row`` of the current insertion tableau
|
|
572
|
+
with the number ``x``.
|
|
573
|
+
|
|
574
|
+
The row ``row`` is modified in place. The bumped-out entry
|
|
575
|
+
is returned.
|
|
576
|
+
|
|
577
|
+
EXAMPLES::
|
|
578
|
+
|
|
579
|
+
sage: from sage.combinat.rsk import RuleRSK
|
|
580
|
+
sage: r = [2,3,3,4,8]
|
|
581
|
+
sage: x = RuleRSK().reverse_insertion(4, r); r
|
|
582
|
+
[2, 3, 4, 4, 8]
|
|
583
|
+
sage: x
|
|
584
|
+
3
|
|
585
|
+
"""
|
|
586
|
+
y_pos = bisect_left(row, x) - 1
|
|
587
|
+
# switch x and y
|
|
588
|
+
x, row[y_pos] = row[y_pos], x
|
|
589
|
+
return x
|
|
590
|
+
|
|
591
|
+
def _backward_format_output(self, lower_row, upper_row, output,
|
|
592
|
+
p_is_standard, q_is_standard):
|
|
593
|
+
r"""
|
|
594
|
+
Return the final output of the ``RSK_inverse`` correspondence
|
|
595
|
+
from the output of the corresponding ``backward_rule``.
|
|
596
|
+
|
|
597
|
+
.. NOTE::
|
|
598
|
+
|
|
599
|
+
The default implementation of ``backward_rule`` lists
|
|
600
|
+
bumped-out entries in the order in which the reverse
|
|
601
|
+
bumping happens, which is *opposite* to the order of the
|
|
602
|
+
final output.
|
|
603
|
+
|
|
604
|
+
EXAMPLES::
|
|
605
|
+
|
|
606
|
+
sage: from sage.combinat.rsk import RuleRSK
|
|
607
|
+
sage: RuleRSK()._backward_format_output([1, 2, 3, 4], None,
|
|
608
|
+
....: 'permutation', True, True)
|
|
609
|
+
[4, 3, 2, 1]
|
|
610
|
+
sage: RuleRSK()._backward_format_output([1, 2, 3, 4], None,
|
|
611
|
+
....: 'permutation', False, True)
|
|
612
|
+
Traceback (most recent call last):
|
|
613
|
+
...
|
|
614
|
+
TypeError: p must be standard to have a valid permutation as output
|
|
615
|
+
"""
|
|
616
|
+
if q_is_standard and output == 'permutation':
|
|
617
|
+
if not p_is_standard:
|
|
618
|
+
raise TypeError("p must be standard to have a valid permutation as output")
|
|
619
|
+
from sage.combinat.permutation import Permutation
|
|
620
|
+
return Permutation(reversed(lower_row))
|
|
621
|
+
return super()._backward_format_output(lower_row, upper_row, output,
|
|
622
|
+
p_is_standard, q_is_standard)
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
class RuleEG(Rule):
|
|
626
|
+
r"""
|
|
627
|
+
Rule for Edelman-Greene insertion.
|
|
628
|
+
|
|
629
|
+
For a reduced word of a permutation (i.e., an element of a type `A`
|
|
630
|
+
Coxeter group), one can use Edelman-Greene insertion, an algorithm
|
|
631
|
+
defined in [EG1987]_ Definition 6.20 (where it is referred to as
|
|
632
|
+
Coxeter-Knuth insertion). The Edelman-Greene insertion is similar to the
|
|
633
|
+
standard row insertion except that (using the notations in
|
|
634
|
+
the documentation of :func:`RSK`) if `k_i` and `k_i + 1` both
|
|
635
|
+
exist in row `i`, we *only* set `k_{i+1} = k_i + 1` and continue.
|
|
636
|
+
|
|
637
|
+
EXAMPLES:
|
|
638
|
+
|
|
639
|
+
Let us reproduce figure 6.4 in [EG1987]_::
|
|
640
|
+
|
|
641
|
+
sage: RSK([2,3,2,1,2,3], insertion=RSK.rules.EG)
|
|
642
|
+
[[[1, 2, 3], [2, 3], [3]], [[1, 2, 6], [3, 5], [4]]]
|
|
643
|
+
|
|
644
|
+
Some more examples::
|
|
645
|
+
|
|
646
|
+
sage: a = [2, 1, 2, 3, 2]
|
|
647
|
+
sage: pq = RSK(a, insertion=RSK.rules.EG); pq
|
|
648
|
+
[[[1, 2, 3], [2, 3]], [[1, 3, 4], [2, 5]]]
|
|
649
|
+
sage: RSK(RSK_inverse(*pq, insertion=RSK.rules.EG, output='matrix'),
|
|
650
|
+
....: insertion=RSK.rules.EG)
|
|
651
|
+
[[[1, 2, 3], [2, 3]], [[1, 3, 4], [2, 5]]]
|
|
652
|
+
sage: RSK_inverse(*pq, insertion=RSK.rules.EG)
|
|
653
|
+
[[1, 2, 3, 4, 5], [2, 1, 2, 3, 2]]
|
|
654
|
+
|
|
655
|
+
The RSK algorithm (:func:`RSK`) built using the Edelman-Greene
|
|
656
|
+
insertion rule ``RuleEG`` is a bijection from reduced words of
|
|
657
|
+
permutations/elements of a type `A` Coxeter group to pairs
|
|
658
|
+
consisting of an increasing tableau and a standard tableau
|
|
659
|
+
of the same shape (see [EG1987]_ Theorem 6.25).
|
|
660
|
+
The inverse of this bijection is obtained using :func:`RSK_inverse`.
|
|
661
|
+
If the optional parameter ``output = 'permutation'`` is set in
|
|
662
|
+
:func:`RSK_inverse`, then the function returns not the
|
|
663
|
+
reduced word itself but the permutation (of smallest possible
|
|
664
|
+
size) whose reduced word it is (although the order of the
|
|
665
|
+
letters is reverse to the usual Sage convention)::
|
|
666
|
+
|
|
667
|
+
sage: w = RSK_inverse(*pq, insertion=RSK.rules.EG, output='permutation'); w
|
|
668
|
+
[4, 3, 1, 2]
|
|
669
|
+
sage: list(reversed(a)) in w.reduced_words()
|
|
670
|
+
True
|
|
671
|
+
|
|
672
|
+
TESTS:
|
|
673
|
+
|
|
674
|
+
Let us check that :func:`RSK_inverse` is the inverse of :func:`RSK`
|
|
675
|
+
on the different types of inputs/outputs for Edelman-Greene.
|
|
676
|
+
First we can check on the reduced words (specifically, those that can
|
|
677
|
+
be obtained using the ``reduced_word()`` method from permutations)::
|
|
678
|
+
|
|
679
|
+
sage: g = lambda w: RSK_inverse(*RSK(w, insertion=RSK.rules.EG),
|
|
680
|
+
....: insertion=RSK.rules.EG, output='word')
|
|
681
|
+
sage: all(p.reduced_word() == list(g(p.reduced_word()))
|
|
682
|
+
....: for n in range(7) for p in Permutations(n))
|
|
683
|
+
True
|
|
684
|
+
|
|
685
|
+
In case of non-standard tableaux `P, Q`::
|
|
686
|
+
|
|
687
|
+
sage: RSK_inverse(*RSK([1, 2, 3, 2, 1], insertion='EG'),
|
|
688
|
+
....: insertion='EG')
|
|
689
|
+
[[1, 2, 3, 4, 5], [1, 2, 3, 2, 1]]
|
|
690
|
+
sage: RSK_inverse(*RSK([1, 1, 1, 2], [1, 2, 3, 4],
|
|
691
|
+
....: insertion=RSK.rules.EG), insertion=RSK.rules.EG)
|
|
692
|
+
[[1, 1, 1, 2], [1, 2, 3, 4]]
|
|
693
|
+
sage: RSK_inverse(*RSK([1, 2, 3, 3], [2, 1, 2, 2], insertion='EG'),
|
|
694
|
+
....: insertion='EG')
|
|
695
|
+
[[1, 2, 3, 3], [2, 1, 2, 2]]
|
|
696
|
+
|
|
697
|
+
Since the column reading of the insertion tableau from
|
|
698
|
+
Edelman-Greene insertion gives one of reduced words for the
|
|
699
|
+
original permutation, we can also check for that::
|
|
700
|
+
|
|
701
|
+
sage: f = lambda p: [x for row in reversed(p) for x in row]
|
|
702
|
+
sage: g = lambda wd: RSK(wd, insertion=RSK.rules.EG)[0]
|
|
703
|
+
sage: all(p == Permutations(n).from_reduced_word(f(g(wd)))
|
|
704
|
+
....: for n in range(6) for p in Permutations(n)
|
|
705
|
+
....: for wd in p.reduced_words())
|
|
706
|
+
True
|
|
707
|
+
"""
|
|
708
|
+
|
|
709
|
+
def insertion(self, j, r):
|
|
710
|
+
r"""
|
|
711
|
+
Insert the letter ``j`` from the second row of the biword
|
|
712
|
+
into the row `r` using Edelman-Greene insertion,
|
|
713
|
+
if there is bumping to be done.
|
|
714
|
+
|
|
715
|
+
The row `r` is modified in place if bumping occurs. The bumped-out
|
|
716
|
+
entry, if it exists, is returned.
|
|
717
|
+
|
|
718
|
+
EXAMPLES::
|
|
719
|
+
|
|
720
|
+
sage: from sage.combinat.rsk import RuleEG
|
|
721
|
+
sage: qr, r = [1,2,3,4,5], [3,3,2,4,8]
|
|
722
|
+
sage: j = RuleEG().insertion(9, r)
|
|
723
|
+
sage: j is None
|
|
724
|
+
True
|
|
725
|
+
sage: qr, r = [1,2,3,4,5], [2,3,4,5,8]
|
|
726
|
+
sage: j = RuleEG().insertion(3, r); r
|
|
727
|
+
[2, 3, 4, 5, 8]
|
|
728
|
+
sage: j
|
|
729
|
+
4
|
|
730
|
+
sage: qr, r = [1,2,3,4,5], [2,3,5,5,8]
|
|
731
|
+
sage: j = RuleEG().insertion(3, r); r
|
|
732
|
+
[2, 3, 3, 5, 8]
|
|
733
|
+
sage: j
|
|
734
|
+
5
|
|
735
|
+
"""
|
|
736
|
+
if r[-1] <= j:
|
|
737
|
+
return None # j needs to be added at the end of the row
|
|
738
|
+
# Figure out where to insert j into the row r. The
|
|
739
|
+
# bisect command returns the position of the least
|
|
740
|
+
# element of r greater than j. We will call it y.
|
|
741
|
+
y_pos = bisect_right(r, j)
|
|
742
|
+
if r[y_pos] == j + 1 and y_pos > 0 and j == r[y_pos - 1]:
|
|
743
|
+
# Special bump: Nothing to do except increment j by 1
|
|
744
|
+
j += 1
|
|
745
|
+
else:
|
|
746
|
+
# Switch j and y
|
|
747
|
+
j, r[y_pos] = r[y_pos], j
|
|
748
|
+
return j
|
|
749
|
+
|
|
750
|
+
def reverse_insertion(self, x, row):
|
|
751
|
+
r"""
|
|
752
|
+
Reverse bump the row ``row`` of the current insertion tableau
|
|
753
|
+
with the number ``x``.
|
|
754
|
+
|
|
755
|
+
The row ``row`` is modified in place. The bumped-out entry
|
|
756
|
+
is returned.
|
|
757
|
+
|
|
758
|
+
EXAMPLES::
|
|
759
|
+
|
|
760
|
+
sage: from sage.combinat.rsk import RuleEG
|
|
761
|
+
sage: r = [1,1,1,2,3,3]
|
|
762
|
+
sage: x = RuleEG().reverse_insertion(3, r); r
|
|
763
|
+
[1, 1, 1, 2, 3, 3]
|
|
764
|
+
sage: x
|
|
765
|
+
2
|
|
766
|
+
"""
|
|
767
|
+
y_pos = bisect_left(row, x) - 1
|
|
768
|
+
if row[y_pos] == x - 1 and y_pos < len(row)-1 and row[y_pos+1] == x:
|
|
769
|
+
# Nothing to do except decrement x by 1.
|
|
770
|
+
# (Case 1 on p. 74 of Edelman-Greene [EG1987]_.)
|
|
771
|
+
x -= 1
|
|
772
|
+
else:
|
|
773
|
+
# switch x and y
|
|
774
|
+
x, row[y_pos] = row[y_pos], x
|
|
775
|
+
return x
|
|
776
|
+
|
|
777
|
+
def _backward_format_output(self, lower_row, upper_row, output,
|
|
778
|
+
p_is_standard, q_is_standard):
|
|
779
|
+
r"""
|
|
780
|
+
Return the final output of the ``RSK_inverse`` correspondence
|
|
781
|
+
from the output of the corresponding ``backward_rule``.
|
|
782
|
+
|
|
783
|
+
.. NOTE::
|
|
784
|
+
|
|
785
|
+
The default implementation of ``backward_rule`` lists
|
|
786
|
+
bumped-out entries in the order in which the reverse
|
|
787
|
+
bumping happens, which is *opposite* to the order of the
|
|
788
|
+
final output.
|
|
789
|
+
|
|
790
|
+
EXAMPLES::
|
|
791
|
+
|
|
792
|
+
sage: from sage.combinat.rsk import RuleEG
|
|
793
|
+
sage: RuleEG()._backward_format_output([1, 2, 3, 4], None,
|
|
794
|
+
....: 'permutation', True, False)
|
|
795
|
+
Traceback (most recent call last):
|
|
796
|
+
...
|
|
797
|
+
TypeError: q must be standard to have a permutation as valid output
|
|
798
|
+
"""
|
|
799
|
+
if q_is_standard and output == 'permutation':
|
|
800
|
+
n = 0
|
|
801
|
+
if list(lower_row):
|
|
802
|
+
n = max(list(lower_row)) + 1
|
|
803
|
+
from sage.combinat.permutation import Permutations
|
|
804
|
+
return Permutations(n).from_reduced_word(list(lower_row))
|
|
805
|
+
else:
|
|
806
|
+
return super()._backward_format_output(lower_row, upper_row, output,
|
|
807
|
+
p_is_standard, q_is_standard)
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
class RuleHecke(Rule):
|
|
811
|
+
r"""
|
|
812
|
+
Rule for Hecke insertion.
|
|
813
|
+
|
|
814
|
+
The Hecke RSK algorithm is similar to the classical RSK algorithm,
|
|
815
|
+
but is defined using the Hecke insertion introduced in in
|
|
816
|
+
[BKSTY06]_ (but using rows instead of columns).
|
|
817
|
+
It is not clear in what generality it works; thus, following
|
|
818
|
+
[BKSTY06]_, we shall assume that our biword `p` has top row
|
|
819
|
+
`(1, 2, \ldots, n)` (or, at least, has its top row strictly
|
|
820
|
+
increasing).
|
|
821
|
+
|
|
822
|
+
The Hecke RSK algorithm returns a pair of an increasing tableau
|
|
823
|
+
and a set-valued standard tableau. If
|
|
824
|
+
`p = ((j_0, k_0), (j_1, k_1), \ldots, (j_{\ell-1}, k_{\ell-1}))`,
|
|
825
|
+
then the algorithm recursively constructs pairs
|
|
826
|
+
`(P_0, Q_0), (P_1, Q_1), \ldots, (P_\ell, Q_\ell)` of tableaux.
|
|
827
|
+
The construction of `P_{t+1}` and `Q_{t+1}` from `P_t`, `Q_t`,
|
|
828
|
+
`j_t` and `k_t` proceeds as follows: Set `i = j_t`, `x = k_t`,
|
|
829
|
+
`P = P_t` and `Q = Q_t`. We are going to insert `x` into the
|
|
830
|
+
increasing tableau `P` and update the set-valued "recording
|
|
831
|
+
tableau" `Q` accordingly. As in the classical RSK algorithm, we
|
|
832
|
+
first insert `x` into row `1` of `P`, then into row `2` of the
|
|
833
|
+
resulting tableau, and so on, until the construction terminates.
|
|
834
|
+
The details are different: Suppose we are inserting `x` into
|
|
835
|
+
row `R` of `P`. If (Case 1) there exists an entry `y` in row `R`
|
|
836
|
+
such that `x < y`, then let `y` be the minimal such entry. We
|
|
837
|
+
replace this entry `y` with `x` if the result is still an
|
|
838
|
+
increasing tableau; in either subcase, we then continue
|
|
839
|
+
recursively, inserting `y` into the next row of `P`.
|
|
840
|
+
If, on the other hand, (Case 2) no such `y` exists, then we
|
|
841
|
+
append `x` to the end of `R` if the result is an increasing
|
|
842
|
+
tableau (Subcase 2.1), and otherwise (Subcase 2.2) do nothing.
|
|
843
|
+
Furthermore, in Subcase 2.1, we add the box that we have just
|
|
844
|
+
filled with `x` in `P` to the shape of `Q`, and fill it with
|
|
845
|
+
the one-element set `\{i\}`. In Subcase 2.2, we find the
|
|
846
|
+
bottommost box of the column containing the rightmost box of
|
|
847
|
+
row `R`, and add `i` to the entry of `Q` in this box (this
|
|
848
|
+
entry is a set, since `Q` is set-valued). In either
|
|
849
|
+
subcase, we terminate the recursion, and set
|
|
850
|
+
`P_{t+1} = P` and `Q_{t+1} = Q`.
|
|
851
|
+
|
|
852
|
+
Notice that set-valued tableaux are encoded as tableaux whose
|
|
853
|
+
entries are tuples of positive integers; each such tuple is strictly
|
|
854
|
+
increasing and encodes a set (namely, the set of its entries).
|
|
855
|
+
|
|
856
|
+
EXAMPLES:
|
|
857
|
+
|
|
858
|
+
As an example of Hecke insertion, we reproduce
|
|
859
|
+
Example 2.1 in :arxiv:`0801.1319v2`::
|
|
860
|
+
|
|
861
|
+
sage: w = [5, 4, 1, 3, 4, 2, 5, 1, 2, 1, 4, 2, 4]
|
|
862
|
+
sage: P,Q = RSK(w, insertion=RSK.rules.Hecke); [P,Q]
|
|
863
|
+
[[[1, 2, 4, 5], [2, 4, 5], [3, 5], [4], [5]],
|
|
864
|
+
[[(1,), (4,), (5,), (7,)],
|
|
865
|
+
[(2,), (9,), (11, 13)],
|
|
866
|
+
[(3,), (12,)],
|
|
867
|
+
[(6,)],
|
|
868
|
+
[(8, 10)]]]
|
|
869
|
+
sage: wp = RSK_inverse(P, Q, insertion=RSK.rules.Hecke,
|
|
870
|
+
....: output='list'); wp
|
|
871
|
+
[5, 4, 1, 3, 4, 2, 5, 1, 2, 1, 4, 2, 4]
|
|
872
|
+
sage: wp == w
|
|
873
|
+
True
|
|
874
|
+
"""
|
|
875
|
+
|
|
876
|
+
def forward_rule(self, obj1, obj2, check_standard=False):
|
|
877
|
+
r"""
|
|
878
|
+
Return a pair of tableaux obtained by applying Hecke
|
|
879
|
+
insertion to the generalized permutation ``[obj1, obj2]``.
|
|
880
|
+
|
|
881
|
+
INPUT:
|
|
882
|
+
|
|
883
|
+
- ``obj1``, ``obj2`` -- can be one of the following ways to
|
|
884
|
+
represent a generalized permutation (or, equivalently,
|
|
885
|
+
biword):
|
|
886
|
+
|
|
887
|
+
- two lists ``obj1`` and ``obj2`` of equal length,
|
|
888
|
+
to be interpreted as the top row and the bottom row of
|
|
889
|
+
the biword
|
|
890
|
+
|
|
891
|
+
- a word ``obj1`` in an ordered alphabet, to be
|
|
892
|
+
interpreted as the bottom row of the biword (in this
|
|
893
|
+
case, ``obj2`` is ``None``; the top row of the biword
|
|
894
|
+
is understood to be `(1, 2, \ldots, n)` by default)
|
|
895
|
+
|
|
896
|
+
- ``check_standard`` -- boolean (default: ``False``); check if either
|
|
897
|
+
of the resulting tableaux is a standard tableau, and if so, typecast
|
|
898
|
+
it as such
|
|
899
|
+
|
|
900
|
+
EXAMPLES::
|
|
901
|
+
|
|
902
|
+
sage: from sage.combinat.rsk import RuleHecke
|
|
903
|
+
sage: p, q = RuleHecke().forward_rule([3,3,2,4,1], None);p
|
|
904
|
+
[[1, 4], [2], [3]]
|
|
905
|
+
sage: q
|
|
906
|
+
[[(1, 2), (4,)], [(3,)], [(5,)]]
|
|
907
|
+
sage: isinstance(p, SemistandardTableau)
|
|
908
|
+
True
|
|
909
|
+
sage: isinstance(q, Tableau)
|
|
910
|
+
True
|
|
911
|
+
"""
|
|
912
|
+
from sage.combinat.tableau import SemistandardTableau, Tableau
|
|
913
|
+
|
|
914
|
+
if obj2 is None:
|
|
915
|
+
obj2 = obj1
|
|
916
|
+
obj1 = list(range(1, len(obj1) + 1))
|
|
917
|
+
|
|
918
|
+
p = [] # the "insertion" tableau
|
|
919
|
+
q = [] # the "recording" tableau
|
|
920
|
+
|
|
921
|
+
for i, j in zip(obj1, obj2):
|
|
922
|
+
for ir, r in enumerate(p):
|
|
923
|
+
j1 = self.insertion(j, ir, r, p)
|
|
924
|
+
|
|
925
|
+
if j1 is None:
|
|
926
|
+
# We must have len(p[ir-1]) > len(r), since j is coming
|
|
927
|
+
# from the previous row.
|
|
928
|
+
if r[-1] < j and (ir == 0 or p[ir-1][len(r)] < j):
|
|
929
|
+
# We can add a box to the row
|
|
930
|
+
r.append(j)
|
|
931
|
+
q[ir].append((i,)) # Values are always inserted to the right
|
|
932
|
+
else:
|
|
933
|
+
# We must append i to the bottom of this column
|
|
934
|
+
l = len(r) - 1
|
|
935
|
+
while ir < len(q) and len(q[ir]) > l:
|
|
936
|
+
ir += 1
|
|
937
|
+
q[ir-1][-1] = q[ir-1][-1] + (i,)
|
|
938
|
+
break
|
|
939
|
+
else:
|
|
940
|
+
j = j1
|
|
941
|
+
else:
|
|
942
|
+
# We made through all of the rows of p without breaking
|
|
943
|
+
# so we need to add a new row to p and q.
|
|
944
|
+
p.append([j])
|
|
945
|
+
q.append([(i,)])
|
|
946
|
+
return [SemistandardTableau(p), Tableau(q)]
|
|
947
|
+
|
|
948
|
+
def backward_rule(self, p, q, output):
|
|
949
|
+
r"""
|
|
950
|
+
Return the generalized permutation obtained by applying reverse
|
|
951
|
+
Hecke insertion to a pair of tableaux ``(p, q)``.
|
|
952
|
+
|
|
953
|
+
INPUT:
|
|
954
|
+
|
|
955
|
+
- ``p``, ``q`` -- two tableaux of the same shape
|
|
956
|
+
|
|
957
|
+
- ``output`` -- (default: ``'array'``) if ``q`` is semi-standard:
|
|
958
|
+
|
|
959
|
+
- ``'array'`` -- as a two-line array (i.e. generalized permutation
|
|
960
|
+
or biword)
|
|
961
|
+
|
|
962
|
+
and if ``q`` is standard set-valued, we can have the output:
|
|
963
|
+
|
|
964
|
+
- ``'word'`` -- as a word
|
|
965
|
+
- ``'list'`` -- as a list
|
|
966
|
+
|
|
967
|
+
EXAMPLES::
|
|
968
|
+
|
|
969
|
+
sage: from sage.combinat.rsk import RuleHecke
|
|
970
|
+
sage: t1 = Tableau([[1, 4], [2], [3]])
|
|
971
|
+
sage: t2 = Tableau([[(1, 2), (4,)], [(3,)], [(5,)]])
|
|
972
|
+
sage: RuleHecke().backward_rule(t1, t2, 'array')
|
|
973
|
+
[[1, 2, 3, 4, 5], [3, 3, 2, 4, 1]]
|
|
974
|
+
sage: t1 = Tableau([[1, 4], [2, 3]])
|
|
975
|
+
sage: t2 = Tableau([[(1, 2), (4,)], [(3,)], [(5,)]])
|
|
976
|
+
sage: RuleHecke().backward_rule(t1, t2, 'array')
|
|
977
|
+
Traceback (most recent call last):
|
|
978
|
+
...
|
|
979
|
+
ValueError: p(=[[1, 4], [2, 3]]) and
|
|
980
|
+
q(=[[(1, 2), (4,)], [(3,)], [(5,)]]) must have the same shape
|
|
981
|
+
"""
|
|
982
|
+
if p.shape() != q.shape():
|
|
983
|
+
raise ValueError("p(=%s) and q(=%s) must have the same shape" % (p, q))
|
|
984
|
+
from sage.combinat.tableau import SemistandardTableaux
|
|
985
|
+
if p not in SemistandardTableaux():
|
|
986
|
+
raise ValueError("p(=%s) must be a semistandard tableau" % p)
|
|
987
|
+
|
|
988
|
+
# Make a copy of p and q since this is destructive to it
|
|
989
|
+
p_copy = [list(row) for row in p]
|
|
990
|
+
q_copy = [[list(v) for v in row] for row in q]
|
|
991
|
+
# We shall work on these copies of p and q. Notice that p might get
|
|
992
|
+
# some empty rows in the process; we do not bother pruning them, as
|
|
993
|
+
# they do not matter.
|
|
994
|
+
|
|
995
|
+
# upper_row and lower_row will be the upper and lower rows of the
|
|
996
|
+
# generalized permutation we get as a result, but both reversed.
|
|
997
|
+
upper_row = []
|
|
998
|
+
lower_row = []
|
|
999
|
+
d = {}
|
|
1000
|
+
for ri, row in enumerate(q):
|
|
1001
|
+
for ci, entry in enumerate(row):
|
|
1002
|
+
for val in entry:
|
|
1003
|
+
if val in d:
|
|
1004
|
+
d[val][ci] = ri
|
|
1005
|
+
else:
|
|
1006
|
+
d[val] = {ci: ri}
|
|
1007
|
+
# d is now a double family such that for every integers k and j,
|
|
1008
|
+
# the value d[k][j] is the row i such that the (i, j)-th cell of
|
|
1009
|
+
# q is filled with k.
|
|
1010
|
+
for value, row_dict in sorted(d.items(), key=lambda x: -x[0]):
|
|
1011
|
+
for i in sorted(row_dict.values(), reverse=True):
|
|
1012
|
+
# These are always the right-most entry
|
|
1013
|
+
should_be_value = q_copy[i][-1].pop()
|
|
1014
|
+
assert value == should_be_value
|
|
1015
|
+
if not q_copy[i][-1]:
|
|
1016
|
+
# That is, if value was alone in cell q_copy[i][-1].
|
|
1017
|
+
q_copy[i].pop()
|
|
1018
|
+
x = p_copy[i].pop()
|
|
1019
|
+
else:
|
|
1020
|
+
x = p_copy[i][-1]
|
|
1021
|
+
while i > 0:
|
|
1022
|
+
i -= 1
|
|
1023
|
+
row = p_copy[i]
|
|
1024
|
+
x = self.reverse_insertion(i, x, row, p_copy)
|
|
1025
|
+
lower_row.append(x)
|
|
1026
|
+
upper_row.append(value)
|
|
1027
|
+
return self._backward_format_output(lower_row, upper_row, output, False, False)
|
|
1028
|
+
|
|
1029
|
+
def insertion(self, j, ir, r, p):
|
|
1030
|
+
r"""
|
|
1031
|
+
Insert the letter ``j`` from the second row of the biword
|
|
1032
|
+
into the row `r` of the increasing tableau `p` using
|
|
1033
|
+
Hecke insertion, provided that `r` is the `ir`-th row
|
|
1034
|
+
of `p`, and provided that there is bumping to be done.
|
|
1035
|
+
|
|
1036
|
+
The row `r` is modified in place if bumping occurs. The bumped-out
|
|
1037
|
+
entry, if it exists, is returned.
|
|
1038
|
+
|
|
1039
|
+
EXAMPLES::
|
|
1040
|
+
|
|
1041
|
+
sage: from sage.combinat.rsk import RuleHecke
|
|
1042
|
+
sage: from bisect import bisect_right
|
|
1043
|
+
sage: p, q, r = [], [], [3,3,8,8,8,9]
|
|
1044
|
+
sage: j, ir = 8, 1
|
|
1045
|
+
sage: j1 = RuleHecke().insertion(j, ir, r, p)
|
|
1046
|
+
sage: j1 == r[bisect_right(r, j)]
|
|
1047
|
+
True
|
|
1048
|
+
"""
|
|
1049
|
+
if r[-1] <= j:
|
|
1050
|
+
return None # j needs to be added at the end of the row.
|
|
1051
|
+
# Figure out where to insert j into the row r. The
|
|
1052
|
+
# bisect command returns the position of the least
|
|
1053
|
+
# element of r greater than j. We will call it y.
|
|
1054
|
+
y_pos = bisect_right(r, j)
|
|
1055
|
+
y = r[y_pos]
|
|
1056
|
+
# Check to see if we can swap j for y
|
|
1057
|
+
if (y_pos == 0 or r[y_pos-1] < j) and (ir == 0 or p[ir-1][y_pos] < j):
|
|
1058
|
+
r[y_pos] = j
|
|
1059
|
+
j = y
|
|
1060
|
+
return j
|
|
1061
|
+
|
|
1062
|
+
def reverse_insertion(self, i, x, row, p):
|
|
1063
|
+
r"""
|
|
1064
|
+
Reverse bump the row ``row`` of the current insertion tableau
|
|
1065
|
+
``p`` with the number ``x``, provided that ``row`` is the
|
|
1066
|
+
`i`-th row of `p`.
|
|
1067
|
+
|
|
1068
|
+
The row ``row`` is modified in place. The bumped-out entry
|
|
1069
|
+
is returned.
|
|
1070
|
+
|
|
1071
|
+
EXAMPLES::
|
|
1072
|
+
|
|
1073
|
+
sage: from sage.combinat.rsk import RuleHecke
|
|
1074
|
+
sage: from bisect import bisect_left
|
|
1075
|
+
sage: r = [2,3,3,4,8,9]
|
|
1076
|
+
sage: x, i, p = 9, 1, [1, 2]
|
|
1077
|
+
sage: x1 = RuleHecke().reverse_insertion(i, x, r, p)
|
|
1078
|
+
sage: x1 == r[bisect_left(r,x) - 1]
|
|
1079
|
+
True
|
|
1080
|
+
"""
|
|
1081
|
+
y_pos = bisect_left(row, x) - 1
|
|
1082
|
+
y = row[y_pos]
|
|
1083
|
+
# Check to see if we can swap x for y
|
|
1084
|
+
if ((y_pos == len(row) - 1 or x < row[y_pos+1])
|
|
1085
|
+
and (i == len(p) - 1 or len(p[i+1]) <= y_pos
|
|
1086
|
+
or x < p[i+1][y_pos])):
|
|
1087
|
+
row[y_pos] = x
|
|
1088
|
+
x = y
|
|
1089
|
+
return x
|
|
1090
|
+
|
|
1091
|
+
def _backward_format_output(self, lower_row, upper_row, output,
|
|
1092
|
+
p_is_standard, q_is_standard):
|
|
1093
|
+
r"""
|
|
1094
|
+
Return the final output of the ``RSK_inverse`` correspondence
|
|
1095
|
+
from the output of the corresponding ``backward_rule``.
|
|
1096
|
+
|
|
1097
|
+
.. NOTE::
|
|
1098
|
+
|
|
1099
|
+
The default implementation of ``backward_rule`` lists
|
|
1100
|
+
bumped-out entries in the order in which the reverse
|
|
1101
|
+
bumping happens, which is *opposite* to the order of the
|
|
1102
|
+
final output.
|
|
1103
|
+
|
|
1104
|
+
EXAMPLES::
|
|
1105
|
+
|
|
1106
|
+
sage: from sage.combinat.rsk import RuleHecke
|
|
1107
|
+
sage: RuleHecke()._backward_format_output([1, 1, 3, 9], [1, 2, 3, 4],
|
|
1108
|
+
....: 'array', False, False)
|
|
1109
|
+
[[4, 3, 2, 1], [9, 3, 1, 1]]
|
|
1110
|
+
sage: RuleHecke()._backward_format_output([1, 1, 3, 9], [1, 2, 3, 4],
|
|
1111
|
+
....: 'word', False, False)
|
|
1112
|
+
Traceback (most recent call last):
|
|
1113
|
+
...
|
|
1114
|
+
TypeError: q must be standard to have a word as valid output
|
|
1115
|
+
sage: RuleHecke()._backward_format_output([1, 1, 3, 9], [4, 3, 2, 1],
|
|
1116
|
+
....: 'word', False, False)
|
|
1117
|
+
word: 9311
|
|
1118
|
+
sage: RuleHecke()._backward_format_output([1, 1, 3, 9], [1, 2, 3, 4],
|
|
1119
|
+
....: 'list', False, False)
|
|
1120
|
+
Traceback (most recent call last):
|
|
1121
|
+
...
|
|
1122
|
+
TypeError: q must be standard to have a list as valid output
|
|
1123
|
+
sage: RuleHecke()._backward_format_output([1, 1, 3, 9], [4, 3, 2, 1],
|
|
1124
|
+
....: 'list', False, False)
|
|
1125
|
+
[9, 3, 1, 1]
|
|
1126
|
+
sage: RuleHecke()._backward_format_output([1, 1, 3, 9], [1, 2, 3, 4],
|
|
1127
|
+
....: 'random_type', False, False)
|
|
1128
|
+
Traceback (most recent call last):
|
|
1129
|
+
...
|
|
1130
|
+
ValueError: invalid output option
|
|
1131
|
+
"""
|
|
1132
|
+
if output == 'array':
|
|
1133
|
+
return [list(reversed(upper_row)), list(reversed(lower_row))]
|
|
1134
|
+
is_standard = (upper_row == list(range(len(upper_row), 0, -1)))
|
|
1135
|
+
if output == 'word':
|
|
1136
|
+
if not is_standard:
|
|
1137
|
+
raise TypeError(
|
|
1138
|
+
"q must be standard to have a %s as valid output" % output)
|
|
1139
|
+
from sage.combinat.words.word import Word
|
|
1140
|
+
return Word(reversed(lower_row))
|
|
1141
|
+
if output == 'list':
|
|
1142
|
+
if not is_standard:
|
|
1143
|
+
raise TypeError(
|
|
1144
|
+
"q must be standard to have a %s as valid output" % output)
|
|
1145
|
+
return list(reversed(lower_row))
|
|
1146
|
+
raise ValueError("invalid output option")
|
|
1147
|
+
|
|
1148
|
+
|
|
1149
|
+
class RuleDualRSK(Rule):
|
|
1150
|
+
r"""
|
|
1151
|
+
Rule for dual RSK insertion.
|
|
1152
|
+
|
|
1153
|
+
Dual RSK insertion differs from classical RSK insertion in the
|
|
1154
|
+
following ways:
|
|
1155
|
+
|
|
1156
|
+
* The input (in terms of biwords) is no longer an arbitrary biword,
|
|
1157
|
+
but rather a strict biword (i.e., a pair of two lists
|
|
1158
|
+
`[a_1, a_2, \ldots, a_n]` and `[b_1, b_2, \ldots, b_n]` that
|
|
1159
|
+
satisfy the strict inequalities
|
|
1160
|
+
`(a_1, b_1) < (a_2, b_2) < \cdots < (a_n, b_n)` in
|
|
1161
|
+
lexicographic order).
|
|
1162
|
+
In terms of matrices, this means that the input is not an
|
|
1163
|
+
arbitrary matrix with nonnegative integer entries, but rather
|
|
1164
|
+
a `\{0, 1\}`-matrix (i.e., a matrix whose entries are `0`'s
|
|
1165
|
+
and `1`'s).
|
|
1166
|
+
|
|
1167
|
+
* The output still consists of two tableaux `(P, Q)` of equal
|
|
1168
|
+
shapes, but rather than both of them being semistandard, now
|
|
1169
|
+
`P` is row-strict (i.e., its transpose is semistandard) while
|
|
1170
|
+
`Q` is semistandard.
|
|
1171
|
+
|
|
1172
|
+
* The main difference is in the way bumping works. Namely,
|
|
1173
|
+
when a number `k_i` is inserted into the `i`-th row of `P`,
|
|
1174
|
+
it bumps out the first integer greater **or equal to** `k_i`
|
|
1175
|
+
in this row (rather than greater than `k_i`).
|
|
1176
|
+
|
|
1177
|
+
The RSK and dual RSK algorithms agree for permutation matrices.
|
|
1178
|
+
|
|
1179
|
+
For more information, see Chapter 7, Section 14 in [Sta-EC2]_
|
|
1180
|
+
(where dual RSK is called `\mathrm{RSK}^{\ast}`) or the third
|
|
1181
|
+
solution to Exercise 2.7.12(a) in [GR2018v5sol]_.
|
|
1182
|
+
|
|
1183
|
+
EXAMPLES::
|
|
1184
|
+
|
|
1185
|
+
sage: RSK([3,3,2,4,1], insertion=RSK.rules.dualRSK)
|
|
1186
|
+
[[[1, 4], [2], [3], [3]], [[1, 4], [2], [3], [5]]]
|
|
1187
|
+
sage: RSK(Word([3,3,2,4,1]), insertion=RSK.rules.dualRSK)
|
|
1188
|
+
[[[1, 4], [2], [3], [3]], [[1, 4], [2], [3], [5]]]
|
|
1189
|
+
sage: RSK(Word([2,3,3,2,1,3,2,3]), insertion=RSK.rules.dualRSK)
|
|
1190
|
+
[[[1, 2, 3], [2, 3], [2, 3], [3]], [[1, 2, 8], [3, 6], [4, 7], [5]]]
|
|
1191
|
+
|
|
1192
|
+
Using dual RSK insertion with a strict biword::
|
|
1193
|
+
|
|
1194
|
+
sage: RSK([1,1,2,4,4,5],[2,4,1,1,3,2], insertion=RSK.rules.dualRSK)
|
|
1195
|
+
[[[1, 2], [1, 3], [2, 4]], [[1, 1], [2, 4], [4, 5]]]
|
|
1196
|
+
sage: RSK([1,1,2,3,3,4,5],[1,3,2,1,3,3,2], insertion=RSK.rules.dualRSK)
|
|
1197
|
+
[[[1, 2, 3], [1, 2], [3], [3]], [[1, 1, 3], [2, 4], [3], [5]]]
|
|
1198
|
+
sage: RSK([1, 2, 2, 2], [2, 1, 2, 4], insertion=RSK.rules.dualRSK)
|
|
1199
|
+
[[[1, 2, 4], [2]], [[1, 2, 2], [2]]]
|
|
1200
|
+
sage: RSK(Word([1,1,3,4,4]), [1,4,2,1,3], insertion=RSK.rules.dualRSK)
|
|
1201
|
+
[[[1, 2, 3], [1], [4]], [[1, 1, 4], [3], [4]]]
|
|
1202
|
+
sage: RSK([1,3,3,4,4], Word([6,1,2,1,7]), insertion=RSK.rules.dualRSK)
|
|
1203
|
+
[[[1, 2, 7], [1], [6]], [[1, 3, 4], [3], [4]]]
|
|
1204
|
+
|
|
1205
|
+
Using dual RSK insertion with a `\{0, 1\}`-matrix::
|
|
1206
|
+
|
|
1207
|
+
sage: RSK(matrix([[0,1],[1,1]]), insertion=RSK.rules.dualRSK)
|
|
1208
|
+
[[[1, 2], [2]], [[1, 2], [2]]]
|
|
1209
|
+
|
|
1210
|
+
We can also give it something looking like a matrix::
|
|
1211
|
+
|
|
1212
|
+
sage: RSK([[0,1],[1,1]], insertion=RSK.rules.dualRSK)
|
|
1213
|
+
[[[1, 2], [2]], [[1, 2], [2]]]
|
|
1214
|
+
|
|
1215
|
+
Let us now call the inverse correspondence::
|
|
1216
|
+
|
|
1217
|
+
sage: RSK_inverse(*RSK([1, 2, 2, 2], [2, 1, 2, 3],
|
|
1218
|
+
....: insertion=RSK.rules.dualRSK),insertion=RSK.rules.dualRSK)
|
|
1219
|
+
[[1, 2, 2, 2], [2, 1, 2, 3]]
|
|
1220
|
+
sage: P,Q = RSK([1, 2, 2, 2], [2, 1, 2, 3],insertion=RSK.rules.dualRSK)
|
|
1221
|
+
sage: RSK_inverse(P, Q, insertion=RSK.rules.dualRSK)
|
|
1222
|
+
[[1, 2, 2, 2], [2, 1, 2, 3]]
|
|
1223
|
+
|
|
1224
|
+
When applied to two standard tableaux, reverse dual RSK
|
|
1225
|
+
insertion behaves identically to the usual reverse RSK insertion::
|
|
1226
|
+
|
|
1227
|
+
sage: t1 = Tableau([[1, 2, 5], [3], [4]])
|
|
1228
|
+
sage: t2 = Tableau([[1, 2, 3], [4], [5]])
|
|
1229
|
+
sage: RSK_inverse(t1, t2, insertion=RSK.rules.dualRSK)
|
|
1230
|
+
[[1, 2, 3, 4, 5], [1, 4, 5, 3, 2]]
|
|
1231
|
+
sage: RSK_inverse(t1, t2, 'word', insertion=RSK.rules.dualRSK)
|
|
1232
|
+
word: 14532
|
|
1233
|
+
sage: RSK_inverse(t1, t2, 'matrix', insertion=RSK.rules.dualRSK)
|
|
1234
|
+
[1 0 0 0 0]
|
|
1235
|
+
[0 0 0 1 0]
|
|
1236
|
+
[0 0 0 0 1]
|
|
1237
|
+
[0 0 1 0 0]
|
|
1238
|
+
[0 1 0 0 0]
|
|
1239
|
+
sage: RSK_inverse(t1, t2, 'permutation', insertion=RSK.rules.dualRSK)
|
|
1240
|
+
[1, 4, 5, 3, 2]
|
|
1241
|
+
sage: RSK_inverse(t1, t1, 'permutation', insertion=RSK.rules.dualRSK)
|
|
1242
|
+
[1, 4, 3, 2, 5]
|
|
1243
|
+
sage: RSK_inverse(t2, t2, 'permutation', insertion=RSK.rules.dualRSK)
|
|
1244
|
+
[1, 2, 5, 4, 3]
|
|
1245
|
+
sage: RSK_inverse(t2, t1, 'permutation', insertion=RSK.rules.dualRSK)
|
|
1246
|
+
[1, 5, 4, 2, 3]
|
|
1247
|
+
|
|
1248
|
+
Let us check that forward and backward dual RSK are mutually
|
|
1249
|
+
inverse when the first tableau is merely transpose semistandard::
|
|
1250
|
+
|
|
1251
|
+
sage: p = Tableau([[1,2,2],[1]]); q = Tableau([[1,2,4],[3]])
|
|
1252
|
+
sage: ret = RSK_inverse(p, q, insertion=RSK.rules.dualRSK); ret
|
|
1253
|
+
[[1, 2, 3, 4], [1, 2, 1, 2]]
|
|
1254
|
+
sage: RSK_inverse(p, q, 'word', insertion=RSK.rules.dualRSK)
|
|
1255
|
+
word: 1212
|
|
1256
|
+
|
|
1257
|
+
In general for dual RSK::
|
|
1258
|
+
|
|
1259
|
+
sage: p = Tableau([[1,1,2],[1]]); q = Tableau([[1,3,3],[2]])
|
|
1260
|
+
sage: RSK_inverse(p, q, insertion=RSK.rules.dualRSK)
|
|
1261
|
+
[[1, 2, 3, 3], [1, 1, 1, 2]]
|
|
1262
|
+
sage: RSK_inverse(p, q, 'matrix', insertion=RSK.rules.dualRSK)
|
|
1263
|
+
[1 0]
|
|
1264
|
+
[1 0]
|
|
1265
|
+
[1 1]
|
|
1266
|
+
|
|
1267
|
+
TESTS:
|
|
1268
|
+
|
|
1269
|
+
Empty objects::
|
|
1270
|
+
|
|
1271
|
+
sage: RSK(Permutation([]), insertion=RSK.rules.dualRSK)
|
|
1272
|
+
[[], []]
|
|
1273
|
+
sage: RSK(Word([]), insertion=RSK.rules.dualRSK)
|
|
1274
|
+
[[], []]
|
|
1275
|
+
sage: RSK(matrix([[]]), insertion=RSK.rules.dualRSK)
|
|
1276
|
+
[[], []]
|
|
1277
|
+
sage: RSK([], [], insertion=RSK.rules.dualRSK)
|
|
1278
|
+
[[], []]
|
|
1279
|
+
sage: RSK([[]], insertion=RSK.rules.dualRSK)
|
|
1280
|
+
[[], []]
|
|
1281
|
+
|
|
1282
|
+
Check that :func:`RSK_inverse` is the inverse of :func:`RSK` on the
|
|
1283
|
+
different types of inputs/outputs::
|
|
1284
|
+
|
|
1285
|
+
sage: RSK_inverse(Tableau([]), Tableau([]),
|
|
1286
|
+
....: insertion=RSK.rules.dualRSK)
|
|
1287
|
+
[[], []]
|
|
1288
|
+
sage: f = lambda p: RSK_inverse(*RSK(p, insertion=RSK.rules.dualRSK),
|
|
1289
|
+
....: output='permutation', insertion=RSK.rules.dualRSK)
|
|
1290
|
+
sage: all(p == f(p) for n in range(7) for p in Permutations(n))
|
|
1291
|
+
True
|
|
1292
|
+
sage: all(RSK_inverse(*RSK(w, insertion=RSK.rules.dualRSK),
|
|
1293
|
+
....: output='word', insertion=RSK.rules.dualRSK) == w
|
|
1294
|
+
....: for n in range(4) for w in Words(5, n))
|
|
1295
|
+
True
|
|
1296
|
+
sage: from sage.combinat.integer_matrices import IntegerMatrices
|
|
1297
|
+
sage: M = IntegerMatrices([1,2,2,1], [3,1,1,1]) #this is probably wrong
|
|
1298
|
+
sage: all(RSK_inverse(*RSK(m, insertion=RSK.rules.dualRSK),
|
|
1299
|
+
....: output='matrix', insertion=RSK.rules.dualRSK) == m
|
|
1300
|
+
....: for m in M if all(x in [0, 1] for x in m))
|
|
1301
|
+
True
|
|
1302
|
+
|
|
1303
|
+
sage: n = ZZ.random_element(200)
|
|
1304
|
+
sage: p = Permutations(n).random_element()
|
|
1305
|
+
sage: True if p == f(p) else p
|
|
1306
|
+
True
|
|
1307
|
+
|
|
1308
|
+
Checking that the tableaux should be of same shape::
|
|
1309
|
+
|
|
1310
|
+
sage: RSK_inverse(Tableau([[1,2,3]]), Tableau([[1,2]]),
|
|
1311
|
+
....: insertion=RSK.rules.dualRSK)
|
|
1312
|
+
Traceback (most recent call last):
|
|
1313
|
+
...
|
|
1314
|
+
ValueError: p(=[[1, 2, 3]]) and q(=[[1, 2]]) must have the same shape
|
|
1315
|
+
"""
|
|
1316
|
+
|
|
1317
|
+
def to_pairs(self, obj1=None, obj2=None, check=True):
|
|
1318
|
+
r"""
|
|
1319
|
+
Given a valid input for the dual RSK algorithm, such as
|
|
1320
|
+
two `n`-tuples ``obj1`` `= [a_1, a_2, \ldots, a_n]`
|
|
1321
|
+
and ``obj2`` `= [b_1, b_2, \ldots, b_n]` forming a strict
|
|
1322
|
+
biword (i.e., satisfying `a_1 \leq a_2 \leq \cdots \leq a_n`,
|
|
1323
|
+
and if `a_i = a_{i+1}`, then `b_i < b_{i+1}`) or a
|
|
1324
|
+
`\{0, 1\}`-matrix ("rook placement"), or a single word, return
|
|
1325
|
+
the array `[(a_1, b_1), (a_2, b_2), \ldots, (a_n, b_n)]`.
|
|
1326
|
+
|
|
1327
|
+
INPUT:
|
|
1328
|
+
|
|
1329
|
+
- ``obj1``, ``obj2`` -- anything representing a strict biword
|
|
1330
|
+
(see the doc of :meth:`forward_rule` for the
|
|
1331
|
+
encodings accepted)
|
|
1332
|
+
|
|
1333
|
+
- ``check`` -- boolean (default: ``True``); whether to check
|
|
1334
|
+
that ``obj1`` and ``obj2`` actually define a valid
|
|
1335
|
+
strict biword
|
|
1336
|
+
|
|
1337
|
+
EXAMPLES::
|
|
1338
|
+
|
|
1339
|
+
sage: from sage.combinat.rsk import RuleDualRSK
|
|
1340
|
+
sage: list(RuleDualRSK().to_pairs([1, 2, 2, 2], [2, 1, 2, 3]))
|
|
1341
|
+
[(1, 2), (2, 1), (2, 2), (2, 3)]
|
|
1342
|
+
sage: RuleDualRSK().to_pairs([1, 2, 2, 2], [1, 2, 3, 3])
|
|
1343
|
+
Traceback (most recent call last):
|
|
1344
|
+
...
|
|
1345
|
+
ValueError: invalid strict biword
|
|
1346
|
+
sage: m = Matrix(ZZ, 3, 2, [0,1,1,1,0,1]) ; m
|
|
1347
|
+
[0 1]
|
|
1348
|
+
[1 1]
|
|
1349
|
+
[0 1]
|
|
1350
|
+
sage: list(RuleDualRSK().to_pairs(m))
|
|
1351
|
+
[(1, 2), (2, 1), (2, 2), (3, 2)]
|
|
1352
|
+
sage: m = Matrix(ZZ, 3, 2, [0,1,1,0,0,2]) ; m
|
|
1353
|
+
[0 1]
|
|
1354
|
+
[1 0]
|
|
1355
|
+
[0 2]
|
|
1356
|
+
sage: RuleDualRSK().to_pairs(m)
|
|
1357
|
+
Traceback (most recent call last):
|
|
1358
|
+
...
|
|
1359
|
+
ValueError: dual RSK requires a {0, 1}-matrix
|
|
1360
|
+
"""
|
|
1361
|
+
if obj2 is None:
|
|
1362
|
+
try:
|
|
1363
|
+
itr = obj1._rsk_iter()
|
|
1364
|
+
except AttributeError:
|
|
1365
|
+
# If this is (something which looks like) a matrix
|
|
1366
|
+
# then build the generalized permutation
|
|
1367
|
+
try:
|
|
1368
|
+
t = []
|
|
1369
|
+
b = []
|
|
1370
|
+
for i, row in enumerate(obj1):
|
|
1371
|
+
for j, mult in enumerate(row):
|
|
1372
|
+
if mult > 1:
|
|
1373
|
+
raise ValueError("dual RSK requires a {0, 1}-matrix")
|
|
1374
|
+
if mult > 0:
|
|
1375
|
+
t.append(i+1)
|
|
1376
|
+
b.append(j+1)
|
|
1377
|
+
itr = zip(t, b)
|
|
1378
|
+
except TypeError:
|
|
1379
|
+
itr = zip(range(1, len(obj1)+1), obj1)
|
|
1380
|
+
else:
|
|
1381
|
+
if check:
|
|
1382
|
+
if len(obj1) != len(obj2):
|
|
1383
|
+
raise ValueError("the two arrays must be the same length")
|
|
1384
|
+
# Check it is a generalized permutation (i.e., biword):
|
|
1385
|
+
# that is, the pairs of corresponding entries of obj1
|
|
1386
|
+
# and obj2 are weakly increasing in lexicographic order.
|
|
1387
|
+
lt = 0
|
|
1388
|
+
lb = 0
|
|
1389
|
+
for t, b in zip(obj1, obj2):
|
|
1390
|
+
if t < lt or (t == lt and b <= lb):
|
|
1391
|
+
raise ValueError("invalid strict biword")
|
|
1392
|
+
lt = t
|
|
1393
|
+
lb = b
|
|
1394
|
+
itr = zip(obj1, obj2)
|
|
1395
|
+
return itr
|
|
1396
|
+
|
|
1397
|
+
def insertion(self, j, r):
|
|
1398
|
+
r"""
|
|
1399
|
+
Insert the letter ``j`` from the second row of the biword
|
|
1400
|
+
into the row `r` using dual RSK insertion, if there is
|
|
1401
|
+
bumping to be done.
|
|
1402
|
+
|
|
1403
|
+
The row `r` is modified in place if bumping occurs. The bumped-out
|
|
1404
|
+
entry, if it exists, is returned.
|
|
1405
|
+
|
|
1406
|
+
EXAMPLES::
|
|
1407
|
+
|
|
1408
|
+
sage: from sage.combinat.rsk import RuleDualRSK
|
|
1409
|
+
sage: r = [1, 3, 4, 5]
|
|
1410
|
+
sage: j = RuleDualRSK().insertion(4, r); j
|
|
1411
|
+
4
|
|
1412
|
+
sage: r
|
|
1413
|
+
[1, 3, 4, 5]
|
|
1414
|
+
sage: r = [1, 2, 3, 6, 7]
|
|
1415
|
+
sage: j = RuleDualRSK().insertion(4, r); j
|
|
1416
|
+
6
|
|
1417
|
+
sage: r
|
|
1418
|
+
[1, 2, 3, 4, 7]
|
|
1419
|
+
sage: r = [1, 3]
|
|
1420
|
+
sage: j = RuleDualRSK().insertion(4, r); j is None
|
|
1421
|
+
True
|
|
1422
|
+
sage: r
|
|
1423
|
+
[1, 3]
|
|
1424
|
+
"""
|
|
1425
|
+
if r[-1] < j:
|
|
1426
|
+
return None # j needs to be added at the end of the row.
|
|
1427
|
+
# Figure out where to insert j into the row r. The
|
|
1428
|
+
# bisect command returns the position of the least
|
|
1429
|
+
# element of r greater or equal to j. We will call it y.
|
|
1430
|
+
y_pos = bisect_left(r, j)
|
|
1431
|
+
# Switch j and y
|
|
1432
|
+
j, r[y_pos] = r[y_pos], j
|
|
1433
|
+
return j
|
|
1434
|
+
|
|
1435
|
+
def reverse_insertion(self, x, row):
|
|
1436
|
+
r"""
|
|
1437
|
+
Reverse bump the row ``row`` of the current insertion tableau
|
|
1438
|
+
with the number ``x`` using dual RSK insertion.
|
|
1439
|
+
|
|
1440
|
+
The row ``row`` is modified in place. The bumped-out entry
|
|
1441
|
+
is returned.
|
|
1442
|
+
|
|
1443
|
+
EXAMPLES::
|
|
1444
|
+
|
|
1445
|
+
sage: from sage.combinat.rsk import RuleDualRSK
|
|
1446
|
+
sage: r = [1, 2, 4, 6, 7]
|
|
1447
|
+
sage: x = RuleDualRSK().reverse_insertion(6, r); r
|
|
1448
|
+
[1, 2, 4, 6, 7]
|
|
1449
|
+
sage: x
|
|
1450
|
+
6
|
|
1451
|
+
sage: r = [1, 2, 4, 5, 7]
|
|
1452
|
+
sage: x = RuleDualRSK().reverse_insertion(6, r); r
|
|
1453
|
+
[1, 2, 4, 6, 7]
|
|
1454
|
+
sage: x
|
|
1455
|
+
5
|
|
1456
|
+
"""
|
|
1457
|
+
y_pos = bisect_right(row, x) - 1
|
|
1458
|
+
# switch x and y
|
|
1459
|
+
x, row[y_pos] = row[y_pos], x
|
|
1460
|
+
return x
|
|
1461
|
+
|
|
1462
|
+
def _backward_format_output(self, lower_row, upper_row, output,
|
|
1463
|
+
p_is_standard, q_is_standard):
|
|
1464
|
+
r"""
|
|
1465
|
+
Return the final output of the ``RSK_inverse`` correspondence
|
|
1466
|
+
from the output of the corresponding ``backward_rule``.
|
|
1467
|
+
|
|
1468
|
+
.. NOTE::
|
|
1469
|
+
|
|
1470
|
+
The default implementation of ``backward_rule`` lists
|
|
1471
|
+
bumped-out entries in the order in which the reverse
|
|
1472
|
+
bumping happens, which is *opposite* to the order of the
|
|
1473
|
+
final output.
|
|
1474
|
+
|
|
1475
|
+
EXAMPLES::
|
|
1476
|
+
|
|
1477
|
+
sage: from sage.combinat.rsk import RuleDualRSK
|
|
1478
|
+
sage: RuleDualRSK()._backward_format_output([1, 2, 3, 4], None,
|
|
1479
|
+
....: 'permutation', True, True)
|
|
1480
|
+
[4, 3, 2, 1]
|
|
1481
|
+
sage: RuleDualRSK()._backward_format_output([1, 2, 3, 4], None,
|
|
1482
|
+
....: 'permutation', False, True)
|
|
1483
|
+
Traceback (most recent call last):
|
|
1484
|
+
...
|
|
1485
|
+
TypeError: p must be standard to have a valid permutation as output
|
|
1486
|
+
"""
|
|
1487
|
+
if q_is_standard and output == 'permutation':
|
|
1488
|
+
if not p_is_standard:
|
|
1489
|
+
raise TypeError("p must be standard to have a valid permutation as output")
|
|
1490
|
+
from sage.combinat.permutation import Permutation
|
|
1491
|
+
return Permutation(reversed(lower_row))
|
|
1492
|
+
else:
|
|
1493
|
+
return super()._backward_format_output(lower_row, upper_row, output,
|
|
1494
|
+
p_is_standard, q_is_standard)
|
|
1495
|
+
|
|
1496
|
+
def _forward_format_output(self, p, q, check_standard):
|
|
1497
|
+
r"""
|
|
1498
|
+
Return final output of the ``RSK`` (here, dual RSK)
|
|
1499
|
+
correspondence from the output of the corresponding
|
|
1500
|
+
``forward_rule``.
|
|
1501
|
+
|
|
1502
|
+
EXAMPLES::
|
|
1503
|
+
|
|
1504
|
+
sage: from sage.combinat.rsk import RuleDualRSK
|
|
1505
|
+
sage: isinstance(RuleDualRSK()._forward_format_output([[1,2,3,4,5]],
|
|
1506
|
+
....: [[1,2,3,4,5]], True)[0], StandardTableau)
|
|
1507
|
+
True
|
|
1508
|
+
sage: isinstance(RuleDualRSK()._forward_format_output([[1,2,3,4,5]],
|
|
1509
|
+
....: [[1,2,3,4,5]], False)[0], Tableau)
|
|
1510
|
+
True
|
|
1511
|
+
sage: isinstance(RuleDualRSK()._forward_format_output([[1,1,1,3,7]],
|
|
1512
|
+
....: [[1,2,3,4,5]], True)[0], Tableau)
|
|
1513
|
+
True
|
|
1514
|
+
"""
|
|
1515
|
+
from sage.combinat.tableau import Tableau, StandardTableau, SemistandardTableau
|
|
1516
|
+
|
|
1517
|
+
if len(p) == 0:
|
|
1518
|
+
return [StandardTableau([]), StandardTableau([])]
|
|
1519
|
+
|
|
1520
|
+
if check_standard:
|
|
1521
|
+
try:
|
|
1522
|
+
P = StandardTableau(p)
|
|
1523
|
+
except ValueError:
|
|
1524
|
+
P = Tableau(p)
|
|
1525
|
+
try:
|
|
1526
|
+
Q = StandardTableau(q)
|
|
1527
|
+
except ValueError:
|
|
1528
|
+
Q = SemistandardTableau(q)
|
|
1529
|
+
return [P, Q]
|
|
1530
|
+
return [Tableau(p), SemistandardTableau(q)]
|
|
1531
|
+
|
|
1532
|
+
|
|
1533
|
+
class RuleCoRSK(RuleRSK):
|
|
1534
|
+
r"""
|
|
1535
|
+
Rule for coRSK insertion.
|
|
1536
|
+
|
|
1537
|
+
CoRSK insertion differs from classical RSK insertion in the
|
|
1538
|
+
following ways:
|
|
1539
|
+
|
|
1540
|
+
* The input (in terms of biwords) is no longer a biword,
|
|
1541
|
+
but rather a strict cobiword -- i.e., a pair of two lists
|
|
1542
|
+
`[a_1, a_2, \ldots, a_n]` and `[b_1, b_2, \ldots, b_n]` that
|
|
1543
|
+
satisfy the strict inequalities
|
|
1544
|
+
`(a_1, b_1) \widetilde{<} (a_2, b_2) \widetilde{<} \cdots
|
|
1545
|
+
\widetilde{<} (a_n, b_n)`, where
|
|
1546
|
+
the binary relation `\widetilde{<}` on pairs of integers
|
|
1547
|
+
is defined by having `(u_1, v_1) \widetilde{<} (u_2, v_2)`
|
|
1548
|
+
if and only if either `u_1 < u_2` or (`u_1 = u_2` and
|
|
1549
|
+
`v_1 > v_2`).
|
|
1550
|
+
In terms of matrices, this means that the input is not an
|
|
1551
|
+
arbitrary matrix with nonnegative integer entries, but rather
|
|
1552
|
+
a `\{0, 1\}`-matrix (i.e., a matrix whose entries are `0`'s
|
|
1553
|
+
and `1`'s).
|
|
1554
|
+
|
|
1555
|
+
* The output still consists of two tableaux `(P, Q)` of equal
|
|
1556
|
+
shapes, but rather than both of them being semistandard, now
|
|
1557
|
+
`Q` is row-strict (i.e., its transpose is semistandard) while
|
|
1558
|
+
`P` is semistandard.
|
|
1559
|
+
|
|
1560
|
+
Bumping proceeds in the same way as for RSK insertion.
|
|
1561
|
+
|
|
1562
|
+
The RSK and coRSK algorithms agree for permutation matrices.
|
|
1563
|
+
|
|
1564
|
+
For more information, see Section A.4 in [Ful1997]_ (specifically,
|
|
1565
|
+
construction (1d)) or the second solution to Exercise 2.7.12(a) in
|
|
1566
|
+
[GR2018v5sol]_.
|
|
1567
|
+
|
|
1568
|
+
EXAMPLES::
|
|
1569
|
+
|
|
1570
|
+
sage: RSK([1,2,5,3,1], insertion = RSK.rules.coRSK)
|
|
1571
|
+
[[[1, 1, 3], [2], [5]], [[1, 2, 3], [4], [5]]]
|
|
1572
|
+
sage: RSK(Word([2,3,3,2,1,3,2,3]), insertion = RSK.rules.coRSK)
|
|
1573
|
+
[[[1, 2, 2, 3, 3], [2, 3], [3]], [[1, 2, 3, 6, 8], [4, 7], [5]]]
|
|
1574
|
+
sage: RSK(Word([3,3,2,4,1]), insertion = RSK.rules.coRSK)
|
|
1575
|
+
[[[1, 3, 4], [2], [3]], [[1, 2, 4], [3], [5]]]
|
|
1576
|
+
sage: from sage.combinat.rsk import to_matrix
|
|
1577
|
+
sage: RSK(to_matrix([1, 1, 3, 3, 4], [3, 2, 2, 1, 3]), insertion = RSK.rules.coRSK)
|
|
1578
|
+
[[[1, 2, 3], [2], [3]], [[1, 3, 4], [1], [3]]]
|
|
1579
|
+
|
|
1580
|
+
Using coRSK insertion with a `\{0, 1\}`-matrix::
|
|
1581
|
+
|
|
1582
|
+
sage: RSK(matrix([[0,1],[1,0]]), insertion = RSK.rules.coRSK)
|
|
1583
|
+
[[[1], [2]], [[1], [2]]]
|
|
1584
|
+
|
|
1585
|
+
We can also give it something looking like a matrix::
|
|
1586
|
+
|
|
1587
|
+
sage: RSK([[0,1],[1,0]], insertion = RSK.rules.coRSK)
|
|
1588
|
+
[[[1], [2]], [[1], [2]]]
|
|
1589
|
+
|
|
1590
|
+
We can also use the inverse correspondence::
|
|
1591
|
+
|
|
1592
|
+
sage: RSK_inverse(*RSK([1, 2, 2, 2], [2, 3, 2, 1],
|
|
1593
|
+
....: insertion=RSK.rules.coRSK),insertion=RSK.rules.coRSK)
|
|
1594
|
+
[[1, 2, 2, 2], [2, 3, 2, 1]]
|
|
1595
|
+
sage: P,Q = RSK([1, 2, 2, 2], [2, 3, 2, 1],insertion=RSK.rules.coRSK)
|
|
1596
|
+
sage: RSK_inverse(P, Q, insertion=RSK.rules.coRSK)
|
|
1597
|
+
[[1, 2, 2, 2], [2, 3, 2, 1]]
|
|
1598
|
+
|
|
1599
|
+
When applied to two standard tableaux, backwards coRSK
|
|
1600
|
+
insertion behaves identically to the usual backwards RSK
|
|
1601
|
+
insertion::
|
|
1602
|
+
|
|
1603
|
+
sage: t1 = Tableau([[1, 2, 5], [3], [4]])
|
|
1604
|
+
sage: t2 = Tableau([[1, 2, 3], [4], [5]])
|
|
1605
|
+
sage: RSK_inverse(t1, t2, insertion=RSK.rules.coRSK)
|
|
1606
|
+
[[1, 2, 3, 4, 5], [1, 4, 5, 3, 2]]
|
|
1607
|
+
sage: RSK_inverse(t1, t2, 'word', insertion=RSK.rules.coRSK)
|
|
1608
|
+
word: 14532
|
|
1609
|
+
sage: RSK_inverse(t1, t2, 'matrix', insertion=RSK.rules.coRSK)
|
|
1610
|
+
[1 0 0 0 0]
|
|
1611
|
+
[0 0 0 1 0]
|
|
1612
|
+
[0 0 0 0 1]
|
|
1613
|
+
[0 0 1 0 0]
|
|
1614
|
+
[0 1 0 0 0]
|
|
1615
|
+
sage: RSK_inverse(t1, t2, 'permutation', insertion=RSK.rules.coRSK)
|
|
1616
|
+
[1, 4, 5, 3, 2]
|
|
1617
|
+
sage: RSK_inverse(t1, t1, 'permutation', insertion=RSK.rules.coRSK)
|
|
1618
|
+
[1, 4, 3, 2, 5]
|
|
1619
|
+
sage: RSK_inverse(t2, t2, 'permutation', insertion=RSK.rules.coRSK)
|
|
1620
|
+
[1, 2, 5, 4, 3]
|
|
1621
|
+
sage: RSK_inverse(t2, t1, 'permutation', insertion=RSK.rules.coRSK)
|
|
1622
|
+
[1, 5, 4, 2, 3]
|
|
1623
|
+
|
|
1624
|
+
For coRSK, the first tableau is semistandard while the second tableau
|
|
1625
|
+
is transpose semistandard::
|
|
1626
|
+
|
|
1627
|
+
sage: p = Tableau([[1,2,2],[5]]); q = Tableau([[1,2,4],[3]])
|
|
1628
|
+
sage: ret = RSK_inverse(p, q, insertion=RSK.rules.coRSK); ret
|
|
1629
|
+
[[1, 2, 3, 4], [1, 5, 2, 2]]
|
|
1630
|
+
sage: RSK_inverse(p, q, 'word', insertion=RSK.rules.coRSK)
|
|
1631
|
+
word: 1522
|
|
1632
|
+
|
|
1633
|
+
TESTS:
|
|
1634
|
+
|
|
1635
|
+
Empty objects::
|
|
1636
|
+
|
|
1637
|
+
sage: RSK(Permutation([]), insertion=RSK.rules.coRSK)
|
|
1638
|
+
[[], []]
|
|
1639
|
+
sage: RSK(Word([]), insertion=RSK.rules.coRSK)
|
|
1640
|
+
[[], []]
|
|
1641
|
+
sage: RSK(matrix([[]]), insertion=RSK.rules.coRSK)
|
|
1642
|
+
[[], []]
|
|
1643
|
+
sage: RSK([], [], insertion=RSK.rules.coRSK)
|
|
1644
|
+
[[], []]
|
|
1645
|
+
sage: RSK([[]], insertion=RSK.rules.coRSK)
|
|
1646
|
+
[[], []]
|
|
1647
|
+
|
|
1648
|
+
Check that :func:`RSK_inverse` is the inverse of :func:`RSK` on the
|
|
1649
|
+
different types of inputs/outputs::
|
|
1650
|
+
|
|
1651
|
+
sage: RSK_inverse(Tableau([]), Tableau([]),
|
|
1652
|
+
....: insertion=RSK.rules.coRSK)
|
|
1653
|
+
[[], []]
|
|
1654
|
+
sage: f = lambda p: RSK_inverse(*RSK(p, insertion=RSK.rules.coRSK),
|
|
1655
|
+
....: output='permutation', insertion=RSK.rules.coRSK)
|
|
1656
|
+
sage: all(p == f(p) for n in range(7) for p in Permutations(n))
|
|
1657
|
+
True
|
|
1658
|
+
sage: all(RSK_inverse(*RSK(w, insertion=RSK.rules.coRSK),
|
|
1659
|
+
....: output='word', insertion=RSK.rules.coRSK) == w
|
|
1660
|
+
....: for n in range(4) for w in Words(5, n))
|
|
1661
|
+
True
|
|
1662
|
+
sage: from sage.combinat.integer_matrices import IntegerMatrices
|
|
1663
|
+
sage: M = IntegerMatrices([1,2,2,1], [3,1,1,1])
|
|
1664
|
+
sage: all(RSK_inverse(*RSK(m, insertion=RSK.rules.coRSK),
|
|
1665
|
+
....: output='matrix', insertion=RSK.rules.coRSK) == m
|
|
1666
|
+
....: for m in M if all(x in [0, 1] for x in m))
|
|
1667
|
+
True
|
|
1668
|
+
|
|
1669
|
+
sage: n = ZZ.random_element(200)
|
|
1670
|
+
sage: p = Permutations(n).random_element()
|
|
1671
|
+
sage: True if p == f(p) else p
|
|
1672
|
+
True
|
|
1673
|
+
|
|
1674
|
+
Checking that the tableaux should be of same shape::
|
|
1675
|
+
|
|
1676
|
+
sage: RSK_inverse(Tableau([[1,2,3]]), Tableau([[1,2]]),
|
|
1677
|
+
....: insertion=RSK.rules.dualRSK)
|
|
1678
|
+
Traceback (most recent call last):
|
|
1679
|
+
...
|
|
1680
|
+
ValueError: p(=[[1, 2, 3]]) and q(=[[1, 2]]) must have the same shape
|
|
1681
|
+
|
|
1682
|
+
Checking that the biword is a strict cobiword::
|
|
1683
|
+
|
|
1684
|
+
sage: RSK([1,2,4,3], [1,2,3,4], insertion=RSK.rules.coRSK)
|
|
1685
|
+
Traceback (most recent call last):
|
|
1686
|
+
...
|
|
1687
|
+
ValueError: invalid strict cobiword
|
|
1688
|
+
sage: RSK([1,2,3,3], [1,2,3,4], insertion=RSK.rules.coRSK)
|
|
1689
|
+
Traceback (most recent call last):
|
|
1690
|
+
...
|
|
1691
|
+
ValueError: invalid strict cobiword
|
|
1692
|
+
sage: RSK([1,2,3,3], [1,2,3,3], insertion=RSK.rules.coRSK)
|
|
1693
|
+
Traceback (most recent call last):
|
|
1694
|
+
...
|
|
1695
|
+
ValueError: invalid strict cobiword
|
|
1696
|
+
"""
|
|
1697
|
+
|
|
1698
|
+
def to_pairs(self, obj1=None, obj2=None, check=True):
|
|
1699
|
+
r"""
|
|
1700
|
+
Given a valid input for the coRSK algorithm, such as
|
|
1701
|
+
two `n`-tuples ``obj1`` `= [a_1, a_2, \ldots, a_n]`
|
|
1702
|
+
and ``obj2`` `= [b_1, b_2, \ldots, b_n]` forming a
|
|
1703
|
+
strict cobiword (i.e., satisfying
|
|
1704
|
+
`a_1 \leq a_2 \leq \cdots \leq a_n`, and if
|
|
1705
|
+
`a_i = a_{i+1}`, then `b_i > b_{i+1}`),
|
|
1706
|
+
or a `\{0, 1\}`-matrix ("rook placement"), or a
|
|
1707
|
+
single word, return the array
|
|
1708
|
+
`[(a_1, b_1), (a_2, b_2), \ldots, (a_n, b_n)]`.
|
|
1709
|
+
|
|
1710
|
+
INPUT:
|
|
1711
|
+
|
|
1712
|
+
- ``obj1``, ``obj2`` -- anything representing a strict
|
|
1713
|
+
cobiword (see the doc of :meth:`forward_rule` for
|
|
1714
|
+
the encodings accepted)
|
|
1715
|
+
|
|
1716
|
+
- ``check`` -- boolean (default: ``True``); whether to check
|
|
1717
|
+
that ``obj1`` and ``obj2`` actually define a valid
|
|
1718
|
+
strict cobiword
|
|
1719
|
+
|
|
1720
|
+
EXAMPLES::
|
|
1721
|
+
|
|
1722
|
+
sage: from sage.combinat.rsk import RuleCoRSK
|
|
1723
|
+
sage: list(RuleCoRSK().to_pairs([1, 2, 2, 2], [2, 3, 2, 1]))
|
|
1724
|
+
[(1, 2), (2, 3), (2, 2), (2, 1)]
|
|
1725
|
+
sage: RuleCoRSK().to_pairs([1, 2, 2, 2], [1, 2, 3, 3])
|
|
1726
|
+
Traceback (most recent call last):
|
|
1727
|
+
...
|
|
1728
|
+
ValueError: invalid strict cobiword
|
|
1729
|
+
sage: m = Matrix(ZZ, 3, 2, [0,1,1,1,0,1]) ; m
|
|
1730
|
+
[0 1]
|
|
1731
|
+
[1 1]
|
|
1732
|
+
[0 1]
|
|
1733
|
+
sage: list(RuleCoRSK().to_pairs(m))
|
|
1734
|
+
[(1, 2), (2, 2), (2, 1), (3, 2)]
|
|
1735
|
+
sage: m = Matrix(ZZ, 3, 2, [0,1,1,0,0,2]) ; m
|
|
1736
|
+
[0 1]
|
|
1737
|
+
[1 0]
|
|
1738
|
+
[0 2]
|
|
1739
|
+
sage: RuleCoRSK().to_pairs(m)
|
|
1740
|
+
Traceback (most recent call last):
|
|
1741
|
+
...
|
|
1742
|
+
ValueError: coRSK requires a {0, 1}-matrix
|
|
1743
|
+
"""
|
|
1744
|
+
if obj2 is None:
|
|
1745
|
+
try:
|
|
1746
|
+
itr = obj1._rsk_iter()
|
|
1747
|
+
except AttributeError:
|
|
1748
|
+
# If this is (something which looks like) a matrix
|
|
1749
|
+
# then build the generalized permutation
|
|
1750
|
+
try:
|
|
1751
|
+
t = []
|
|
1752
|
+
b = []
|
|
1753
|
+
for i, row in enumerate(obj1):
|
|
1754
|
+
for j, mult in reversed(list(enumerate(row))):
|
|
1755
|
+
if mult > 1:
|
|
1756
|
+
raise ValueError("coRSK requires a {0, 1}-matrix")
|
|
1757
|
+
if mult > 0:
|
|
1758
|
+
t.append(i+1)
|
|
1759
|
+
b.append(j+1)
|
|
1760
|
+
itr = zip(t, b)
|
|
1761
|
+
except TypeError:
|
|
1762
|
+
itr = zip(range(1, len(obj1)+1), obj1)
|
|
1763
|
+
else:
|
|
1764
|
+
if check:
|
|
1765
|
+
if len(obj1) != len(obj2):
|
|
1766
|
+
raise ValueError("the two arrays must be the same length")
|
|
1767
|
+
# Check it is a generalized permutation (i.e., biword):
|
|
1768
|
+
# that is, the pairs of corresponding entries of obj1
|
|
1769
|
+
# and obj2 are weakly increasing in lexicographic order.
|
|
1770
|
+
lt = 0
|
|
1771
|
+
lb = 0
|
|
1772
|
+
for t, b in zip(obj1, obj2):
|
|
1773
|
+
if t < lt or (t == lt and b >= lb):
|
|
1774
|
+
raise ValueError("invalid strict cobiword")
|
|
1775
|
+
lt = t
|
|
1776
|
+
lb = b
|
|
1777
|
+
itr = zip(obj1, obj2)
|
|
1778
|
+
return itr
|
|
1779
|
+
|
|
1780
|
+
def _forward_format_output(self, p, q, check_standard):
|
|
1781
|
+
r"""
|
|
1782
|
+
Return final output of the ``RSK`` correspondence from the
|
|
1783
|
+
output of the corresponding ``forward_rule``.
|
|
1784
|
+
|
|
1785
|
+
EXAMPLES::
|
|
1786
|
+
|
|
1787
|
+
sage: from sage.combinat.rsk import RuleCoRSK
|
|
1788
|
+
sage: isinstance(RuleCoRSK()._forward_format_output([[1,2,3,4,5]],
|
|
1789
|
+
....: [[1,2,3,4,5]], True)[0], StandardTableau)
|
|
1790
|
+
True
|
|
1791
|
+
sage: isinstance(RuleCoRSK()._forward_format_output([[1,2,3,4,5]],
|
|
1792
|
+
....: [[1,2,3,4,5]], False)[0], SemistandardTableau)
|
|
1793
|
+
True
|
|
1794
|
+
sage: isinstance(RuleCoRSK()._forward_format_output([[1,1,1,3,7]],
|
|
1795
|
+
....: [[1,2,3,4,5]], True)[1], Tableau)
|
|
1796
|
+
True
|
|
1797
|
+
sage: isinstance(RuleCoRSK()._forward_format_output([[1,1,1,3,7]],
|
|
1798
|
+
....: [[1,2,3,4,5]], False)[1], Tableau)
|
|
1799
|
+
True
|
|
1800
|
+
"""
|
|
1801
|
+
from sage.combinat.tableau import SemistandardTableau, StandardTableau, Tableau
|
|
1802
|
+
|
|
1803
|
+
if check_standard:
|
|
1804
|
+
try:
|
|
1805
|
+
P = StandardTableau(p)
|
|
1806
|
+
except ValueError:
|
|
1807
|
+
P = SemistandardTableau(p)
|
|
1808
|
+
try:
|
|
1809
|
+
Q = StandardTableau(q)
|
|
1810
|
+
except ValueError:
|
|
1811
|
+
Q = Tableau(q)
|
|
1812
|
+
return [P, Q]
|
|
1813
|
+
return [SemistandardTableau(p), Tableau(q)]
|
|
1814
|
+
|
|
1815
|
+
def backward_rule(self, p, q, output):
|
|
1816
|
+
r"""
|
|
1817
|
+
Return the strict cobiword obtained by applying reverse
|
|
1818
|
+
coRSK insertion to a pair of tableaux ``(p, q)``.
|
|
1819
|
+
|
|
1820
|
+
INPUT:
|
|
1821
|
+
|
|
1822
|
+
- ``p``, ``q`` -- two tableaux of the same shape
|
|
1823
|
+
|
|
1824
|
+
- ``output`` -- (default: ``'array'``) if ``q`` is row-strict:
|
|
1825
|
+
|
|
1826
|
+
- ``'array'`` -- as a two-line array (i.e. strict cobiword)
|
|
1827
|
+
- ``'matrix'`` -- as a `\{0, 1\}`-matrix
|
|
1828
|
+
|
|
1829
|
+
and if ``q`` is standard, we can have the output:
|
|
1830
|
+
|
|
1831
|
+
- ``'word'`` -- as a word
|
|
1832
|
+
|
|
1833
|
+
and additionally if ``p`` is standard, we can also have the output:
|
|
1834
|
+
|
|
1835
|
+
- ``'permutation'`` -- as a permutation
|
|
1836
|
+
|
|
1837
|
+
EXAMPLES::
|
|
1838
|
+
|
|
1839
|
+
sage: from sage.combinat.rsk import RuleCoRSK
|
|
1840
|
+
sage: t1 = Tableau([[1, 1, 2], [2, 3], [4]])
|
|
1841
|
+
sage: t2 = Tableau([[1, 4, 5], [1, 4], [2]])
|
|
1842
|
+
sage: RuleCoRSK().backward_rule(t1, t2, 'array')
|
|
1843
|
+
[[1, 1, 2, 4, 4, 5], [4, 2, 1, 3, 1, 2]]
|
|
1844
|
+
"""
|
|
1845
|
+
# Make a copy of p since this is destructive to it
|
|
1846
|
+
p_copy = [list(row) for row in p]
|
|
1847
|
+
|
|
1848
|
+
if q.is_standard():
|
|
1849
|
+
rev_word = [] # This will be our word in reverse
|
|
1850
|
+
d = {qij: i for i, Li in enumerate(q) for qij in Li}
|
|
1851
|
+
# d is now a dictionary which assigns to each integer k the
|
|
1852
|
+
# number of the row of q containing k.
|
|
1853
|
+
|
|
1854
|
+
for key in sorted(d, reverse=True):
|
|
1855
|
+
# Delete last entry from i-th row of p_copy
|
|
1856
|
+
i = d[key]
|
|
1857
|
+
x = p_copy[i].pop() # Always the right-most entry
|
|
1858
|
+
for row in reversed(p_copy[:i]):
|
|
1859
|
+
x = self.reverse_insertion(x, row)
|
|
1860
|
+
rev_word.append(x)
|
|
1861
|
+
return self._backward_format_output(rev_word, None, output, p.is_standard(), True)
|
|
1862
|
+
|
|
1863
|
+
upper_row = []
|
|
1864
|
+
lower_row = []
|
|
1865
|
+
# upper_row and lower_row will be the upper and lower rows of the
|
|
1866
|
+
# strict cobiword we get as a result, but both reversed.
|
|
1867
|
+
d = {}
|
|
1868
|
+
for row, Li in enumerate(q):
|
|
1869
|
+
for val in Li:
|
|
1870
|
+
if val in d:
|
|
1871
|
+
d[val].append(row)
|
|
1872
|
+
else:
|
|
1873
|
+
d[val] = [row]
|
|
1874
|
+
# d is now a dictionary which assigns to each integer k the
|
|
1875
|
+
# list of the rows of q containing k.
|
|
1876
|
+
for value, row_list in sorted(d.items(), reverse=True, key=lambda x: x[0]):
|
|
1877
|
+
for i in sorted(row_list, reverse=True):
|
|
1878
|
+
x = p_copy[i].pop() # Always the right-most entry
|
|
1879
|
+
for row in reversed(p_copy[:i]):
|
|
1880
|
+
x = self.reverse_insertion(x, row)
|
|
1881
|
+
lower_row.append(x)
|
|
1882
|
+
upper_row.append(value)
|
|
1883
|
+
return self._backward_format_output(lower_row, upper_row, output, p.is_standard(), False)
|
|
1884
|
+
|
|
1885
|
+
|
|
1886
|
+
class RuleSuperRSK(RuleRSK):
|
|
1887
|
+
r"""
|
|
1888
|
+
Rule for super RSK insertion.
|
|
1889
|
+
|
|
1890
|
+
Super RSK is based on `\epsilon`-insertion, a combination of
|
|
1891
|
+
row and column classical RSK insertion.
|
|
1892
|
+
|
|
1893
|
+
Super RSK insertion differs from the classical RSK insertion in the
|
|
1894
|
+
following ways:
|
|
1895
|
+
|
|
1896
|
+
* The input (in terms of biwords) is no longer an arbitrary biword,
|
|
1897
|
+
but rather a restricted super biword (i.e., a pair of two lists
|
|
1898
|
+
`[a_1, a_2, \ldots, a_n]` and `[b_1, b_2, \ldots, b_n]` that
|
|
1899
|
+
contains entries with even and odd parity and pairs with mixed
|
|
1900
|
+
parity entries do not repeat).
|
|
1901
|
+
|
|
1902
|
+
* The output still consists of two tableaux `(P, Q)` of equal
|
|
1903
|
+
shapes, but rather than both of them being semistandard, now
|
|
1904
|
+
they are semistandard super tableaux.
|
|
1905
|
+
|
|
1906
|
+
* The main difference is in the way bumping works. Instead of having
|
|
1907
|
+
only row bumping super RSK uses `\epsilon`-insertion, a combination
|
|
1908
|
+
of classical RSK bumping along the rows and a dual RSK like bumping
|
|
1909
|
+
(i.e. when a number `k_i` is inserted into the `i`-th row of `P`, it
|
|
1910
|
+
bumps out the first integer greater **or equal to** `k_i` in the column)
|
|
1911
|
+
along the column.
|
|
1912
|
+
|
|
1913
|
+
EXAMPLES::
|
|
1914
|
+
|
|
1915
|
+
sage: RSK([1], [1], insertion='superRSK')
|
|
1916
|
+
[[[1]], [[1]]]
|
|
1917
|
+
sage: RSK([1, 2], [1, 3], insertion='superRSK')
|
|
1918
|
+
[[[1, 3]], [[1, 2]]]
|
|
1919
|
+
sage: RSK([1, 2, 3], [1, 3, "3p"], insertion='superRSK')
|
|
1920
|
+
[[[1, 3], [3']], [[1, 2], [3]]]
|
|
1921
|
+
sage: RSK([1, 3, "3p", "2p"], insertion='superRSK')
|
|
1922
|
+
[[[1, 3', 3], [2']], [[1', 1, 2'], [2]]]
|
|
1923
|
+
sage: RSK(["1p", "2p", 2, 2, "3p", "3p", 3, 3],
|
|
1924
|
+
....: ["1p", 1, "2p", 2, "3p", "3p", "3p", 3], insertion='superRSK')
|
|
1925
|
+
[[[1', 2, 3', 3], [1, 3'], [2'], [3']], [[1', 2, 3', 3], [2', 3'], [2], [3]]]
|
|
1926
|
+
sage: P = SemistandardSuperTableau([[1, '3p', 3], ['2p']])
|
|
1927
|
+
sage: Q = SemistandardSuperTableau([['1p', 1, '2p'], [2]])
|
|
1928
|
+
sage: RSK_inverse(P, Q, insertion=RSK.rules.superRSK)
|
|
1929
|
+
[[1', 1, 2', 2], [1, 3, 3', 2']]
|
|
1930
|
+
|
|
1931
|
+
We apply super RSK on Example 5.1 in [Muth2019]_::
|
|
1932
|
+
|
|
1933
|
+
sage: P,Q = RSK(["1p", "2p", 2, 2, "3p", "3p", 3, 3],
|
|
1934
|
+
....: ["3p", 1, 2, 3, "3p", "3p", "2p", "1p"], insertion='superRSK')
|
|
1935
|
+
sage: (P, Q)
|
|
1936
|
+
([[1', 2', 3', 3], [1, 2, 3'], [3']], [[1', 2, 2, 3'], [2', 3, 3], [3']])
|
|
1937
|
+
sage: ascii_art((P, Q))
|
|
1938
|
+
( 1' 2' 3' 3 1' 2 2 3' )
|
|
1939
|
+
( 1 2 3' 2' 3 3 )
|
|
1940
|
+
( 3' , 3' )
|
|
1941
|
+
sage: RSK_inverse(P, Q, insertion=RSK.rules.superRSK)
|
|
1942
|
+
[[1', 2', 2, 2, 3', 3', 3, 3], [3', 1, 2, 3, 3', 3', 2', 1']]
|
|
1943
|
+
|
|
1944
|
+
Example 6.1 in [Muth2019]_::
|
|
1945
|
+
|
|
1946
|
+
sage: P,Q = RSK(["1p", "2p", 2, 2, "3p", "3p", 3, 3],
|
|
1947
|
+
....: ["3p", 1, 2, 3, "3p", "3p", "2p", "1p"], insertion='superRSK')
|
|
1948
|
+
sage: ascii_art((P, Q))
|
|
1949
|
+
( 1' 2' 3' 3 1' 2 2 3' )
|
|
1950
|
+
( 1 2 3' 2' 3 3 )
|
|
1951
|
+
( 3' , 3' )
|
|
1952
|
+
sage: RSK_inverse(P, Q, insertion=RSK.rules.superRSK)
|
|
1953
|
+
[[1', 2', 2, 2, 3', 3', 3, 3], [3', 1, 2, 3, 3', 3', 2', 1']]
|
|
1954
|
+
|
|
1955
|
+
sage: P,Q = RSK(["1p", 1, "2p", 2, "3p", "3p", "3p", 3],
|
|
1956
|
+
....: [3, "2p", 3, 2, "3p", "3p", "1p", 2], insertion='superRSK')
|
|
1957
|
+
sage: ascii_art((P, Q))
|
|
1958
|
+
( 1' 2 2 3' 1' 2' 3' 3 )
|
|
1959
|
+
( 2' 3 3 1 2 3' )
|
|
1960
|
+
( 3' , 3' )
|
|
1961
|
+
sage: RSK_inverse(P, Q, insertion=RSK.rules.superRSK)
|
|
1962
|
+
[[1', 1, 2', 2, 3', 3', 3', 3], [3, 2', 3, 2, 3', 3', 1', 2]]
|
|
1963
|
+
|
|
1964
|
+
Let us now call the inverse correspondence::
|
|
1965
|
+
|
|
1966
|
+
sage: P, Q = RSK([1, 2, 2, 2], [2, 1, 2, 3],
|
|
1967
|
+
....: insertion=RSK.rules.superRSK)
|
|
1968
|
+
sage: RSK_inverse(P, Q, insertion=RSK.rules.superRSK)
|
|
1969
|
+
[[1, 2, 2, 2], [2, 1, 2, 3]]
|
|
1970
|
+
|
|
1971
|
+
When applied to two tableaux with only even parity elements, reverse super
|
|
1972
|
+
RSK insertion behaves identically to the usual reversel RSK insertion::
|
|
1973
|
+
|
|
1974
|
+
sage: t1 = Tableau([[1, 2, 5], [3], [4]])
|
|
1975
|
+
sage: t2 = Tableau([[1, 2, 3], [4], [5]])
|
|
1976
|
+
sage: RSK_inverse(t1, t2, insertion=RSK.rules.RSK)
|
|
1977
|
+
[[1, 2, 3, 4, 5], [1, 4, 5, 3, 2]]
|
|
1978
|
+
sage: t1 = SemistandardSuperTableau([[1, 2, 5], [3], [4]])
|
|
1979
|
+
sage: t2 = SemistandardSuperTableau([[1, 2, 3], [4], [5]])
|
|
1980
|
+
sage: RSK_inverse(t1, t2, insertion=RSK.rules.superRSK)
|
|
1981
|
+
[[1, 2, 3, 4, 5], [1, 4, 5, 3, 2]]
|
|
1982
|
+
|
|
1983
|
+
TESTS:
|
|
1984
|
+
|
|
1985
|
+
Empty objects::
|
|
1986
|
+
|
|
1987
|
+
sage: RSK(Word([]), insertion=RSK.rules.superRSK)
|
|
1988
|
+
[[], []]
|
|
1989
|
+
sage: RSK([], [], insertion=RSK.rules.superRSK)
|
|
1990
|
+
[[], []]
|
|
1991
|
+
|
|
1992
|
+
Check that :func:`RSK_inverse` is the inverse of :func:`RSK` on the
|
|
1993
|
+
different types of inputs/outputs::
|
|
1994
|
+
|
|
1995
|
+
sage: from sage.combinat.shifted_primed_tableau import PrimedEntry
|
|
1996
|
+
sage: RSK_inverse(SemistandardSuperTableau([]),
|
|
1997
|
+
....: SemistandardSuperTableau([]), insertion=RSK.rules.superRSK)
|
|
1998
|
+
[[], []]
|
|
1999
|
+
sage: f = lambda p: RSK_inverse(*RSK(p, insertion=RSK.rules.superRSK),
|
|
2000
|
+
....: insertion=RSK.rules.superRSK)
|
|
2001
|
+
sage: all(p == f(p)[1] for n in range(5) for p in Permutations(n))
|
|
2002
|
+
True
|
|
2003
|
+
|
|
2004
|
+
sage: SST = StandardSuperTableaux([3,2,1])
|
|
2005
|
+
sage: f = lambda P, Q: RSK(*RSK_inverse(P, Q, insertion=RSK.rules.superRSK),
|
|
2006
|
+
....: insertion=RSK.rules.superRSK)
|
|
2007
|
+
sage: all([P, Q] == f(P, Q) for n in range(7) for la in Partitions(n)
|
|
2008
|
+
....: for P in StandardSuperTableaux(la) for Q in StandardSuperTableaux(la))
|
|
2009
|
+
True
|
|
2010
|
+
|
|
2011
|
+
Checking that tableaux should be of same shape::
|
|
2012
|
+
|
|
2013
|
+
sage: RSK_inverse(SemistandardSuperTableau([[1, 2, 3]]),
|
|
2014
|
+
....: SemistandardSuperTableau([[1, 2]]),
|
|
2015
|
+
....: insertion=RSK.rules.superRSK)
|
|
2016
|
+
Traceback (most recent call last):
|
|
2017
|
+
...
|
|
2018
|
+
ValueError: p(=[[1, 2, 3]]) and q(=[[1, 2]]) must have the same shape
|
|
2019
|
+
"""
|
|
2020
|
+
|
|
2021
|
+
def to_pairs(self, obj1=None, obj2=None, check=True):
|
|
2022
|
+
r"""
|
|
2023
|
+
Given a valid input for the super RSK algorithm, such as
|
|
2024
|
+
two `n`-tuples ``obj1`` `= [a_1, a_2, \ldots, a_n]`
|
|
2025
|
+
and ``obj2`` `= [b_1, b_2, \ldots, b_n]` forming a restricted
|
|
2026
|
+
super biword (i.e., entries with even and odd parity and no
|
|
2027
|
+
repetition of corresponding pairs with mixed parity entries)
|
|
2028
|
+
return the array `[(a_1, b_1), (a_2, b_2), \ldots, (a_n, b_n)]`.
|
|
2029
|
+
|
|
2030
|
+
INPUT:
|
|
2031
|
+
|
|
2032
|
+
- ``obj1``, ``obj2`` -- anything representing a restricted super biword
|
|
2033
|
+
(see the doc of :meth:`forward_rule` for the
|
|
2034
|
+
encodings accepted)
|
|
2035
|
+
|
|
2036
|
+
- ``check`` -- boolean (default: ``True``); whether to check
|
|
2037
|
+
that ``obj1`` and ``obj2`` actually define a valid
|
|
2038
|
+
restricted super biword
|
|
2039
|
+
|
|
2040
|
+
EXAMPLES::
|
|
2041
|
+
|
|
2042
|
+
sage: from sage.combinat.rsk import RuleSuperRSK
|
|
2043
|
+
sage: list(RuleSuperRSK().to_pairs([2, '1p', 1],[1, 1, '1p']))
|
|
2044
|
+
[(2, 1), (1', 1), (1, 1')]
|
|
2045
|
+
sage: list(RuleSuperRSK().to_pairs([1, '1p', '2p']))
|
|
2046
|
+
[(1', 1), (1, 1'), (2', 2')]
|
|
2047
|
+
sage: list(RuleSuperRSK().to_pairs([1, 1], ['1p', '1p']))
|
|
2048
|
+
Traceback (most recent call last):
|
|
2049
|
+
...
|
|
2050
|
+
ValueError: invalid restricted superbiword
|
|
2051
|
+
"""
|
|
2052
|
+
from sage.combinat.shifted_primed_tableau import PrimedEntry
|
|
2053
|
+
# Initializing itr for itr = None case
|
|
2054
|
+
itr = None
|
|
2055
|
+
if obj2 is None:
|
|
2056
|
+
try:
|
|
2057
|
+
itr = obj1._rsk_iter()
|
|
2058
|
+
except AttributeError:
|
|
2059
|
+
# set recording list (obj1) to default value [1', 1, 2', 2, ...]
|
|
2060
|
+
obj2, obj1 = obj1, []
|
|
2061
|
+
a = ZZ.one() / ZZ(2)
|
|
2062
|
+
for i in range(len(obj2)):
|
|
2063
|
+
obj1.append(a)
|
|
2064
|
+
a = a + ZZ.one() / ZZ(2)
|
|
2065
|
+
else:
|
|
2066
|
+
if check:
|
|
2067
|
+
if len(obj1) != len(obj2):
|
|
2068
|
+
raise ValueError("the two arrays must be the same length")
|
|
2069
|
+
mixed_parity = []
|
|
2070
|
+
# Check it is a restricted superbiword: that is,
|
|
2071
|
+
# the entries can have even or odd parity, but repetition of
|
|
2072
|
+
# the pairs of corresponding entries of obj1
|
|
2073
|
+
# and obj2 with mixed-parity is not allowed
|
|
2074
|
+
for t, b in zip(obj1, obj2):
|
|
2075
|
+
if PrimedEntry(t).is_primed() != PrimedEntry(b).is_primed():
|
|
2076
|
+
if (t, b) in mixed_parity:
|
|
2077
|
+
raise ValueError("invalid restricted superbiword")
|
|
2078
|
+
else:
|
|
2079
|
+
mixed_parity.append((t, b))
|
|
2080
|
+
# Since the _rsk_iter() gives unprimed entries
|
|
2081
|
+
# We will create obj1 and obj2 from it.
|
|
2082
|
+
if itr:
|
|
2083
|
+
obj1, obj2 = [], []
|
|
2084
|
+
for i, j in itr:
|
|
2085
|
+
obj1.append(i)
|
|
2086
|
+
obj2.append(j)
|
|
2087
|
+
# Converting entries of obj1 and obj2 to PrimedEntry
|
|
2088
|
+
for i in range(len(obj1)):
|
|
2089
|
+
obj1[i] = PrimedEntry(obj1[i])
|
|
2090
|
+
obj2[i] = PrimedEntry(obj2[i])
|
|
2091
|
+
return zip(obj1, obj2)
|
|
2092
|
+
|
|
2093
|
+
def _get_col(self, t, col_index):
|
|
2094
|
+
r"""
|
|
2095
|
+
Return the column as a list of a given tableau ``t`` (list of lists)
|
|
2096
|
+
at index ``col_index`` (indexing starting from zero).
|
|
2097
|
+
|
|
2098
|
+
EXAMPLES::
|
|
2099
|
+
|
|
2100
|
+
sage: from sage.combinat.rsk import RuleSuperRSK
|
|
2101
|
+
sage: t = [[1,2,3,4], [5,6,7,8], [9,10]];
|
|
2102
|
+
sage: RuleSuperRSK()._get_col(t, 0)
|
|
2103
|
+
[1, 5, 9]
|
|
2104
|
+
sage: RuleSuperRSK()._get_col(t, 2)
|
|
2105
|
+
[3, 7]
|
|
2106
|
+
"""
|
|
2107
|
+
# t is the tableau (list of lists)
|
|
2108
|
+
# compute how many rows will contribute to the col
|
|
2109
|
+
num_rows_long_enough = 0
|
|
2110
|
+
for row in t:
|
|
2111
|
+
if len(row) > col_index:
|
|
2112
|
+
num_rows_long_enough += 1
|
|
2113
|
+
else:
|
|
2114
|
+
break
|
|
2115
|
+
# create the col
|
|
2116
|
+
col = [t[row_index][col_index] for row_index in range(num_rows_long_enough)]
|
|
2117
|
+
return col
|
|
2118
|
+
|
|
2119
|
+
def _set_col(self, t, col_index, col):
|
|
2120
|
+
r"""
|
|
2121
|
+
Set the column of a given tableau ``t`` (list of lists) at
|
|
2122
|
+
index ``col_index`` (indexing starting from zero) as ``col``.
|
|
2123
|
+
|
|
2124
|
+
.. NOTE::
|
|
2125
|
+
|
|
2126
|
+
If ``length(col)`` is greater than the corresponding column in
|
|
2127
|
+
tableau ``t`` then only those rows of ``t`` will be set which
|
|
2128
|
+
have ``length(row) <= col_index``. Similarly if ``length(col)``
|
|
2129
|
+
is less than the corresponding column in tableau ``t`` then only
|
|
2130
|
+
those entries of the corresponding column in ``t`` which have row
|
|
2131
|
+
index less than ``length(col)`` will be set, rest will remain
|
|
2132
|
+
unchanged.
|
|
2133
|
+
|
|
2134
|
+
EXAMPLES::
|
|
2135
|
+
|
|
2136
|
+
sage: from sage.combinat.rsk import RuleSuperRSK
|
|
2137
|
+
sage: t = [[1,2,3,4], [5,6,7,8], [9,10]]
|
|
2138
|
+
sage: col = [1, 2, 3, 4]
|
|
2139
|
+
sage: RuleSuperRSK()._set_col(t, 0, col); t
|
|
2140
|
+
[[1, 2, 3, 4], [2, 6, 7, 8], [3, 10], [4]]
|
|
2141
|
+
sage: col = [1]
|
|
2142
|
+
sage: RuleSuperRSK()._set_col(t, 2, col); t
|
|
2143
|
+
[[1, 2, 1, 4], [2, 6, 7, 8], [3, 10], [4]]
|
|
2144
|
+
"""
|
|
2145
|
+
# overwrite a column in tableau t (list of lists) with col
|
|
2146
|
+
for row_index, val in enumerate(col):
|
|
2147
|
+
# add a box/node if necessary
|
|
2148
|
+
if row_index == len(t):
|
|
2149
|
+
t.append([])
|
|
2150
|
+
if col_index == len(t[row_index]):
|
|
2151
|
+
t[row_index].append(None)
|
|
2152
|
+
# set value
|
|
2153
|
+
t[row_index][col_index] = val
|
|
2154
|
+
|
|
2155
|
+
def forward_rule(self, obj1, obj2, check_standard=False, check=True):
|
|
2156
|
+
r"""
|
|
2157
|
+
Return a pair of tableaux obtained by applying forward
|
|
2158
|
+
insertion to the restricted super biword ``[obj1, obj2]``.
|
|
2159
|
+
|
|
2160
|
+
INPUT:
|
|
2161
|
+
|
|
2162
|
+
- ``obj1``, ``obj2`` -- can be one of the following ways to
|
|
2163
|
+
represent a generalized permutation (or, equivalently,
|
|
2164
|
+
biword):
|
|
2165
|
+
|
|
2166
|
+
- two lists ``obj1`` and ``obj2`` of equal length,
|
|
2167
|
+
to be interpreted as the top row and the bottom row of
|
|
2168
|
+
the biword
|
|
2169
|
+
|
|
2170
|
+
- a word ``obj1`` in an ordered alphabet, to be
|
|
2171
|
+
interpreted as the bottom row of the biword (in this
|
|
2172
|
+
case, ``obj2`` is ``None``; the top row of the biword
|
|
2173
|
+
is understood to be `(1, 2, \ldots, n)` by default)
|
|
2174
|
+
|
|
2175
|
+
- any object ``obj1`` which has a method ``_rsk_iter()``,
|
|
2176
|
+
as long as this method returns an iterator yielding
|
|
2177
|
+
pairs of numbers, which then are interpreted as top
|
|
2178
|
+
entries and bottom entries in the biword (in this case,
|
|
2179
|
+
``obj2`` is ``None``)
|
|
2180
|
+
|
|
2181
|
+
- ``check_standard`` -- boolean (default: ``False``); check if either
|
|
2182
|
+
of the resulting tableaux is a standard super tableau, and if so,
|
|
2183
|
+
typecast it as such
|
|
2184
|
+
|
|
2185
|
+
- ``check`` -- boolean (default: ``True``); whether to check
|
|
2186
|
+
that ``obj1`` and ``obj2`` actually define a valid
|
|
2187
|
+
restricted super biword
|
|
2188
|
+
|
|
2189
|
+
EXAMPLES::
|
|
2190
|
+
|
|
2191
|
+
sage: from sage.combinat.rsk import RuleSuperRSK
|
|
2192
|
+
sage: p, q = RuleSuperRSK().forward_rule([1, 2], [1, 3]); p
|
|
2193
|
+
[[1, 3]]
|
|
2194
|
+
sage: q
|
|
2195
|
+
[[1, 2]]
|
|
2196
|
+
sage: isinstance(p, SemistandardSuperTableau)
|
|
2197
|
+
True
|
|
2198
|
+
sage: isinstance(q, SemistandardSuperTableau)
|
|
2199
|
+
True
|
|
2200
|
+
"""
|
|
2201
|
+
itr = self.to_pairs(obj1, obj2, check=check)
|
|
2202
|
+
p = [] # the "insertion" tableau
|
|
2203
|
+
q = [] # the "recording" tableau
|
|
2204
|
+
for i, j in itr:
|
|
2205
|
+
# loop
|
|
2206
|
+
row_index = -1
|
|
2207
|
+
col_index = -1
|
|
2208
|
+
epsilon = 1 if i.is_primed() else 0
|
|
2209
|
+
while True:
|
|
2210
|
+
if i.is_primed() == j.is_primed():
|
|
2211
|
+
# row insertion
|
|
2212
|
+
row_index += 1
|
|
2213
|
+
if row_index == len(p):
|
|
2214
|
+
p.append([j])
|
|
2215
|
+
q.append([i])
|
|
2216
|
+
break
|
|
2217
|
+
else:
|
|
2218
|
+
j1, col_index = self.insertion(j, p[row_index], epsilon=epsilon)
|
|
2219
|
+
if j1 is None:
|
|
2220
|
+
p[row_index].append(j)
|
|
2221
|
+
q[row_index].append(i)
|
|
2222
|
+
break
|
|
2223
|
+
else:
|
|
2224
|
+
j = j1
|
|
2225
|
+
else:
|
|
2226
|
+
# column insertion
|
|
2227
|
+
col_index += 1
|
|
2228
|
+
if not p or col_index == len(p[0]):
|
|
2229
|
+
self._set_col(p, col_index, [j])
|
|
2230
|
+
self._set_col(q, col_index, [i])
|
|
2231
|
+
break
|
|
2232
|
+
else:
|
|
2233
|
+
# retrieve column
|
|
2234
|
+
c = self._get_col(p, col_index)
|
|
2235
|
+
j1, row_index = self.insertion(j, c, epsilon=epsilon)
|
|
2236
|
+
if j1 is None:
|
|
2237
|
+
c.append(j)
|
|
2238
|
+
self._set_col(p, col_index, c)
|
|
2239
|
+
if col_index == 0:
|
|
2240
|
+
q.append([])
|
|
2241
|
+
q[row_index].append(i)
|
|
2242
|
+
break
|
|
2243
|
+
else:
|
|
2244
|
+
j = j1
|
|
2245
|
+
self._set_col(p, col_index, c)
|
|
2246
|
+
return self._forward_format_output(p, q, check_standard=check_standard)
|
|
2247
|
+
|
|
2248
|
+
def insertion(self, j, r, epsilon=0):
|
|
2249
|
+
r"""
|
|
2250
|
+
Insert the letter ``j`` from the second row of the biword
|
|
2251
|
+
into the row ``r`` using dual RSK insertion or classical
|
|
2252
|
+
Schensted insertion depending on the value of ``epsilon``,
|
|
2253
|
+
if there is bumping to be done.
|
|
2254
|
+
|
|
2255
|
+
The row `r` is modified in place if bumping occurs. The bumped-out
|
|
2256
|
+
entry, if it exists, is returned.
|
|
2257
|
+
|
|
2258
|
+
EXAMPLES::
|
|
2259
|
+
|
|
2260
|
+
sage: from sage.combinat.rsk import RuleSuperRSK
|
|
2261
|
+
sage: from bisect import bisect_left, bisect_right
|
|
2262
|
+
sage: r = [1, 3, 3, 3, 4]
|
|
2263
|
+
sage: j = 3
|
|
2264
|
+
sage: j, y_pos = RuleSuperRSK().insertion(j, r, epsilon=0); r
|
|
2265
|
+
[1, 3, 3, 3, 3]
|
|
2266
|
+
sage: j
|
|
2267
|
+
4
|
|
2268
|
+
sage: y_pos
|
|
2269
|
+
4
|
|
2270
|
+
sage: r = [1, 3, 3, 3, 4]
|
|
2271
|
+
sage: j = 3
|
|
2272
|
+
sage: j, y_pos = RuleSuperRSK().insertion(j, r, epsilon=1); r
|
|
2273
|
+
[1, 3, 3, 3, 4]
|
|
2274
|
+
sage: j
|
|
2275
|
+
3
|
|
2276
|
+
sage: y_pos
|
|
2277
|
+
1
|
|
2278
|
+
"""
|
|
2279
|
+
bisect = bisect_right if epsilon == 0 else bisect_left
|
|
2280
|
+
|
|
2281
|
+
if (r[-1] < j) or (r[-1] == j and epsilon == 0):
|
|
2282
|
+
return None, len(r) # j needs to be added at the end of the list r.
|
|
2283
|
+
# Figure out where to insert j into the list r. The
|
|
2284
|
+
# bisect command returns the position of the least
|
|
2285
|
+
# element of r greater than j. We will call it y.
|
|
2286
|
+
y_pos = bisect(r, j)
|
|
2287
|
+
# Switch j and y
|
|
2288
|
+
j, r[y_pos] = r[y_pos], j
|
|
2289
|
+
return j, y_pos
|
|
2290
|
+
|
|
2291
|
+
def _forward_format_output(self, p, q, check_standard):
|
|
2292
|
+
r"""
|
|
2293
|
+
Return final output of the ``RSK`` (here, super RSK)
|
|
2294
|
+
correspondence from the output of the corresponding
|
|
2295
|
+
``forward_rule``.
|
|
2296
|
+
|
|
2297
|
+
EXAMPLES::
|
|
2298
|
+
|
|
2299
|
+
sage: from sage.combinat.rsk import RuleSuperRSK
|
|
2300
|
+
sage: isinstance(RuleSuperRSK()._forward_format_output(
|
|
2301
|
+
....: [['1p', 1, '2p']], [['1p', '1', '2p']], True)[0],
|
|
2302
|
+
....: StandardSuperTableau)
|
|
2303
|
+
True
|
|
2304
|
+
sage: isinstance(RuleSuperRSK()._forward_format_output(
|
|
2305
|
+
....: [[1, '2p', 3]], [[1, 2, 3]], False)[0],
|
|
2306
|
+
....: SemistandardSuperTableau)
|
|
2307
|
+
True
|
|
2308
|
+
sage: isinstance(RuleSuperRSK()._forward_format_output(
|
|
2309
|
+
....: [[1, 1, 3]], [[1, 2, 3]], True)[0],
|
|
2310
|
+
....: SemistandardSuperTableau)
|
|
2311
|
+
True
|
|
2312
|
+
"""
|
|
2313
|
+
from sage.combinat.tableau import StandardTableau
|
|
2314
|
+
from sage.combinat.super_tableau import SemistandardSuperTableau, StandardSuperTableau
|
|
2315
|
+
|
|
2316
|
+
if not p:
|
|
2317
|
+
return [StandardTableau([]), StandardTableau([])]
|
|
2318
|
+
if check_standard:
|
|
2319
|
+
try:
|
|
2320
|
+
P = StandardSuperTableau(p)
|
|
2321
|
+
except ValueError:
|
|
2322
|
+
P = SemistandardSuperTableau(p)
|
|
2323
|
+
try:
|
|
2324
|
+
Q = StandardSuperTableau(q)
|
|
2325
|
+
except ValueError:
|
|
2326
|
+
Q = SemistandardSuperTableau(q)
|
|
2327
|
+
return [P, Q]
|
|
2328
|
+
return [SemistandardSuperTableau(p), SemistandardSuperTableau(q)]
|
|
2329
|
+
|
|
2330
|
+
def backward_rule(self, p, q, output='array'):
|
|
2331
|
+
r"""
|
|
2332
|
+
Return the restricted super biword obtained by applying reverse
|
|
2333
|
+
super RSK insertion to a pair of tableaux ``(p, q)``.
|
|
2334
|
+
|
|
2335
|
+
INPUT:
|
|
2336
|
+
|
|
2337
|
+
- ``p``, ``q`` -- two tableaux of the same shape
|
|
2338
|
+
|
|
2339
|
+
- ``output`` -- (default: ``'array'``) if ``q`` is row-strict:
|
|
2340
|
+
|
|
2341
|
+
- ``'array'`` -- as a two-line array (i.e. restricted super biword)
|
|
2342
|
+
|
|
2343
|
+
and if ``q`` is standard, we can have the output:
|
|
2344
|
+
|
|
2345
|
+
- ``'word'`` -- as a word
|
|
2346
|
+
|
|
2347
|
+
EXAMPLES::
|
|
2348
|
+
|
|
2349
|
+
sage: from sage.combinat.rsk import RuleSuperRSK
|
|
2350
|
+
sage: t1 = SemistandardSuperTableau([['1p', '3p', '4p'], [2], [3]])
|
|
2351
|
+
sage: t2 = SemistandardSuperTableau([[1, 2, 4], [3], [5]])
|
|
2352
|
+
sage: RuleSuperRSK().backward_rule(t1, t2, 'array')
|
|
2353
|
+
[[1, 2, 3, 4, 5], [4', 3, 3', 2, 1']]
|
|
2354
|
+
sage: t1 = SemistandardSuperTableau([[1, 3], ['3p']])
|
|
2355
|
+
sage: t2 = SemistandardSuperTableau([[1, 2], [3]])
|
|
2356
|
+
sage: RuleSuperRSK().backward_rule(t1, t2, 'array')
|
|
2357
|
+
[[1, 2, 3], [1, 3, 3']]
|
|
2358
|
+
"""
|
|
2359
|
+
p_copy = [list(row) for row in p]
|
|
2360
|
+
upper_row = []
|
|
2361
|
+
lower_row = []
|
|
2362
|
+
# upper_row and lower_row will be the upper and lower rows of the
|
|
2363
|
+
# generalized permutation we get as a result, but both reversed
|
|
2364
|
+
d = {}
|
|
2365
|
+
for row, Li in enumerate(q):
|
|
2366
|
+
for col, val in enumerate(Li):
|
|
2367
|
+
if val in d and col in d[val]:
|
|
2368
|
+
d[val][col].append(row)
|
|
2369
|
+
elif val not in d:
|
|
2370
|
+
d[val] = {col: [row]}
|
|
2371
|
+
else:
|
|
2372
|
+
d[val][col] = [row]
|
|
2373
|
+
# d is now a double family such that for every integers k and j,
|
|
2374
|
+
# the value d[k][j] is the list of rows i such that the (i, j)-th
|
|
2375
|
+
# cell of q is filled with k.
|
|
2376
|
+
for value, iter_dict in sorted(d.items(), reverse=True, key=lambda x: x[0]):
|
|
2377
|
+
epsilon = 1 if value.is_primed() else 0
|
|
2378
|
+
if epsilon == 1:
|
|
2379
|
+
iter_copy = dict(iter_dict)
|
|
2380
|
+
iter_dict = {}
|
|
2381
|
+
for k, v in iter_copy.items():
|
|
2382
|
+
for vi in v:
|
|
2383
|
+
if vi in iter_dict:
|
|
2384
|
+
iter_dict[vi].append(k)
|
|
2385
|
+
else:
|
|
2386
|
+
iter_dict[vi] = [k]
|
|
2387
|
+
for key in sorted(iter_dict, reverse=True):
|
|
2388
|
+
for rows in iter_dict[key]:
|
|
2389
|
+
row_index, col_index = (rows, key) if epsilon == 0 else (key, rows)
|
|
2390
|
+
x = p_copy[row_index].pop() # Always the right-most entry
|
|
2391
|
+
while True:
|
|
2392
|
+
if value.is_primed() == x.is_primed():
|
|
2393
|
+
# row bumping
|
|
2394
|
+
row_index -= 1
|
|
2395
|
+
if row_index < 0:
|
|
2396
|
+
break
|
|
2397
|
+
x, col_index = self.reverse_insertion(x, p_copy[row_index], epsilon=epsilon)
|
|
2398
|
+
else:
|
|
2399
|
+
# column bumping
|
|
2400
|
+
col_index -= 1
|
|
2401
|
+
if col_index < 0:
|
|
2402
|
+
break
|
|
2403
|
+
c = self._get_col(p_copy, col_index)
|
|
2404
|
+
x, row_index = self.reverse_insertion(x, c, epsilon=epsilon)
|
|
2405
|
+
self._set_col(p_copy, col_index, c)
|
|
2406
|
+
upper_row.append(value)
|
|
2407
|
+
lower_row.append(x)
|
|
2408
|
+
return self._backward_format_output(lower_row, upper_row, output, q.is_standard())
|
|
2409
|
+
|
|
2410
|
+
def reverse_insertion(self, x, row, epsilon=0):
|
|
2411
|
+
r"""
|
|
2412
|
+
Reverse bump the row ``row`` of the current insertion tableau
|
|
2413
|
+
with the number ``x`` using dual RSK insertion or classical
|
|
2414
|
+
Schensted insertion depending on the value of `epsilon`.
|
|
2415
|
+
|
|
2416
|
+
The row ``row`` is modified in place. The bumped-out entry
|
|
2417
|
+
is returned along with the bumped position.
|
|
2418
|
+
|
|
2419
|
+
EXAMPLES::
|
|
2420
|
+
|
|
2421
|
+
sage: from sage.combinat.rsk import RuleSuperRSK
|
|
2422
|
+
sage: from bisect import bisect_left, bisect_right
|
|
2423
|
+
sage: r = [1, 3, 3, 3, 4]
|
|
2424
|
+
sage: j = 2
|
|
2425
|
+
sage: j, y = RuleSuperRSK().reverse_insertion(j, r, epsilon=0); r
|
|
2426
|
+
[2, 3, 3, 3, 4]
|
|
2427
|
+
sage: j
|
|
2428
|
+
1
|
|
2429
|
+
sage: y
|
|
2430
|
+
0
|
|
2431
|
+
sage: r = [1, 3, 3, 3, 4]
|
|
2432
|
+
sage: j = 3
|
|
2433
|
+
sage: j, y = RuleSuperRSK().reverse_insertion(j, r, epsilon=0); r
|
|
2434
|
+
[3, 3, 3, 3, 4]
|
|
2435
|
+
sage: j
|
|
2436
|
+
1
|
|
2437
|
+
sage: y
|
|
2438
|
+
0
|
|
2439
|
+
sage: r = [1, 3, 3, 3, 4]
|
|
2440
|
+
sage: j = (3)
|
|
2441
|
+
sage: j, y = RuleSuperRSK().reverse_insertion(j, r, epsilon=1); r
|
|
2442
|
+
[1, 3, 3, 3, 4]
|
|
2443
|
+
sage: j
|
|
2444
|
+
3
|
|
2445
|
+
sage: y
|
|
2446
|
+
3
|
|
2447
|
+
"""
|
|
2448
|
+
bisect = bisect_left if epsilon == 0 else bisect_right
|
|
2449
|
+
y_pos = bisect(row, x) - 1
|
|
2450
|
+
# switch x and y
|
|
2451
|
+
x, row[y_pos] = row[y_pos], x
|
|
2452
|
+
return x, y_pos
|
|
2453
|
+
|
|
2454
|
+
def _backward_format_output(self, lower_row, upper_row, output,
|
|
2455
|
+
q_is_standard):
|
|
2456
|
+
r"""
|
|
2457
|
+
Return the final output of the ``RSK_inverse`` correspondence
|
|
2458
|
+
from the output of the corresponding ``backward_rule``.
|
|
2459
|
+
|
|
2460
|
+
.. NOTE::
|
|
2461
|
+
|
|
2462
|
+
The default implementation of ``backward_rule`` lists
|
|
2463
|
+
bumped-out entries in the order in which the reverse
|
|
2464
|
+
bumping happens, which is *opposite* to the order of the
|
|
2465
|
+
final output.
|
|
2466
|
+
|
|
2467
|
+
EXAMPLES::
|
|
2468
|
+
|
|
2469
|
+
sage: from sage.combinat.rsk import RuleSuperRSK
|
|
2470
|
+
sage: from sage.combinat.shifted_primed_tableau import PrimedEntry
|
|
2471
|
+
sage: RuleSuperRSK()._backward_format_output([PrimedEntry('1p'),
|
|
2472
|
+
....: PrimedEntry(1), PrimedEntry('3p'), PrimedEntry(9)],
|
|
2473
|
+
....: [PrimedEntry(1), PrimedEntry('2p'), PrimedEntry('3p'),
|
|
2474
|
+
....: PrimedEntry(4)], 'array', False)
|
|
2475
|
+
[[4, 3', 2', 1], [9, 3', 1, 1']]
|
|
2476
|
+
sage: RuleSuperRSK()._backward_format_output([PrimedEntry(1),
|
|
2477
|
+
....: PrimedEntry('2p'), PrimedEntry('3p'), PrimedEntry(4)],
|
|
2478
|
+
....: [PrimedEntry('1p'), PrimedEntry(1), PrimedEntry('2p'),
|
|
2479
|
+
....: PrimedEntry(2)], 'word', True)
|
|
2480
|
+
word: 4,3',2',1
|
|
2481
|
+
sage: RuleSuperRSK()._backward_format_output([PrimedEntry(1),
|
|
2482
|
+
....: PrimedEntry(2), PrimedEntry(3), PrimedEntry(4)],
|
|
2483
|
+
....: [PrimedEntry('1p'), PrimedEntry(1), PrimedEntry('2p'),
|
|
2484
|
+
....: PrimedEntry(2)], 'word', True)
|
|
2485
|
+
word: 4321
|
|
2486
|
+
"""
|
|
2487
|
+
if output == 'array':
|
|
2488
|
+
return [list(reversed(upper_row)), list(reversed(lower_row))]
|
|
2489
|
+
if output == 'word':
|
|
2490
|
+
if q_is_standard:
|
|
2491
|
+
from sage.combinat.words.word import Word
|
|
2492
|
+
return Word(reversed(lower_row))
|
|
2493
|
+
else:
|
|
2494
|
+
raise TypeError("q must be standard to have a %s as "
|
|
2495
|
+
"valid output" % output)
|
|
2496
|
+
raise ValueError("invalid output option")
|
|
2497
|
+
|
|
2498
|
+
|
|
2499
|
+
class RuleStar(Rule):
|
|
2500
|
+
r"""
|
|
2501
|
+
Rule for `\star`-insertion.
|
|
2502
|
+
|
|
2503
|
+
The `\star`-insertion is similar to the classical RSK algorithm
|
|
2504
|
+
and is defined in [MPPS2020]_. The bottom row of the increasing
|
|
2505
|
+
Hecke biword is a word in the 0-Hecke monoid that is fully
|
|
2506
|
+
commutative. When inserting a letter `x` into a row `R`, there
|
|
2507
|
+
are three cases:
|
|
2508
|
+
|
|
2509
|
+
- Case 1: If `R` is empty or `x > \max(R)`, append `x` to row `R`
|
|
2510
|
+
and terminate.
|
|
2511
|
+
|
|
2512
|
+
- Case 2: Otherwise if `x` is not in `R`, locate the smallest `y` in `R`
|
|
2513
|
+
with `y > x`. Bump `y` with `x` and insert `y` into the next row.
|
|
2514
|
+
|
|
2515
|
+
- Case 3: Otherwise, if `x` is in `R`, locate the smallest `y` in `R` with
|
|
2516
|
+
`y \leq x` and interval `[y,x]` contained in `R`. Row `R` remains
|
|
2517
|
+
unchanged and `y` is to be inserted into the next row.
|
|
2518
|
+
|
|
2519
|
+
The `\star`-insertion returns a pair consisting a conjugate of a
|
|
2520
|
+
semistandard tableau and a semistandard tableau. It is a bijection from the
|
|
2521
|
+
collection of all increasing Hecke biwords whose bottom row is a fully
|
|
2522
|
+
commutative word to pairs (P, Q) of tableaux of the same shape such that
|
|
2523
|
+
P is conjugate semistandard, Q is semistandard and the row reading word of
|
|
2524
|
+
P is fully commutative [MPPS2020]_.
|
|
2525
|
+
|
|
2526
|
+
EXAMPLES:
|
|
2527
|
+
|
|
2528
|
+
As an example of `\star`-insertion, we reproduce Example 28 in [MPPS2020]_::
|
|
2529
|
+
|
|
2530
|
+
sage: # needs sage.groups
|
|
2531
|
+
sage: from sage.combinat.rsk import RuleStar
|
|
2532
|
+
sage: p,q = RuleStar().forward_rule([1,1,2,2,4,4], [1,3,2,4,2,4])
|
|
2533
|
+
sage: ascii_art(p, q)
|
|
2534
|
+
1 2 4 1 1 2
|
|
2535
|
+
1 4 2 4
|
|
2536
|
+
3 4
|
|
2537
|
+
sage: line1,line2 = RuleStar().backward_rule(p, q)
|
|
2538
|
+
sage: line1,line2
|
|
2539
|
+
([1, 1, 2, 2, 4, 4], [1, 3, 2, 4, 2, 4])
|
|
2540
|
+
sage: RSK_inverse(p, q, output='DecreasingHeckeFactorization', insertion='Star')
|
|
2541
|
+
(4, 2)()(4, 2)(3, 1)
|
|
2542
|
+
|
|
2543
|
+
sage: # needs sage.groups
|
|
2544
|
+
sage: from sage.combinat.crystals.fully_commutative_stable_grothendieck import DecreasingHeckeFactorization
|
|
2545
|
+
sage: h = DecreasingHeckeFactorization([[4, 2], [], [4, 2], [3, 1]])
|
|
2546
|
+
sage: RSK_inverse(*RSK(h,insertion='Star'),insertion='Star',
|
|
2547
|
+
....: output='DecreasingHeckeFactorization')
|
|
2548
|
+
(4, 2)()(4, 2)(3, 1)
|
|
2549
|
+
sage: p,q = RSK(h, insertion='Star')
|
|
2550
|
+
sage: ascii_art(p, q)
|
|
2551
|
+
1 2 4 1 1 2
|
|
2552
|
+
1 4 2 4
|
|
2553
|
+
3 4
|
|
2554
|
+
sage: RSK_inverse(p, q, insertion='Star')
|
|
2555
|
+
[[1, 1, 2, 2, 4, 4], [1, 3, 2, 4, 2, 4]]
|
|
2556
|
+
sage: f = RSK_inverse(p, q, output='DecreasingHeckeFactorization', insertion='Star')
|
|
2557
|
+
sage: f == h
|
|
2558
|
+
True
|
|
2559
|
+
|
|
2560
|
+
.. WARNING::
|
|
2561
|
+
|
|
2562
|
+
When ``output`` is set to ``'DecreasingHeckeFactorization'``, the
|
|
2563
|
+
inverse of `\star`-insertion of `(P,Q)` returns a decreasing
|
|
2564
|
+
factorization whose number of factors is the maximum entry of `Q`::
|
|
2565
|
+
|
|
2566
|
+
sage: # needs sage.groups
|
|
2567
|
+
sage: from sage.combinat.crystals.fully_commutative_stable_grothendieck import DecreasingHeckeFactorization
|
|
2568
|
+
sage: h1 = DecreasingHeckeFactorization([[],[3,1],[1]]); h1
|
|
2569
|
+
()(3, 1)(1)
|
|
2570
|
+
sage: P,Q = RSK(h1, insertion='Star')
|
|
2571
|
+
sage: ascii_art(P, Q)
|
|
2572
|
+
1 3 1 2
|
|
2573
|
+
1 2
|
|
2574
|
+
sage: h2 = RSK_inverse(P, Q, insertion='Star',
|
|
2575
|
+
....: output='DecreasingHeckeFactorization'); h2
|
|
2576
|
+
(3, 1)(1)
|
|
2577
|
+
|
|
2578
|
+
TESTS:
|
|
2579
|
+
|
|
2580
|
+
Check that :func:`RSK` is the inverse of :func:`RSK_inverse` for various
|
|
2581
|
+
outputs/inputs::
|
|
2582
|
+
|
|
2583
|
+
sage: from sage.combinat.partition import Partitions_n
|
|
2584
|
+
sage: shapes = [shape for n in range(7) for shape in Partitions_n(n)]
|
|
2585
|
+
sage: row_reading = lambda T: [x for row in reversed(T) for x in row]
|
|
2586
|
+
sage: from sage.monoids.hecke_monoid import HeckeMonoid
|
|
2587
|
+
sage: H = HeckeMonoid(SymmetricGroup(4+1))
|
|
2588
|
+
sage: from sage.combinat import permutation
|
|
2589
|
+
sage: reduce = lambda w: permutation.from_reduced_word(H.from_reduced_word(w).reduced_word())
|
|
2590
|
+
sage: fc = lambda w: not reduce(w).has_pattern([3,2,1])
|
|
2591
|
+
sage: FC_tabs = [T for shape in shapes
|
|
2592
|
+
....: for T in SemistandardTableaux(shape, max_entry=4)
|
|
2593
|
+
....: if fc(row_reading(T.conjugate()))]
|
|
2594
|
+
sage: Checks = []
|
|
2595
|
+
sage: for T in FC_tabs: # long time
|
|
2596
|
+
....: shape = T.shape().conjugate()
|
|
2597
|
+
....: P = T.conjugate()
|
|
2598
|
+
....: Checks += [all((P,Q) == tuple(RSK(*RSK_inverse(P, Q,
|
|
2599
|
+
....: insertion='Star', output='array'),
|
|
2600
|
+
....: insertion='Star'))
|
|
2601
|
+
....: for Q in SemistandardTableaux(shape, max_entry=5))]
|
|
2602
|
+
sage: all(Checks)
|
|
2603
|
+
True
|
|
2604
|
+
sage: Checks = []
|
|
2605
|
+
sage: for T in FC_tabs: # long time
|
|
2606
|
+
....: shape = T.shape().conjugate()
|
|
2607
|
+
....: P = T.conjugate()
|
|
2608
|
+
....: Checks += [all((P,Q) == tuple(RSK(RSK_inverse(P, Q,
|
|
2609
|
+
....: insertion='Star', output='DecreasingHeckeFactorization'),
|
|
2610
|
+
....: insertion='Star'))
|
|
2611
|
+
....: for Q in SemistandardTableaux(shape, max_entry=5))]
|
|
2612
|
+
sage: all(Checks)
|
|
2613
|
+
True
|
|
2614
|
+
sage: Checks = []
|
|
2615
|
+
sage: for T in FC_tabs:
|
|
2616
|
+
....: shape = T.shape().conjugate()
|
|
2617
|
+
....: P = T.conjugate()
|
|
2618
|
+
....: for Q in StandardTableaux(shape, max_entry=5):
|
|
2619
|
+
....: Checks += [(P,Q) == tuple(RSK(RSK_inverse(P, Q,
|
|
2620
|
+
....: insertion='Star', output='word'),
|
|
2621
|
+
....: insertion='Star'))]
|
|
2622
|
+
sage: all(Checks)
|
|
2623
|
+
True
|
|
2624
|
+
|
|
2625
|
+
Check that :func:`RSK_inverse` is the inverse of :func:`RSK` on arrays
|
|
2626
|
+
and words::
|
|
2627
|
+
|
|
2628
|
+
sage: S = SymmetricGroup(3+1)
|
|
2629
|
+
sage: from sage.combinat import permutation
|
|
2630
|
+
sage: FC = [x
|
|
2631
|
+
....: for x in S
|
|
2632
|
+
....: if (not permutation.from_reduced_word(
|
|
2633
|
+
....: x.reduced_word()).has_pattern([3,2,1]) and
|
|
2634
|
+
....: x.reduced_word())]
|
|
2635
|
+
sage: Triples = [(w, factors, ex)
|
|
2636
|
+
....: for w in FC
|
|
2637
|
+
....: for factors in range(2, 5+1)
|
|
2638
|
+
....: for ex in range(4)]
|
|
2639
|
+
sage: Checks = []
|
|
2640
|
+
sage: for t in Triples:
|
|
2641
|
+
....: B = crystals.FullyCommutativeStableGrothendieck(*t)
|
|
2642
|
+
....: Checks += [all(b.to_increasing_hecke_biword() ==
|
|
2643
|
+
....: RSK_inverse(*RSK(
|
|
2644
|
+
....: *b.to_increasing_hecke_biword(),
|
|
2645
|
+
....: insertion='Star'), insertion='Star')
|
|
2646
|
+
....: for b in B)]
|
|
2647
|
+
sage: all(Checks)
|
|
2648
|
+
True
|
|
2649
|
+
|
|
2650
|
+
sage: from sage.monoids.hecke_monoid import HeckeMonoid
|
|
2651
|
+
sage: Checks = []
|
|
2652
|
+
sage: H = HeckeMonoid(SymmetricGroup(3+1))
|
|
2653
|
+
sage: reduce = lambda w: permutation.from_reduced_word(H.from_reduced_word(w).reduced_word())
|
|
2654
|
+
sage: fc = lambda w: not reduce(w).has_pattern([3,2,1])
|
|
2655
|
+
sage: words = [w for n in range(10) for w in Words(3, n) if fc(w)]
|
|
2656
|
+
sage: all([all(w == RSK_inverse(*RSK(w, insertion='Star'),
|
|
2657
|
+
....: insertion='Star', output='word') for w in words)])
|
|
2658
|
+
True
|
|
2659
|
+
"""
|
|
2660
|
+
|
|
2661
|
+
def forward_rule(self, obj1, obj2=None, check_braid=True):
|
|
2662
|
+
r"""
|
|
2663
|
+
Return a pair of tableaux obtained by applying forward insertion
|
|
2664
|
+
to the increasing Hecke biword ``[obj1, obj2]``.
|
|
2665
|
+
|
|
2666
|
+
INPUT:
|
|
2667
|
+
|
|
2668
|
+
- ``obj1``, ``obj2`` -- can be one of the following ways to represent a
|
|
2669
|
+
biword (or, equivalently, an increasing 0-Hecke factorization) that
|
|
2670
|
+
is fully commutative:
|
|
2671
|
+
|
|
2672
|
+
- two lists ``obj1`` and ``obj2`` of equal length, to be
|
|
2673
|
+
interpreted as the top row and the bottom row of the biword.
|
|
2674
|
+
|
|
2675
|
+
- a word ``obj1`` in an ordered alphabet, to be interpreted as
|
|
2676
|
+
the bottom row of the biword (in this case, ``obj2`` is ``None``;
|
|
2677
|
+
the top row of the biword is understood to be `(1,2,\ldots,n)`
|
|
2678
|
+
by default).
|
|
2679
|
+
|
|
2680
|
+
- a DecreasingHeckeFactorization ``obj1``, the whose increasing
|
|
2681
|
+
Hecke biword will be interpreted as the bottom row; the top row is
|
|
2682
|
+
understood to be the indices of the factors for each letter in
|
|
2683
|
+
this biword.
|
|
2684
|
+
|
|
2685
|
+
- ``check_braid`` -- boolean (default: ``True``); indicator to validate
|
|
2686
|
+
that input is associated to a fully commutative word in the 0-Hecke
|
|
2687
|
+
monoid, validation is performed if set to ``True``. Οtherwise, this
|
|
2688
|
+
validation is ignored.
|
|
2689
|
+
|
|
2690
|
+
EXAMPLES::
|
|
2691
|
+
|
|
2692
|
+
sage: # needs sage.groups
|
|
2693
|
+
sage: from sage.combinat.rsk import RuleStar
|
|
2694
|
+
sage: p,q = RuleStar().forward_rule([1,1,2,3,3], [2,3,3,1,3]); p,q
|
|
2695
|
+
([[1, 3], [2, 3], [2]], [[1, 1], [2, 3], [3]])
|
|
2696
|
+
sage: p,q = RuleStar().forward_rule([2,3,3,1,3]); p,q
|
|
2697
|
+
([[1, 3], [2, 3], [2]], [[1, 2], [3, 5], [4]])
|
|
2698
|
+
sage: p,q = RSK([1,1,2,3,3], [2,3,3,1,3], insertion=RSK.rules.Star); p,q
|
|
2699
|
+
([[1, 3], [2, 3], [2]], [[1, 1], [2, 3], [3]])
|
|
2700
|
+
|
|
2701
|
+
sage: # needs sage.groups
|
|
2702
|
+
sage: from sage.combinat.crystals.fully_commutative_stable_grothendieck import DecreasingHeckeFactorization
|
|
2703
|
+
sage: h = DecreasingHeckeFactorization([[3, 1], [3], [3, 2]])
|
|
2704
|
+
sage: p,q = RSK(h, insertion=RSK.rules.Star); p,q
|
|
2705
|
+
([[1, 3], [2, 3], [2]], [[1, 1], [2, 3], [3]])
|
|
2706
|
+
|
|
2707
|
+
TESTS:
|
|
2708
|
+
|
|
2709
|
+
Empty objects::
|
|
2710
|
+
|
|
2711
|
+
sage: # needs sage.groups
|
|
2712
|
+
sage: from sage.combinat.rsk import RuleStar
|
|
2713
|
+
sage: p,q = RuleStar().forward_rule([]); p,q
|
|
2714
|
+
([], [])
|
|
2715
|
+
|
|
2716
|
+
sage: # needs sage.groups
|
|
2717
|
+
sage: from sage.combinat.crystals.fully_commutative_stable_grothendieck import DecreasingHeckeFactorization
|
|
2718
|
+
sage: h = DecreasingHeckeFactorization([[],[]])
|
|
2719
|
+
sage: p,q = RuleStar().forward_rule(h); p,q
|
|
2720
|
+
([], [])
|
|
2721
|
+
|
|
2722
|
+
Invalid inputs::
|
|
2723
|
+
|
|
2724
|
+
sage: # needs sage.groups
|
|
2725
|
+
sage: p,q = RuleStar().forward_rule([1,1,2,3,3], [2,2,3,1,3])
|
|
2726
|
+
Traceback (most recent call last):
|
|
2727
|
+
...
|
|
2728
|
+
ValueError: [1, 1, 2, 3, 3], [2, 2, 3, 1, 3] is not an increasing factorization
|
|
2729
|
+
sage: p,q = RuleStar().forward_rule([1,1,2,2,4,4], [1,3,2,4,1,3])
|
|
2730
|
+
Traceback (most recent call last):
|
|
2731
|
+
...
|
|
2732
|
+
ValueError: the Star insertion is not defined for non-fully commutative words
|
|
2733
|
+
"""
|
|
2734
|
+
if obj2 is None and obj1 is not None:
|
|
2735
|
+
from sage.combinat.crystals.fully_commutative_stable_grothendieck import DecreasingHeckeFactorization
|
|
2736
|
+
if not isinstance(obj1, DecreasingHeckeFactorization):
|
|
2737
|
+
obj2 = obj1
|
|
2738
|
+
obj1 = list(range(1, len(obj1)+1))
|
|
2739
|
+
else:
|
|
2740
|
+
h = obj1
|
|
2741
|
+
obj1 = sum([[h.factors-i]*len(h.value[i]) for i in reversed(range(h.factors))], [])
|
|
2742
|
+
obj2 = [i for f in h.value[::-1] for i in reversed(f)]
|
|
2743
|
+
if len(obj1) != len(obj2):
|
|
2744
|
+
raise ValueError(f"{obj1} and {obj2} have different number of elements")
|
|
2745
|
+
for i in range(len(obj1)-1):
|
|
2746
|
+
if obj1[i] > obj1[i+1] or (obj1[i] == obj1[i+1] and obj2[i] >= obj2[i+1]):
|
|
2747
|
+
raise ValueError(f"{obj1}, {obj2} is not an increasing factorization")
|
|
2748
|
+
if check_braid:
|
|
2749
|
+
N = max(obj2)+1 if obj2 else 1
|
|
2750
|
+
from sage.monoids.hecke_monoid import HeckeMonoid
|
|
2751
|
+
from sage.groups.perm_gps.permgroup_named import SymmetricGroup
|
|
2752
|
+
H = HeckeMonoid(SymmetricGroup(N))
|
|
2753
|
+
h = H.from_reduced_word(obj2)
|
|
2754
|
+
from sage.combinat import permutation
|
|
2755
|
+
p = permutation.from_reduced_word(h.reduced_word())
|
|
2756
|
+
if p.has_pattern([3, 2, 1]):
|
|
2757
|
+
raise ValueError("the Star insertion is not defined for non-fully commutative words")
|
|
2758
|
+
|
|
2759
|
+
p = [] # the "insertion" tableau
|
|
2760
|
+
q = [] # the "recording" tableau
|
|
2761
|
+
for i, j in zip(obj1, obj2):
|
|
2762
|
+
for r, qr in zip(p, q):
|
|
2763
|
+
j1 = self.insertion(j, r)
|
|
2764
|
+
if j1 is None:
|
|
2765
|
+
r.append(j)
|
|
2766
|
+
qr.append(i) # Values are always inserted to the right
|
|
2767
|
+
break
|
|
2768
|
+
else:
|
|
2769
|
+
j = j1
|
|
2770
|
+
else:
|
|
2771
|
+
# We made through all of the rows of p without breaking
|
|
2772
|
+
# so we need to add a new row to p and q.
|
|
2773
|
+
p.append([j])
|
|
2774
|
+
q.append([i])
|
|
2775
|
+
from sage.combinat.tableau import Tableau, SemistandardTableau
|
|
2776
|
+
p = Tableau(p)
|
|
2777
|
+
q = SemistandardTableau(q)
|
|
2778
|
+
return [p, q]
|
|
2779
|
+
|
|
2780
|
+
def backward_rule(self, p, q, output='array'):
|
|
2781
|
+
r"""
|
|
2782
|
+
Return the increasing Hecke biword obtained by applying reverse
|
|
2783
|
+
`\star`-insertion to a pair of tableaux ``(p, q)``.
|
|
2784
|
+
|
|
2785
|
+
INPUT:
|
|
2786
|
+
|
|
2787
|
+
- ``p``, ``q`` -- two tableaux of the same shape, where ``p`` is the
|
|
2788
|
+
conjugate of a semistandard tableau, whose reading word is fully
|
|
2789
|
+
commutative and ``q`` is a semistandard tableau.
|
|
2790
|
+
|
|
2791
|
+
- ``output`` -- (default: ``'array'``) if ``q`` is semi-standard:
|
|
2792
|
+
|
|
2793
|
+
- ``'array'`` -- as a two-line array (i.e. generalized permutation
|
|
2794
|
+
or biword) that is an increasing Hecke biword
|
|
2795
|
+
- ``'DecreasingHeckeFactorization'`` -- as a decreasing
|
|
2796
|
+
factorization in the 0-Hecke monoid
|
|
2797
|
+
|
|
2798
|
+
and if ``q`` is standard:
|
|
2799
|
+
|
|
2800
|
+
- ``'word'`` -- as a (possibly non-reduced) word in the 0-Hecke
|
|
2801
|
+
monoid
|
|
2802
|
+
|
|
2803
|
+
.. WARNING::
|
|
2804
|
+
|
|
2805
|
+
When output is 'DecreasingHeckeFactorization', the number of factors
|
|
2806
|
+
in the output is the largest number in ``obj1``.
|
|
2807
|
+
|
|
2808
|
+
EXAMPLES::
|
|
2809
|
+
|
|
2810
|
+
sage: # needs sage.groups
|
|
2811
|
+
sage: from sage.combinat.rsk import RuleStar
|
|
2812
|
+
sage: p,q = RuleStar().forward_rule([1,1,2,2,4,4], [1,3,2,4,2,4])
|
|
2813
|
+
sage: ascii_art(p, q)
|
|
2814
|
+
1 2 4 1 1 2
|
|
2815
|
+
1 4 2 4
|
|
2816
|
+
3 4
|
|
2817
|
+
sage: line1,line2 = RuleStar().backward_rule(p, q); line1,line2
|
|
2818
|
+
([1, 1, 2, 2, 4, 4], [1, 3, 2, 4, 2, 4])
|
|
2819
|
+
sage: RuleStar().backward_rule(p, q, output = 'DecreasingHeckeFactorization')
|
|
2820
|
+
(4, 2)()(4, 2)(3, 1)
|
|
2821
|
+
|
|
2822
|
+
TESTS:
|
|
2823
|
+
|
|
2824
|
+
Empty objects::
|
|
2825
|
+
|
|
2826
|
+
sage: # needs sage.groups
|
|
2827
|
+
sage: RuleStar().backward_rule(Tableau([]), Tableau([]))
|
|
2828
|
+
[[], []]
|
|
2829
|
+
sage: RuleStar().backward_rule(Tableau([]), Tableau([]), output='word')
|
|
2830
|
+
word:
|
|
2831
|
+
sage: RuleStar().backward_rule(Tableau([]), Tableau([]),output='DecreasingHeckeFactorization')
|
|
2832
|
+
()
|
|
2833
|
+
"""
|
|
2834
|
+
from sage.combinat.tableau import SemistandardTableaux
|
|
2835
|
+
if p.shape() != q.shape():
|
|
2836
|
+
raise ValueError("p(=%s) and q(=%s) must have the same shape" % (p, q))
|
|
2837
|
+
if q not in SemistandardTableaux():
|
|
2838
|
+
raise ValueError("q(=%s) must be a semistandard tableau" % q)
|
|
2839
|
+
if p.conjugate() not in SemistandardTableaux():
|
|
2840
|
+
raise ValueError("the conjugate of p(=%s) must be a semistandard tableau" % p.conjugate())
|
|
2841
|
+
|
|
2842
|
+
row_reading = [ele for row in reversed(p) for ele in row]
|
|
2843
|
+
N = max(row_reading + [0]) + 1
|
|
2844
|
+
from sage.monoids.hecke_monoid import HeckeMonoid
|
|
2845
|
+
from sage.groups.perm_gps.permgroup_named import SymmetricGroup
|
|
2846
|
+
H = HeckeMonoid(SymmetricGroup(N))
|
|
2847
|
+
h = H.from_reduced_word(row_reading)
|
|
2848
|
+
from sage.combinat import permutation
|
|
2849
|
+
w = permutation.from_reduced_word(h.reduced_word())
|
|
2850
|
+
if w.has_pattern([3, 2, 1]):
|
|
2851
|
+
raise ValueError(f"the row reading word of the insertion tableau {p} is not fully-commutative")
|
|
2852
|
+
|
|
2853
|
+
p_copy = p.to_list()
|
|
2854
|
+
line1 = []
|
|
2855
|
+
line2 = []
|
|
2856
|
+
d = {}
|
|
2857
|
+
for i, row in enumerate(q):
|
|
2858
|
+
for j, val in enumerate(row):
|
|
2859
|
+
if val in d:
|
|
2860
|
+
d[val][j] = i
|
|
2861
|
+
else:
|
|
2862
|
+
d[val] = {j: i}
|
|
2863
|
+
# d is now a double family such that for all integers k and j,
|
|
2864
|
+
# d[k][j]=i means that (i, j)-th cell of q is filled with k.
|
|
2865
|
+
for value, row_dict in sorted(d.items(), key=lambda x: -x[0]):
|
|
2866
|
+
for j in sorted(row_dict, reverse=True):
|
|
2867
|
+
i = row_dict[j]
|
|
2868
|
+
x = p_copy[i].pop() # Always the right-most entry
|
|
2869
|
+
while i > 0:
|
|
2870
|
+
i -= 1
|
|
2871
|
+
row = p_copy[i]
|
|
2872
|
+
x = self.reverse_insertion(x, row)
|
|
2873
|
+
line2.append(x)
|
|
2874
|
+
line1.append(value)
|
|
2875
|
+
return self._backward_format_output(line1[::-1], line2[::-1], output)
|
|
2876
|
+
|
|
2877
|
+
def insertion(self, b, r):
|
|
2878
|
+
r"""
|
|
2879
|
+
Insert the letter ``b`` from the second row of the biword into the row
|
|
2880
|
+
``r`` using `\star`-insertion defined in [MPPS2020]_.
|
|
2881
|
+
|
|
2882
|
+
The row `r` is modified in place if bumping occurs and `b` is not in
|
|
2883
|
+
row `r`. The bumped-out entry, if it exists, is returned.
|
|
2884
|
+
|
|
2885
|
+
EXAMPLES::
|
|
2886
|
+
|
|
2887
|
+
sage: from sage.combinat.rsk import RuleStar
|
|
2888
|
+
sage: RuleStar().insertion(3, [1,2,4,5])
|
|
2889
|
+
4
|
|
2890
|
+
sage: RuleStar().insertion(3, [1,2,3,5])
|
|
2891
|
+
1
|
|
2892
|
+
sage: RuleStar().insertion(6, [1,2,3,5]) is None
|
|
2893
|
+
True
|
|
2894
|
+
"""
|
|
2895
|
+
if r[-1] < b:
|
|
2896
|
+
return None # append b to the end of row r
|
|
2897
|
+
if b in r:
|
|
2898
|
+
k = b
|
|
2899
|
+
while k in r:
|
|
2900
|
+
k -= 1
|
|
2901
|
+
k += 1
|
|
2902
|
+
else:
|
|
2903
|
+
y_pos = bisect_right(r, b)
|
|
2904
|
+
k = r[y_pos]
|
|
2905
|
+
r[y_pos] = b
|
|
2906
|
+
return k
|
|
2907
|
+
|
|
2908
|
+
def reverse_insertion(self, x, r):
|
|
2909
|
+
"""
|
|
2910
|
+
Reverse bump the row ``r`` of the current insertion tableau ``p``
|
|
2911
|
+
with number ``x``, provided that ``r`` is the ``i``-th row of ``p``.
|
|
2912
|
+
|
|
2913
|
+
The row ``r`` is modified in place. The bumped-out entry is returned.
|
|
2914
|
+
|
|
2915
|
+
EXAMPLES::
|
|
2916
|
+
|
|
2917
|
+
sage: from sage.combinat.rsk import RuleStar
|
|
2918
|
+
sage: RuleStar().reverse_insertion(4, [1,2,3,5])
|
|
2919
|
+
3
|
|
2920
|
+
sage: RuleStar().reverse_insertion(1, [1,2,3,5])
|
|
2921
|
+
3
|
|
2922
|
+
sage: RuleStar().reverse_insertion(5, [1,2,3,5])
|
|
2923
|
+
5
|
|
2924
|
+
"""
|
|
2925
|
+
if x in r:
|
|
2926
|
+
y = x
|
|
2927
|
+
while y in r:
|
|
2928
|
+
y += 1
|
|
2929
|
+
y -= 1
|
|
2930
|
+
else:
|
|
2931
|
+
y_pos = bisect_left(r, x) - 1
|
|
2932
|
+
y = r[y_pos]
|
|
2933
|
+
r[y_pos] = x
|
|
2934
|
+
x = y
|
|
2935
|
+
return x
|
|
2936
|
+
|
|
2937
|
+
def _backward_format_output(self, obj1, obj2, output):
|
|
2938
|
+
r"""
|
|
2939
|
+
Return the final output of the ``RSK_inverse`` correspondence from
|
|
2940
|
+
the output of the corresponding ``backward_rule``.
|
|
2941
|
+
|
|
2942
|
+
EXAMPLES::
|
|
2943
|
+
|
|
2944
|
+
sage: # needs sage.groups
|
|
2945
|
+
sage: from sage.combinat.rsk import RuleStar
|
|
2946
|
+
sage: RuleStar()._backward_format_output([1, 1, 2, 2, 4, 4], [1, 3, 2, 4, 2, 4], 'array')
|
|
2947
|
+
[[1, 1, 2, 2, 4, 4], [1, 3, 2, 4, 2, 4]]
|
|
2948
|
+
sage: RuleStar()._backward_format_output([1, 1, 2, 2, 4, 4], [1, 3, 2, 4, 2, 4], 'DecreasingHeckeFactorization')
|
|
2949
|
+
(4, 2)()(4, 2)(3, 1)
|
|
2950
|
+
sage: RuleStar()._backward_format_output([1, 2, 3, 4, 5, 6], [4, 2, 4, 2, 3, 1], 'word')
|
|
2951
|
+
word: 424231
|
|
2952
|
+
"""
|
|
2953
|
+
if len(obj1) != len(obj2):
|
|
2954
|
+
raise ValueError(f"{obj1} and {obj2} are of different lengths")
|
|
2955
|
+
if output == 'array':
|
|
2956
|
+
return [obj1, obj2]
|
|
2957
|
+
elif output == 'word':
|
|
2958
|
+
if obj1 == list(range(1, len(obj1)+1)):
|
|
2959
|
+
from sage.combinat.words.word import Word
|
|
2960
|
+
return Word(obj2)
|
|
2961
|
+
else:
|
|
2962
|
+
raise TypeError("upper row must be standard")
|
|
2963
|
+
elif output == 'DecreasingHeckeFactorization':
|
|
2964
|
+
from sage.combinat.crystals.fully_commutative_stable_grothendieck import DecreasingHeckeFactorization
|
|
2965
|
+
obj1.reverse()
|
|
2966
|
+
obj2.reverse()
|
|
2967
|
+
df = []
|
|
2968
|
+
for j in range(len(obj1)):
|
|
2969
|
+
if j == 0:
|
|
2970
|
+
df.append([])
|
|
2971
|
+
if j > 0 and obj1[j] < obj1[j-1]:
|
|
2972
|
+
df.extend([] for _ in range(obj1[j-1]-obj1[j]))
|
|
2973
|
+
df[-1].append(obj2[j])
|
|
2974
|
+
if obj1:
|
|
2975
|
+
df.extend([] for a in range(obj1[-1]-1))
|
|
2976
|
+
# If biword is empty, return a decreasing factorization with 1 factor
|
|
2977
|
+
else:
|
|
2978
|
+
df.append([])
|
|
2979
|
+
return DecreasingHeckeFactorization(df)
|
|
2980
|
+
|
|
2981
|
+
|
|
2982
|
+
class InsertionRules:
|
|
2983
|
+
r"""
|
|
2984
|
+
Catalog of rules for RSK-like insertion algorithms.
|
|
2985
|
+
"""
|
|
2986
|
+
RSK = RuleRSK
|
|
2987
|
+
EG = RuleEG
|
|
2988
|
+
Hecke = RuleHecke
|
|
2989
|
+
dualRSK = RuleDualRSK
|
|
2990
|
+
coRSK = RuleCoRSK
|
|
2991
|
+
superRSK = RuleSuperRSK
|
|
2992
|
+
Star = RuleStar
|
|
2993
|
+
|
|
2994
|
+
#####################################################################
|
|
2995
|
+
|
|
2996
|
+
|
|
2997
|
+
def RSK(obj1=None, obj2=None, insertion=InsertionRules.RSK, check_standard=False, **options):
|
|
2998
|
+
r"""
|
|
2999
|
+
Perform the Robinson-Schensted-Knuth (RSK) correspondence.
|
|
3000
|
+
|
|
3001
|
+
The Robinson-Schensted-Knuth (RSK) correspondence (also known
|
|
3002
|
+
as the RSK algorithm) is most naturally stated as a bijection
|
|
3003
|
+
between generalized permutations (also known as two-line arrays,
|
|
3004
|
+
biwords, ...) and pairs of semi-standard Young tableaux `(P, Q)`
|
|
3005
|
+
of identical shape. The tableau `P` is known as the insertion
|
|
3006
|
+
tableau, and `Q` is known as the recording tableau.
|
|
3007
|
+
|
|
3008
|
+
The basic operation is known as row insertion `P \leftarrow k`
|
|
3009
|
+
(where `P` is a given semi-standard Young tableau, and `k` is an
|
|
3010
|
+
integer). Row insertion is a recursive algorithm which starts by
|
|
3011
|
+
setting `k_0 = k`, and in its `i`-th step inserts the number `k_i`
|
|
3012
|
+
into the `i`-th row of `P` (we start counting the rows at `0`) by
|
|
3013
|
+
replacing the first integer greater than `k_i` in the row by `k_i`
|
|
3014
|
+
and defines `k_{i+1}` as the integer that has been replaced. If no
|
|
3015
|
+
integer greater than `k_i` exists in the `i`-th row, then `k_i` is
|
|
3016
|
+
simply appended to the row and the algorithm terminates at this point.
|
|
3017
|
+
|
|
3018
|
+
A *generalized permutation* (or *biword*) is a list
|
|
3019
|
+
`((j_0, k_0), (j_1, k_1), \ldots, (j_{\ell-1}, k_{\ell-1}))`
|
|
3020
|
+
of pairs such that the letters `j_0, j_1, \ldots, j_{\ell-1}`
|
|
3021
|
+
are weakly increasing (that is,
|
|
3022
|
+
`j_0 \leq j_1 \leq \cdots \leq j_{\ell-1}`), whereas the letters
|
|
3023
|
+
`k_i` satisfy `k_i \leq k_{i+1}` whenever `j_i = j_{i+1}`.
|
|
3024
|
+
The `\ell`-tuple `(j_0, j_1, \ldots, j_{\ell-1})` is called the
|
|
3025
|
+
*top line* of this generalized permutation,
|
|
3026
|
+
whereas the `\ell`-tuple `(k_0, k_1, \ldots, k_{\ell-1})` is
|
|
3027
|
+
called its *bottom line*.
|
|
3028
|
+
|
|
3029
|
+
Now the RSK algorithm, applied to a generalized permutation
|
|
3030
|
+
`p = ((j_0, k_0), (j_1, k_1), \ldots, (j_{\ell-1}, k_{\ell-1}))`
|
|
3031
|
+
(encoded as a lexicographically sorted list of pairs) starts by
|
|
3032
|
+
initializing two semi-standard tableaux `P_0` and `Q_0` as empty
|
|
3033
|
+
tableaux. For each nonnegative integer `t` starting at `0`, take
|
|
3034
|
+
the pair `(j_t, k_t)` from `p` and set
|
|
3035
|
+
`P_{t+1} = P_t \leftarrow k_t`, and define `Q_{t+1}` by adding a
|
|
3036
|
+
new box filled with `j_t` to the tableau `Q_t` at the same
|
|
3037
|
+
location the row insertion on `P_t` ended (that is to say, adding
|
|
3038
|
+
a new box with entry `j_t` such that `P_{t+1}` and `Q_{t+1}` have
|
|
3039
|
+
the same shape). The iterative process stops when `t` reaches the
|
|
3040
|
+
size of `p`, and the pair `(P_t, Q_t)` at this point is the image
|
|
3041
|
+
of `p` under the Robinson-Schensted-Knuth correspondence.
|
|
3042
|
+
|
|
3043
|
+
This correspondence has been introduced in [Knu1970]_, where it has
|
|
3044
|
+
been referred to as "Construction A".
|
|
3045
|
+
|
|
3046
|
+
For more information, see Chapter 7 in [Sta-EC2]_.
|
|
3047
|
+
|
|
3048
|
+
We also note that integer matrices are in bijection with generalized
|
|
3049
|
+
permutations. Furthermore, we can convert any word `w` (and, in
|
|
3050
|
+
particular, any permutation) to a generalized permutation by
|
|
3051
|
+
considering the top row to be `(1, 2, \ldots, n)` where `n` is the
|
|
3052
|
+
length of `w`.
|
|
3053
|
+
|
|
3054
|
+
The optional argument ``insertion`` allows to specify an alternative
|
|
3055
|
+
insertion procedure to be used instead of the standard
|
|
3056
|
+
Robinson-Schensted-Knuth insertion.
|
|
3057
|
+
|
|
3058
|
+
INPUT:
|
|
3059
|
+
|
|
3060
|
+
- ``obj1``, ``obj2`` -- can be one of the following:
|
|
3061
|
+
|
|
3062
|
+
- a word in an ordered alphabet (in this case, ``obj1`` is said
|
|
3063
|
+
word, and ``obj2`` is ``None``)
|
|
3064
|
+
- an integer matrix
|
|
3065
|
+
- two lists of equal length representing a generalized permutation
|
|
3066
|
+
(namely, the lists `(j_0, j_1, \ldots, j_{\ell-1})` and
|
|
3067
|
+
`(k_0, k_1, \ldots, k_{\ell-1})` represent the generalized
|
|
3068
|
+
permutation
|
|
3069
|
+
`((j_0, k_0), (j_1, k_1), \ldots, (j_{\ell-1}, k_{\ell-1}))`)
|
|
3070
|
+
- any object which has a method ``_rsk_iter()`` which returns an
|
|
3071
|
+
iterator over the object represented as generalized permutation or
|
|
3072
|
+
a pair of lists (in this case, ``obj1`` is said object,
|
|
3073
|
+
and ``obj2`` is ``None``).
|
|
3074
|
+
|
|
3075
|
+
- ``insertion`` -- (default: ``RSK.rules.RSK``) the following types
|
|
3076
|
+
of insertion are currently supported:
|
|
3077
|
+
|
|
3078
|
+
- ``RSK.rules.RSK`` (or ``'RSK'``) -- Robinson-Schensted-Knuth
|
|
3079
|
+
insertion (:class:`~sage.combinat.rsk.RuleRSK`)
|
|
3080
|
+
- ``RSK.rules.EG`` (or ``'EG'``) -- Edelman-Greene insertion
|
|
3081
|
+
(only for reduced words of permutations/elements of a type `A`
|
|
3082
|
+
Coxeter group) (:class:`~sage.combinat.rsk.RuleEG`)
|
|
3083
|
+
- ``RSK.rules.Hecke`` -- (or ``'hecke'``) Hecke insertion (only
|
|
3084
|
+
guaranteed for generalized permutations whose top row is strictly
|
|
3085
|
+
increasing) (:class:`~sage.combinat.rsk.RuleHecke`)
|
|
3086
|
+
- ``RSK.rules.dualRSK`` (or ``'dualRSK'``) -- Dual RSK insertion
|
|
3087
|
+
(only for strict biwords) (:class:`~sage.combinat.rsk.RuleDualRSK`)
|
|
3088
|
+
- ``RSK.rules.coRSK`` (or ``'coRSK'``) -- CoRSK insertion (only
|
|
3089
|
+
for strict cobiwords) (:class:`~sage.combinat.rsk.RuleCoRSK`)
|
|
3090
|
+
- ``RSK.rules.superRSK`` (or ``'super'``) -- Super RSK insertion (only for
|
|
3091
|
+
restricted super biwords) (:class:`~sage.combinat.rsk.RuleSuperRSK`)
|
|
3092
|
+
- ``RSK.rules.Star`` (or ``'Star'``) -- `\star`-insertion (only for
|
|
3093
|
+
fully commutative words in the 0-Hecke monoid)
|
|
3094
|
+
(:class:`~sage.combinat.rsk.RuleStar`)
|
|
3095
|
+
|
|
3096
|
+
- ``check_standard`` -- boolean (default: ``False``); check if either of
|
|
3097
|
+
the resulting tableaux is a standard tableau, and if so, typecast it
|
|
3098
|
+
as such
|
|
3099
|
+
|
|
3100
|
+
For precise information about constraints on the input and output,
|
|
3101
|
+
as well as the definition of the algorithm (if it is not standard
|
|
3102
|
+
RSK), see the particular :class:`~sage.combinat.rsk.Rule` class.
|
|
3103
|
+
|
|
3104
|
+
EXAMPLES:
|
|
3105
|
+
|
|
3106
|
+
If we only input one row, it is understood that the top row
|
|
3107
|
+
should be `(1, 2, \ldots, n)`::
|
|
3108
|
+
|
|
3109
|
+
sage: RSK([3,3,2,4,1])
|
|
3110
|
+
[[[1, 3, 4], [2], [3]], [[1, 2, 4], [3], [5]]]
|
|
3111
|
+
sage: RSK(Word([3,3,2,4,1]))
|
|
3112
|
+
[[[1, 3, 4], [2], [3]], [[1, 2, 4], [3], [5]]]
|
|
3113
|
+
sage: RSK(Word([2,3,3,2,1,3,2,3]))
|
|
3114
|
+
[[[1, 2, 2, 3, 3], [2, 3], [3]], [[1, 2, 3, 6, 8], [4, 7], [5]]]
|
|
3115
|
+
|
|
3116
|
+
We can provide a generalized permutation::
|
|
3117
|
+
|
|
3118
|
+
sage: RSK([1, 2, 2, 2], [2, 1, 1, 2])
|
|
3119
|
+
[[[1, 1, 2], [2]], [[1, 2, 2], [2]]]
|
|
3120
|
+
sage: RSK(Word([1,1,3,4,4]), [1,4,2,1,3])
|
|
3121
|
+
[[[1, 1, 3], [2], [4]], [[1, 1, 4], [3], [4]]]
|
|
3122
|
+
sage: RSK([1,3,3,4,4], Word([6,2,2,1,7]))
|
|
3123
|
+
[[[1, 2, 7], [2], [6]], [[1, 3, 4], [3], [4]]]
|
|
3124
|
+
|
|
3125
|
+
We can provide a matrix::
|
|
3126
|
+
|
|
3127
|
+
sage: RSK(matrix([[0,1],[2,1]]))
|
|
3128
|
+
[[[1, 1, 2], [2]], [[1, 2, 2], [2]]]
|
|
3129
|
+
|
|
3130
|
+
We can also provide something looking like a matrix::
|
|
3131
|
+
|
|
3132
|
+
sage: RSK([[0,1],[2,1]])
|
|
3133
|
+
[[[1, 1, 2], [2]], [[1, 2, 2], [2]]]
|
|
3134
|
+
|
|
3135
|
+
There is also :func:`~sage.combinat.rsk.RSK_inverse` which performs
|
|
3136
|
+
the inverse of the bijection on a pair of semistandard tableaux. We
|
|
3137
|
+
note that the inverse function takes 2 separate tableaux as inputs, so
|
|
3138
|
+
to compose with :func:`~sage.combinat.rsk.RSK`, we need to use the
|
|
3139
|
+
python ``*`` on the output::
|
|
3140
|
+
|
|
3141
|
+
sage: RSK_inverse(*RSK([1, 2, 2, 2], [2, 1, 1, 2]))
|
|
3142
|
+
[[1, 2, 2, 2], [2, 1, 1, 2]]
|
|
3143
|
+
sage: P,Q = RSK([1, 2, 2, 2], [2, 1, 1, 2])
|
|
3144
|
+
sage: RSK_inverse(P, Q)
|
|
3145
|
+
[[1, 2, 2, 2], [2, 1, 1, 2]]
|
|
3146
|
+
|
|
3147
|
+
TESTS:
|
|
3148
|
+
|
|
3149
|
+
Empty objects::
|
|
3150
|
+
|
|
3151
|
+
sage: RSK(Permutation([]))
|
|
3152
|
+
[[], []]
|
|
3153
|
+
sage: RSK(Word([]))
|
|
3154
|
+
[[], []]
|
|
3155
|
+
sage: RSK(matrix([[]]))
|
|
3156
|
+
[[], []]
|
|
3157
|
+
sage: RSK([], [])
|
|
3158
|
+
[[], []]
|
|
3159
|
+
sage: RSK([[]])
|
|
3160
|
+
[[], []]
|
|
3161
|
+
sage: RSK(Word([]), insertion=RSK.rules.EG)
|
|
3162
|
+
[[], []]
|
|
3163
|
+
sage: RSK(Word([]), insertion=RSK.rules.Hecke)
|
|
3164
|
+
[[], []]
|
|
3165
|
+
"""
|
|
3166
|
+
if isinstance(insertion, str):
|
|
3167
|
+
if insertion == 'RSK':
|
|
3168
|
+
insertion = RSK.rules.RSK
|
|
3169
|
+
elif insertion == 'EG':
|
|
3170
|
+
insertion = RSK.rules.EG
|
|
3171
|
+
elif insertion == 'hecke':
|
|
3172
|
+
insertion = RSK.rules.Hecke
|
|
3173
|
+
elif insertion == 'dualRSK':
|
|
3174
|
+
insertion = RSK.rules.dualRSK
|
|
3175
|
+
elif insertion == 'coRSK':
|
|
3176
|
+
insertion = RSK.rules.coRSK
|
|
3177
|
+
elif insertion == 'superRSK':
|
|
3178
|
+
insertion = RSK.rules.superRSK
|
|
3179
|
+
elif insertion == 'Star':
|
|
3180
|
+
insertion = RSK.rules.Star
|
|
3181
|
+
else:
|
|
3182
|
+
raise ValueError("invalid input")
|
|
3183
|
+
|
|
3184
|
+
rule = insertion()
|
|
3185
|
+
if not isinstance(rule, Rule):
|
|
3186
|
+
raise TypeError("the insertion must be an instance of Rule")
|
|
3187
|
+
|
|
3188
|
+
if obj1 is None and obj2 is None:
|
|
3189
|
+
if 'matrix' in options:
|
|
3190
|
+
obj1 = matrix(options['matrix'])
|
|
3191
|
+
else:
|
|
3192
|
+
raise ValueError("invalid input")
|
|
3193
|
+
|
|
3194
|
+
if isinstance(obj1, Matrix):
|
|
3195
|
+
obj1 = obj1.rows()
|
|
3196
|
+
|
|
3197
|
+
output = rule.forward_rule(obj1, obj2, check_standard)
|
|
3198
|
+
return output
|
|
3199
|
+
|
|
3200
|
+
|
|
3201
|
+
robinson_schensted_knuth = RSK
|
|
3202
|
+
RSK.rules = InsertionRules
|
|
3203
|
+
|
|
3204
|
+
|
|
3205
|
+
def RSK_inverse(p, q, output='array', insertion=InsertionRules.RSK):
|
|
3206
|
+
r"""
|
|
3207
|
+
Return the generalized permutation corresponding to the pair of
|
|
3208
|
+
tableaux `(p, q)` under the inverse of the Robinson-Schensted-Knuth
|
|
3209
|
+
correspondence.
|
|
3210
|
+
|
|
3211
|
+
For more information on the bijection, see :func:`RSK`.
|
|
3212
|
+
|
|
3213
|
+
INPUT:
|
|
3214
|
+
|
|
3215
|
+
- ``p``, ``q`` -- two semi-standard tableaux of the same shape, or
|
|
3216
|
+
(in the case when Hecke insertion is used) an increasing tableau and
|
|
3217
|
+
a set-valued tableau of the same shape (see the note below for the
|
|
3218
|
+
format of the set-valued tableau)
|
|
3219
|
+
|
|
3220
|
+
- ``output`` -- (default: ``'array'``) if ``q`` is semi-standard:
|
|
3221
|
+
|
|
3222
|
+
- ``'array'`` -- as a two-line array (i.e. generalized permutation or
|
|
3223
|
+
biword)
|
|
3224
|
+
- ``'matrix'`` -- as an integer matrix
|
|
3225
|
+
|
|
3226
|
+
and if ``q`` is standard, we can also have the output:
|
|
3227
|
+
|
|
3228
|
+
- ``'word'`` -- as a word
|
|
3229
|
+
|
|
3230
|
+
and additionally if ``p`` is standard, we can also have the output:
|
|
3231
|
+
|
|
3232
|
+
- ``'permutation'`` -- as a permutation
|
|
3233
|
+
|
|
3234
|
+
- ``insertion`` -- (default: ``RSK.rules.RSK``) the insertion algorithm
|
|
3235
|
+
used in the bijection. Currently the following are supported:
|
|
3236
|
+
|
|
3237
|
+
- ``RSK.rules.RSK`` (or ``'RSK'``) -- Robinson-Schensted-Knuth
|
|
3238
|
+
insertion (:class:`~sage.combinat.rsk.RuleRSK`)
|
|
3239
|
+
- ``RSK.rules.EG`` (or ``'EG'``) -- Edelman-Greene insertion
|
|
3240
|
+
(only for reduced words of permutations/elements of a type `A`
|
|
3241
|
+
Coxeter group) (:class:`~sage.combinat.rsk.RuleEG`)
|
|
3242
|
+
- ``RSK.rules.Hecke`` (or ``'hecke'``) -- Hecke insertion (only
|
|
3243
|
+
guaranteed for generalized permutations whose top row is strictly
|
|
3244
|
+
increasing) (:class:`~sage.combinat.rsk.RuleHecke`)
|
|
3245
|
+
- ``RSK.rules.dualRSK`` (or ``'dualRSK'``) -- Dual RSK insertion
|
|
3246
|
+
(only for strict biwords) (:class:`~sage.combinat.rsk.RuleDualRSK`)
|
|
3247
|
+
- ``RSK.rules.coRSK`` (or ``'coRSK'``) -- CoRSK insertion (only
|
|
3248
|
+
for strict cobiwords) (:class:`~sage.combinat.rsk.RuleCoRSK`)
|
|
3249
|
+
- ``RSK.rules.superRSK`` (or ``'super'``) -- Super RSK insertion (only for
|
|
3250
|
+
restricted super biwords) (:class:`~sage.combinat.rsk.RuleSuperRSK`)
|
|
3251
|
+
- ``RSK.rules.Star`` (or ``'Star'``) -- `\star`-insertion (only for
|
|
3252
|
+
fully commutative words in the 0-Hecke monoid)
|
|
3253
|
+
(:class:`~sage.combinat.rsk.RuleStar`)
|
|
3254
|
+
|
|
3255
|
+
For precise information about constraints on the input and
|
|
3256
|
+
output, see the particular :class:`~sage.combinat.rsk.Rule` class.
|
|
3257
|
+
|
|
3258
|
+
.. NOTE::
|
|
3259
|
+
|
|
3260
|
+
In the case of Hecke insertion, the input variable ``q`` should
|
|
3261
|
+
be a set-valued tableau, encoded as a tableau whose entries are
|
|
3262
|
+
strictly increasing tuples of positive integers. Each such tuple
|
|
3263
|
+
encodes the set of its entries.
|
|
3264
|
+
|
|
3265
|
+
EXAMPLES:
|
|
3266
|
+
|
|
3267
|
+
If both ``p`` and ``q`` are standard::
|
|
3268
|
+
|
|
3269
|
+
sage: t1 = Tableau([[1, 2, 5], [3], [4]])
|
|
3270
|
+
sage: t2 = Tableau([[1, 2, 3], [4], [5]])
|
|
3271
|
+
sage: RSK_inverse(t1, t2)
|
|
3272
|
+
[[1, 2, 3, 4, 5], [1, 4, 5, 3, 2]]
|
|
3273
|
+
sage: RSK_inverse(t1, t2, 'word')
|
|
3274
|
+
word: 14532
|
|
3275
|
+
sage: RSK_inverse(t1, t2, 'matrix')
|
|
3276
|
+
[1 0 0 0 0]
|
|
3277
|
+
[0 0 0 1 0]
|
|
3278
|
+
[0 0 0 0 1]
|
|
3279
|
+
[0 0 1 0 0]
|
|
3280
|
+
[0 1 0 0 0]
|
|
3281
|
+
sage: RSK_inverse(t1, t2, 'permutation')
|
|
3282
|
+
[1, 4, 5, 3, 2]
|
|
3283
|
+
sage: RSK_inverse(t1, t1, 'permutation')
|
|
3284
|
+
[1, 4, 3, 2, 5]
|
|
3285
|
+
sage: RSK_inverse(t2, t2, 'permutation')
|
|
3286
|
+
[1, 2, 5, 4, 3]
|
|
3287
|
+
sage: RSK_inverse(t2, t1, 'permutation')
|
|
3288
|
+
[1, 5, 4, 2, 3]
|
|
3289
|
+
|
|
3290
|
+
If the first tableau is semistandard::
|
|
3291
|
+
|
|
3292
|
+
sage: p = Tableau([[1,2,2],[3]]); q = Tableau([[1,2,4],[3]])
|
|
3293
|
+
sage: ret = RSK_inverse(p, q); ret
|
|
3294
|
+
[[1, 2, 3, 4], [1, 3, 2, 2]]
|
|
3295
|
+
sage: RSK_inverse(p, q, 'word')
|
|
3296
|
+
word: 1322
|
|
3297
|
+
|
|
3298
|
+
In general::
|
|
3299
|
+
|
|
3300
|
+
sage: p = Tableau([[1,2,2],[2]]); q = Tableau([[1,3,3],[2]])
|
|
3301
|
+
sage: RSK_inverse(p, q)
|
|
3302
|
+
[[1, 2, 3, 3], [2, 1, 2, 2]]
|
|
3303
|
+
sage: RSK_inverse(p, q, 'matrix')
|
|
3304
|
+
[0 1]
|
|
3305
|
+
[1 0]
|
|
3306
|
+
[0 2]
|
|
3307
|
+
|
|
3308
|
+
Using Hecke insertion::
|
|
3309
|
+
|
|
3310
|
+
sage: w = [5, 4, 3, 1, 4, 2, 5, 5]
|
|
3311
|
+
sage: pq = RSK(w, insertion=RSK.rules.Hecke)
|
|
3312
|
+
sage: RSK_inverse(*pq, insertion=RSK.rules.Hecke, output='list')
|
|
3313
|
+
[5, 4, 3, 1, 4, 2, 5, 5]
|
|
3314
|
+
|
|
3315
|
+
.. NOTE::
|
|
3316
|
+
|
|
3317
|
+
The constructor of ``Tableau`` accepts not only semistandard
|
|
3318
|
+
tableaux, but also arbitrary lists that are fillings of a
|
|
3319
|
+
partition diagram. (And such lists are used, e.g., for the
|
|
3320
|
+
set-valued tableau ``q`` that is passed to
|
|
3321
|
+
``RSK_inverse(p, q, insertion='hecke')``.)
|
|
3322
|
+
The user is responsible for ensuring that the tableaux passed to
|
|
3323
|
+
``RSK_inverse`` are of the right types (semistandard, standard,
|
|
3324
|
+
increasing, set-valued as needed).
|
|
3325
|
+
|
|
3326
|
+
TESTS:
|
|
3327
|
+
|
|
3328
|
+
From empty tableaux::
|
|
3329
|
+
|
|
3330
|
+
sage: RSK_inverse(Tableau([]), Tableau([]))
|
|
3331
|
+
[[], []]
|
|
3332
|
+
|
|
3333
|
+
Check that :func:`RSK_inverse` is the inverse of :func:`RSK` on the
|
|
3334
|
+
different types of inputs/outputs::
|
|
3335
|
+
|
|
3336
|
+
sage: f = lambda p: RSK_inverse(*RSK(p), output='permutation')
|
|
3337
|
+
sage: all(p == f(p) for n in range(7) for p in Permutations(n))
|
|
3338
|
+
True
|
|
3339
|
+
sage: all(RSK_inverse(*RSK(w), output='word') == w for n in range(4)
|
|
3340
|
+
....: for w in Words(5, n))
|
|
3341
|
+
True
|
|
3342
|
+
sage: from sage.combinat.integer_matrices import IntegerMatrices
|
|
3343
|
+
sage: M = IntegerMatrices([1,2,2,1], [3,1,1,1])
|
|
3344
|
+
sage: all(RSK_inverse(*RSK(m), output='matrix') == m for m in M)
|
|
3345
|
+
True
|
|
3346
|
+
|
|
3347
|
+
sage: n = ZZ.random_element(200)
|
|
3348
|
+
sage: p = Permutations(n).random_element()
|
|
3349
|
+
sage: is_fine = True if p == f(p) else p ; is_fine
|
|
3350
|
+
True
|
|
3351
|
+
|
|
3352
|
+
Both tableaux must be of the same shape::
|
|
3353
|
+
|
|
3354
|
+
sage: RSK_inverse(Tableau([[1,2,3]]), Tableau([[1,2]]))
|
|
3355
|
+
Traceback (most recent call last):
|
|
3356
|
+
...
|
|
3357
|
+
ValueError: p(=[[1, 2, 3]]) and q(=[[1, 2]]) must have the same shape
|
|
3358
|
+
|
|
3359
|
+
Check that :issue:`20430` is fixed::
|
|
3360
|
+
|
|
3361
|
+
sage: RSK([1,1,1,1,1,1,1,2,2,2,3], [1,1,1,1,1,1,3,2,2,2,1])
|
|
3362
|
+
[[[1, 1, 1, 1, 1, 1, 1, 2, 2], [2], [3]],
|
|
3363
|
+
[[1, 1, 1, 1, 1, 1, 1, 2, 2], [2], [3]]]
|
|
3364
|
+
sage: t = SemistandardTableau([[1, 1, 1, 1, 1, 1, 1, 2, 2], [2], [3]])
|
|
3365
|
+
sage: RSK_inverse(t, t, 'array')
|
|
3366
|
+
[[1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3],
|
|
3367
|
+
[1, 1, 1, 1, 1, 1, 3, 2, 2, 2, 1]]
|
|
3368
|
+
"""
|
|
3369
|
+
if isinstance(insertion, str):
|
|
3370
|
+
if insertion == 'RSK':
|
|
3371
|
+
insertion = RSK.rules.RSK
|
|
3372
|
+
elif insertion == 'EG':
|
|
3373
|
+
insertion = RSK.rules.EG
|
|
3374
|
+
elif insertion == 'hecke':
|
|
3375
|
+
insertion = RSK.rules.Hecke
|
|
3376
|
+
elif insertion == 'dualRSK':
|
|
3377
|
+
insertion = RSK.rules.dualRSK
|
|
3378
|
+
elif insertion == 'coRSK':
|
|
3379
|
+
insertion = RSK.rules.coRSK
|
|
3380
|
+
elif insertion == 'superRSK':
|
|
3381
|
+
insertion = RSK.rules.superRSK
|
|
3382
|
+
elif insertion == 'Star':
|
|
3383
|
+
insertion = RSK.rules.Star
|
|
3384
|
+
else:
|
|
3385
|
+
raise ValueError("invalid input")
|
|
3386
|
+
|
|
3387
|
+
rule = insertion()
|
|
3388
|
+
if not isinstance(rule, Rule):
|
|
3389
|
+
raise TypeError("the insertion must be an instance of Rule")
|
|
3390
|
+
|
|
3391
|
+
if p.shape() != q.shape():
|
|
3392
|
+
raise ValueError(f"p(={p}) and q(={q}) must have the same shape")
|
|
3393
|
+
|
|
3394
|
+
answer = rule.backward_rule(p, q, output)
|
|
3395
|
+
return answer
|
|
3396
|
+
|
|
3397
|
+
|
|
3398
|
+
robinson_schensted_knuth_inverse = RSK_inverse
|
|
3399
|
+
|
|
3400
|
+
|
|
3401
|
+
def to_matrix(t, b):
|
|
3402
|
+
r"""
|
|
3403
|
+
Return the integer matrix corresponding to a two-line array.
|
|
3404
|
+
|
|
3405
|
+
INPUT:
|
|
3406
|
+
|
|
3407
|
+
- ``t`` -- the top row of the array
|
|
3408
|
+
- ``b`` -- the bottom row of the array
|
|
3409
|
+
|
|
3410
|
+
OUTPUT:
|
|
3411
|
+
|
|
3412
|
+
An `m \times n`-matrix (where `m` and `n` are the maximum entries in
|
|
3413
|
+
`t` and `b` respectively) whose `(i, j)`-th entry, for any `i` and `j`,
|
|
3414
|
+
is the number of all positions `k` satisfying `t_k = i` and `b_k = j`.
|
|
3415
|
+
|
|
3416
|
+
EXAMPLES::
|
|
3417
|
+
|
|
3418
|
+
sage: from sage.combinat.rsk import to_matrix
|
|
3419
|
+
sage: to_matrix([1, 1, 3, 3, 4], [2, 3, 1, 1, 3])
|
|
3420
|
+
[0 1 1]
|
|
3421
|
+
[0 0 0]
|
|
3422
|
+
[2 0 0]
|
|
3423
|
+
[0 0 1]
|
|
3424
|
+
"""
|
|
3425
|
+
n = len(b)
|
|
3426
|
+
if len(t) != n:
|
|
3427
|
+
raise ValueError("the two arrays must be the same length")
|
|
3428
|
+
|
|
3429
|
+
# Build the dictionary of entries since the matrix
|
|
3430
|
+
# is typically (very) sparse
|
|
3431
|
+
entries = {}
|
|
3432
|
+
for i in range(n):
|
|
3433
|
+
pos = (t[i]-1, b[i]-1)
|
|
3434
|
+
if pos in entries:
|
|
3435
|
+
entries[pos] += 1
|
|
3436
|
+
else:
|
|
3437
|
+
entries[pos] = 1
|
|
3438
|
+
return matrix(entries, sparse=True)
|