passagemath-combinat 10.6.42__cp314-cp314-musllinux_1_2_x86_64.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/METADATA +160 -0
- passagemath_combinat-10.6.42.dist-info/RECORD +400 -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-0e7fc84e.so.10.5.0 +0 -0
- passagemath_combinat.libs/libsymmetrica-81fe8739.so.3.0.0 +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 +35 -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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/debruijn_sequence.pyx +355 -0
- sage/combinat/decorated_permutation.py +270 -0
- sage/combinat/degree_sequences.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/expnums.pyx +148 -0
- sage/combinat/fast_vector_partitions.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/words/word_char.pyx +847 -0
- sage/combinat/words/word_datatypes.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +0 -0
- sage/sat/solvers/satsolver.pxd +3 -0
- sage/sat/solvers/satsolver.pyx +405 -0
sage/combinat/growth.py
ADDED
|
@@ -0,0 +1,4352 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-combinat
|
|
2
|
+
# sage.doctest: needs sage.combinat sage.graphs
|
|
3
|
+
r"""
|
|
4
|
+
Growth diagrams and dual graded graphs
|
|
5
|
+
|
|
6
|
+
AUTHORS:
|
|
7
|
+
|
|
8
|
+
- Martin Rubey (2016-09): Initial version
|
|
9
|
+
- Martin Rubey (2017-09): generalize, more rules, improve documentation
|
|
10
|
+
- Travis Scrimshaw (2017-09): switch to rule-based framework
|
|
11
|
+
|
|
12
|
+
.. TODO::
|
|
13
|
+
|
|
14
|
+
- provide examples for the P and Q-symbol in the skew case
|
|
15
|
+
- implement a method providing a visualization of the growth
|
|
16
|
+
diagram with all labels, perhaps as LaTeX code
|
|
17
|
+
- when shape is given, check that it is compatible with filling
|
|
18
|
+
or labels
|
|
19
|
+
- optimize rules, mainly for :class:`RuleRSK` and
|
|
20
|
+
:class:`RuleBurge`
|
|
21
|
+
- implement backward rules for :class:`GrowthDiagram.rules.Domino`
|
|
22
|
+
- implement backward rule from [LLMSSZ2013]_, [LS2007]_
|
|
23
|
+
- make semistandard extension generic
|
|
24
|
+
- accommodate dual filtered graphs
|
|
25
|
+
|
|
26
|
+
A guided tour
|
|
27
|
+
=============
|
|
28
|
+
|
|
29
|
+
Growth diagrams, invented by Sergey Fomin [Fom1994]_, [Fom1995]_,
|
|
30
|
+
provide a vast generalization of the Robinson-Schensted-Knuth (RSK)
|
|
31
|
+
correspondence between matrices with nonnegative integer entries and
|
|
32
|
+
pairs of semistandard Young tableaux of the same shape.
|
|
33
|
+
|
|
34
|
+
The main fact is that many correspondences similar to RSK can be
|
|
35
|
+
defined by providing a pair of so-called local rules: a 'forward'
|
|
36
|
+
rule, whose input are three vertices `y`, `t` and `x` of a certain
|
|
37
|
+
directed graph (in the case of Robinson-Schensted: the directed graph
|
|
38
|
+
corresponding to Young's lattice) and an integer (in the case of
|
|
39
|
+
Robinson-Schensted: `0` or `1`), and whose output is a fourth vertex
|
|
40
|
+
`z`. This rule should be invertible in the following sense: there is
|
|
41
|
+
a so-called 'backward' rule that recovers the integer and `t` given
|
|
42
|
+
`y`, `z` and `x`.
|
|
43
|
+
|
|
44
|
+
As an example, the growth rules for the classical RSK correspondence
|
|
45
|
+
are provided by :class:`RuleRSK`. To produce a growth diagram, pass
|
|
46
|
+
the desired rule and a permutation to :class:`GrowthDiagram`::
|
|
47
|
+
|
|
48
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
49
|
+
sage: w = [2,3,6,1,4,5]; G = GrowthDiagram(RuleRSK, w); G
|
|
50
|
+
0 0 0 1 0 0
|
|
51
|
+
1 0 0 0 0 0
|
|
52
|
+
0 1 0 0 0 0
|
|
53
|
+
0 0 0 0 1 0
|
|
54
|
+
0 0 0 0 0 1
|
|
55
|
+
0 0 1 0 0 0
|
|
56
|
+
|
|
57
|
+
The forward rule just mentioned assigns 49 partitions to the corners
|
|
58
|
+
of each of the 36 cells of this matrix (i.e., 49 the vertices of a
|
|
59
|
+
`(6+1) \times (6+1)` grid graph), with the exception of the corners
|
|
60
|
+
on the left and top boundary, which are initialized with the empty
|
|
61
|
+
partition. More precisely, for each cell, the
|
|
62
|
+
:meth:`~sage.combinat.growth.RuleRSK.forward_rule` computes the
|
|
63
|
+
partition `z` labelling the lower right corner, given the content `c`
|
|
64
|
+
of a cell and the other three partitions::
|
|
65
|
+
|
|
66
|
+
t --- x
|
|
67
|
+
| c |
|
|
68
|
+
y --- z
|
|
69
|
+
|
|
70
|
+
.. WARNING::
|
|
71
|
+
|
|
72
|
+
Note that a growth diagram is printed with matrix coordinates,
|
|
73
|
+
the origin being in the top-left corner. Therefore, the growth
|
|
74
|
+
is from the top left to the bottom right!
|
|
75
|
+
|
|
76
|
+
The partitions along the boundary opposite of the origin, reading
|
|
77
|
+
from the bottom left to the top right, are obtained by using the
|
|
78
|
+
method :meth:`~sage.combinat.growth.GrowthDiagram.out_labels`::
|
|
79
|
+
|
|
80
|
+
sage: G.out_labels()
|
|
81
|
+
[[],
|
|
82
|
+
[1],
|
|
83
|
+
[2],
|
|
84
|
+
[3],
|
|
85
|
+
[3, 1],
|
|
86
|
+
[3, 2],
|
|
87
|
+
[4, 2],
|
|
88
|
+
[4, 1],
|
|
89
|
+
[3, 1],
|
|
90
|
+
[2, 1],
|
|
91
|
+
[1, 1],
|
|
92
|
+
[1],
|
|
93
|
+
[]]
|
|
94
|
+
|
|
95
|
+
However, in the case of a rectangular filling, it is more practical
|
|
96
|
+
to split this sequence of labels in two. Interpreting the sequence
|
|
97
|
+
of partitions along the right boundary as a standard Young tableau,
|
|
98
|
+
we then obtain the so-called
|
|
99
|
+
:meth:`~sage.combinat.growth.RulePartitions.P_symbol`, the partitions
|
|
100
|
+
along the bottom boundary yield the so-called
|
|
101
|
+
:meth:`~sage.combinat.growth.RulePartitions.Q_symbol`. These
|
|
102
|
+
coincide with the output of the classical
|
|
103
|
+
:func:`~sage.combinat.rsk.RSK` insertion algorithm::
|
|
104
|
+
|
|
105
|
+
sage: ascii_art([G.P_symbol(), G.Q_symbol()])
|
|
106
|
+
[ 1 3 4 5 1 2 3 6 ]
|
|
107
|
+
[ 2 6 , 4 5 ]
|
|
108
|
+
sage: ascii_art(RSK(w))
|
|
109
|
+
[ 1 3 4 5 1 2 3 6 ]
|
|
110
|
+
[ 2 6 , 4 5 ]
|
|
111
|
+
|
|
112
|
+
The filling can be recovered knowing the partitions labelling the
|
|
113
|
+
corners of the bottom and the right boundary alone, by repeatedly
|
|
114
|
+
applying the :meth:`~sage.combinat.growth.RuleRSK.backward_rule`.
|
|
115
|
+
Therefore, to initialize a :class:`GrowthDiagram`, we can provide
|
|
116
|
+
these labels instead of the filling::
|
|
117
|
+
|
|
118
|
+
sage: GrowthDiagram(RuleRSK, labels=G.out_labels())
|
|
119
|
+
0 0 0 1 0 0
|
|
120
|
+
1 0 0 0 0 0
|
|
121
|
+
0 1 0 0 0 0
|
|
122
|
+
0 0 0 0 1 0
|
|
123
|
+
0 0 0 0 0 1
|
|
124
|
+
0 0 1 0 0 0
|
|
125
|
+
|
|
126
|
+
Invocation
|
|
127
|
+
----------
|
|
128
|
+
|
|
129
|
+
In general, growth diagrams are defined for `0-1`-fillings of
|
|
130
|
+
arbitrary skew shapes. In the case of the Robinson-Schensted-Knuth
|
|
131
|
+
correspondence, even arbitrary nonnegative integers are allowed. In
|
|
132
|
+
other cases, entries may be either zero or an `r`-th root of unity -
|
|
133
|
+
for example, :class:`~sage.combinat.growth.RuleDomino` insertion is
|
|
134
|
+
defined for signed permutations, that is, `r=2`. Traditionally, words
|
|
135
|
+
and permutations are also used to specify a filling in special cases.
|
|
136
|
+
|
|
137
|
+
To accommodate all this, the filling may be passed in various ways.
|
|
138
|
+
The most general possibility is to pass a dictionary of coordinates
|
|
139
|
+
to (signed) entries, where zeros can be omitted. In this case, when
|
|
140
|
+
the parameter ``shape`` is not explicitly specified, it is assumed
|
|
141
|
+
to be the minimal rectangle containing the origin and all coordinates
|
|
142
|
+
with nonzero entries.
|
|
143
|
+
|
|
144
|
+
For example, consider the following generalized permutation::
|
|
145
|
+
|
|
146
|
+
1 2 2 2 4 4
|
|
147
|
+
4 2 3 3 2 3
|
|
148
|
+
|
|
149
|
+
that we encode as the dictionary::
|
|
150
|
+
|
|
151
|
+
sage: P = {(1-1,4-1): 1, (2-1,2-1): 1, (2-1,3-1): 2, (4-1,2-1): 1, (4-1,3-1): 1}
|
|
152
|
+
|
|
153
|
+
Note that we are subtracting `1` from all entries because of
|
|
154
|
+
zero-based indexing, we obtain::
|
|
155
|
+
|
|
156
|
+
sage: GrowthDiagram(RuleRSK, P)
|
|
157
|
+
0 0 0 0
|
|
158
|
+
0 1 0 1
|
|
159
|
+
0 2 0 1
|
|
160
|
+
1 0 0 0
|
|
161
|
+
|
|
162
|
+
Alternatively, we could create the same growth diagram using a
|
|
163
|
+
matrix.
|
|
164
|
+
|
|
165
|
+
Let us also mention that one can pass the arguments specifying
|
|
166
|
+
a growth diagram directly to the rule::
|
|
167
|
+
|
|
168
|
+
sage: RuleRSK(P)
|
|
169
|
+
0 0 0 0
|
|
170
|
+
0 1 0 1
|
|
171
|
+
0 2 0 1
|
|
172
|
+
1 0 0 0
|
|
173
|
+
|
|
174
|
+
In contrast to the classical insertion algorithms, growth diagrams
|
|
175
|
+
immediately generalize to fillings whose shape is an arbitrary skew
|
|
176
|
+
partition::
|
|
177
|
+
|
|
178
|
+
sage: GrowthDiagram(RuleRSK, [3,1,2], shape=SkewPartition([[3,3,2],[1,1]]))
|
|
179
|
+
. 1 0
|
|
180
|
+
. 0 1
|
|
181
|
+
1 0
|
|
182
|
+
|
|
183
|
+
As an important example, consider the Stanley-Sundaram correspondence
|
|
184
|
+
between oscillating tableaux and (partial) perfect matchings.
|
|
185
|
+
Perfect matchings of `\{1, \ldots, 2r\}` are in bijection with
|
|
186
|
+
`0-1`-fillings of a triangular shape with `2r-1` rows, such that for
|
|
187
|
+
each `k` there is either exactly one nonzero entry in row `k` or
|
|
188
|
+
exactly one nonzero entry in column `2r-k`. Explicitly, if `(i,j)`
|
|
189
|
+
is a pair in the perfect matching, the entry in column `i-1` and row
|
|
190
|
+
`2r-j` equals `1`. For example::
|
|
191
|
+
|
|
192
|
+
sage: m = [[1,5],[3,4],[2,7],[6,8]]
|
|
193
|
+
sage: G = RuleRSK({(i-1, 8-j): 1 for i,j in m}, shape=[7,6,5,4,3,2,1]); G
|
|
194
|
+
0 0 0 0 0 1 0
|
|
195
|
+
0 1 0 0 0 0
|
|
196
|
+
0 0 0 0 0
|
|
197
|
+
1 0 0 0
|
|
198
|
+
0 0 1
|
|
199
|
+
0 0
|
|
200
|
+
0
|
|
201
|
+
|
|
202
|
+
The partitions labelling the bottom-right corners along the boundary
|
|
203
|
+
opposite of the origin then form a so-called oscillating tableau -
|
|
204
|
+
the remaining partitions along the bottom-right boundary are
|
|
205
|
+
redundant::
|
|
206
|
+
|
|
207
|
+
sage: G.out_labels()[1::2]
|
|
208
|
+
[[1], [1, 1], [2, 1], [1, 1], [1], [1, 1], [1]]
|
|
209
|
+
|
|
210
|
+
Another great advantage of growth diagrams is that we immediately
|
|
211
|
+
have access to a skew version of the correspondence, by providing
|
|
212
|
+
different initialization for the labels on the side of the origin.
|
|
213
|
+
We reproduce the original example of Bruce Sagan and Richard Stanley,
|
|
214
|
+
see also Tom Roby's thesis [Rob1991]_::
|
|
215
|
+
|
|
216
|
+
sage: w = {(1-1,4-1): 1, (2-1,2-1): 1, (4-1,3-1): 1}
|
|
217
|
+
sage: T = SkewTableau([[None, None], [None, 5], [1]])
|
|
218
|
+
sage: U = SkewTableau([[None, None], [None, 3], [5]])
|
|
219
|
+
sage: labels = T.to_chain()[::-1] + U.to_chain()[1:]
|
|
220
|
+
sage: G = GrowthDiagram(RuleRSK, filling=w, shape=[5,5,5,5,5], labels=labels); G
|
|
221
|
+
0 0 0 0 0
|
|
222
|
+
0 1 0 0 0
|
|
223
|
+
0 0 0 1 0
|
|
224
|
+
1 0 0 0 0
|
|
225
|
+
0 0 0 0 0
|
|
226
|
+
sage: ascii_art([G.P_symbol(), G.Q_symbol()])
|
|
227
|
+
[ . . 2 3 . . 1 4 ]
|
|
228
|
+
[ . . . . ]
|
|
229
|
+
[ . 4 . 2 ]
|
|
230
|
+
[ 1 3 ]
|
|
231
|
+
[ 5 , 5 ]
|
|
232
|
+
|
|
233
|
+
Similarly, there is a correspondence for skew oscillating tableau.
|
|
234
|
+
Let us conclude by reproducing Example 4.2.6 from [Rob1991]_. The
|
|
235
|
+
oscillating tableau, as given, is::
|
|
236
|
+
|
|
237
|
+
sage: o = [[2,1],[2,2],[3,2],[4,2],[4,1],[4,1,1],[3,1,1],[3,1],[3,2],[3,1],[2,1]]
|
|
238
|
+
|
|
239
|
+
From this, we have to construct the list of labels of the corners
|
|
240
|
+
along the bottom-right boundary. The labels with odd indices are
|
|
241
|
+
given by the oscillating tableau, the other labels are obtained by
|
|
242
|
+
taking the smaller of the two neighbouring partitions::
|
|
243
|
+
|
|
244
|
+
sage: l = [o[i//2] if is_even(i) else min(o[(i-1)//2],o[(i+1)//2])
|
|
245
|
+
....: for i in range(2*len(o)-1)]
|
|
246
|
+
sage: la = list(range(len(o)-2, 0, -1))
|
|
247
|
+
sage: G = RuleRSK(labels=l[1:-1], shape=la); G
|
|
248
|
+
0 0 0 0 0 0 0 1 0
|
|
249
|
+
0 1 0 0 0 0 0 0
|
|
250
|
+
0 0 0 0 0 0 0
|
|
251
|
+
0 0 0 0 0 0
|
|
252
|
+
0 0 1 0 0
|
|
253
|
+
0 0 0 0
|
|
254
|
+
0 0 0
|
|
255
|
+
0 0
|
|
256
|
+
0
|
|
257
|
+
|
|
258
|
+
The skew tableaux can now be read off the partitions labelling the
|
|
259
|
+
left and the top boundary. These can be accessed using the method
|
|
260
|
+
:meth:`~sage.combinat.growth.GrowthDiagram.in_labels`::
|
|
261
|
+
|
|
262
|
+
sage: ascii_art(SkewTableau(chain=G.in_labels()[len(o)-2:]),
|
|
263
|
+
....: SkewTableau(chain=G.in_labels()[len(o)-2::-1]))
|
|
264
|
+
. 1 . 7
|
|
265
|
+
5 4
|
|
266
|
+
|
|
267
|
+
Rules currently available
|
|
268
|
+
-------------------------
|
|
269
|
+
|
|
270
|
+
As mentioned at the beginning, the Robinson-Schensted-Knuth
|
|
271
|
+
correspondence is just a special case of growth diagrams. In
|
|
272
|
+
particular, we have implemented the following local rules:
|
|
273
|
+
|
|
274
|
+
- RSK (:class:`~sage.combinat.growth.RuleRSK`).
|
|
275
|
+
- A variation of RSK originally due to Burge
|
|
276
|
+
(:class:`~sage.combinat.growth.RuleBurge`).
|
|
277
|
+
- A correspondence producing binary words originally due to Viennot
|
|
278
|
+
(:class:`~sage.combinat.growth.RuleBinaryWord`).
|
|
279
|
+
- A correspondence producing domino tableaux
|
|
280
|
+
(:class:`~sage.combinat.growth.RuleDomino`) originally due
|
|
281
|
+
to Barbasch and Vogan.
|
|
282
|
+
- A correspondence for shifted shapes
|
|
283
|
+
(:class:`~sage.combinat.growth.RuleShiftedShapes`), where the
|
|
284
|
+
original insertion algorithm is due to Sagan and Worley, and Haiman.
|
|
285
|
+
- The Sylvester correspondence, producing binary trees
|
|
286
|
+
(:class:`~sage.combinat.growth.RuleSylvester`).
|
|
287
|
+
- The Young-Fibonacci correspondence
|
|
288
|
+
(:class:`~sage.combinat.growth.RuleYoungFibonacci`).
|
|
289
|
+
- LLMS insertion (:class:`~sage.combinat.growth.RuleLLMS`).
|
|
290
|
+
|
|
291
|
+
Background
|
|
292
|
+
----------
|
|
293
|
+
|
|
294
|
+
At the heart of Fomin's framework is the notion of dual graded
|
|
295
|
+
graphs. This is a pair of digraphs `P, Q` (multiple edges being
|
|
296
|
+
allowed) on the same set of vertices `V`, that satisfy the following
|
|
297
|
+
conditions:
|
|
298
|
+
|
|
299
|
+
* the graphs are graded, that is, there is a function `\rho : V \to
|
|
300
|
+
\NN`, such that for any edge `(v, w)` of `P` and also of `Q` we
|
|
301
|
+
have `\rho(w) = \rho(v) + 1`,
|
|
302
|
+
|
|
303
|
+
* there is a vertex `0` with rank zero, and
|
|
304
|
+
|
|
305
|
+
* there is a positive integer `r` such that `DU = UD + rI` on the
|
|
306
|
+
free `\ZZ`-module `\ZZ[V]`, where `D` is the down operator of `Q`,
|
|
307
|
+
assigning to each vertex the formal sum of its predecessors, `U` is
|
|
308
|
+
the up operator of `P`, assigning to each vertex the formal sum of
|
|
309
|
+
its successors, and `I` is the identity operator.
|
|
310
|
+
|
|
311
|
+
Note that the condition `DU = UD + rI` is symmetric with respect to
|
|
312
|
+
the interchange of the graphs `P` and `Q`, because the up operator of
|
|
313
|
+
a graph is the transpose of its down operator.
|
|
314
|
+
|
|
315
|
+
For example, taking for both `P` and `Q` to be Young's lattice and
|
|
316
|
+
`r=1`, we obtain the dual graded graphs for classical Schensted
|
|
317
|
+
insertion.
|
|
318
|
+
|
|
319
|
+
Given such a pair of graphs, there is a bijection between the
|
|
320
|
+
`r`-colored permutations on `k` letters and pairs `(p, q)`, where `p`
|
|
321
|
+
is a path in `P` from zero to a vertex of rank `k` and `q` is a path
|
|
322
|
+
in `Q` from zero to the same vertex.
|
|
323
|
+
|
|
324
|
+
It turns out that - in principle - this bijection can always be
|
|
325
|
+
described by so-called local forward and backward rules, see
|
|
326
|
+
[Fom1995]_ for a detailed description. Knowing at least the forward
|
|
327
|
+
rules, or the backward rules, you can implement your own growth
|
|
328
|
+
diagram class.
|
|
329
|
+
|
|
330
|
+
Implementing your own growth diagrams
|
|
331
|
+
-------------------------------------
|
|
332
|
+
|
|
333
|
+
The class :class:`GrowthDiagram` is written so that it is easy to
|
|
334
|
+
implement growth diagrams you come across in your research.
|
|
335
|
+
Moreover, the class tolerates some deviations from Fomin's
|
|
336
|
+
definitions. For example, although the general
|
|
337
|
+
Robinson-Schensted-Knuth correspondence between integer matrices and
|
|
338
|
+
semistandard tableaux is, strictly speaking, not a growth on dual
|
|
339
|
+
graded graphs, it is supported by our framework.
|
|
340
|
+
|
|
341
|
+
For illustration, let us implement a growth diagram class with the
|
|
342
|
+
backward rule only. Suppose that the vertices of the graph are the
|
|
343
|
+
nonnegative integers, the rank is given by the integer itself, and
|
|
344
|
+
the backward rule is `(y, z, x) \mapsto (\min(x,y), 0)` if `y = z`
|
|
345
|
+
or `x = z` and `(y, z, x) \mapsto (\min(x,y), 1)` otherwise.
|
|
346
|
+
|
|
347
|
+
We first need to import the base class for a rule::
|
|
348
|
+
|
|
349
|
+
sage: from sage.combinat.growth import Rule
|
|
350
|
+
|
|
351
|
+
Next, we implement the backward rule and the rank function and
|
|
352
|
+
provide the bottom element ``zero`` of the graph. For more
|
|
353
|
+
information, see :class:`~sage.combinat.growth.Rule`. ::
|
|
354
|
+
|
|
355
|
+
sage: class RulePascal(Rule):
|
|
356
|
+
....: zero = 0
|
|
357
|
+
....: def rank(self, v): return v
|
|
358
|
+
....: def backward_rule(self, y, z, x):
|
|
359
|
+
....: return (min(x,y), 0 if y==z or x==z else 1)
|
|
360
|
+
|
|
361
|
+
We can now compute the filling corresponding to a sequence of labels
|
|
362
|
+
as follows::
|
|
363
|
+
|
|
364
|
+
sage: GrowthDiagram(RulePascal(), labels=[0,1,2,1,2,1,0])
|
|
365
|
+
1 0 0
|
|
366
|
+
0 0 1
|
|
367
|
+
0 1
|
|
368
|
+
|
|
369
|
+
Of course, since we have not provided the forward rule, we cannot
|
|
370
|
+
compute the labels belonging to a filling::
|
|
371
|
+
|
|
372
|
+
sage: GrowthDiagram(RulePascal(), [3,1,2])
|
|
373
|
+
Traceback (most recent call last):
|
|
374
|
+
...
|
|
375
|
+
AttributeError: 'RulePascal' object has no attribute 'forward_rule'...
|
|
376
|
+
|
|
377
|
+
We now re-implement the rule where we provide the dual graded graphs::
|
|
378
|
+
|
|
379
|
+
sage: class RulePascal(Rule):
|
|
380
|
+
....: zero = 0
|
|
381
|
+
....: def rank(self, v): return v
|
|
382
|
+
....: def backward_rule(self, y, z, x):
|
|
383
|
+
....: return (min(x,y), 0 if y==z or x==z else 1)
|
|
384
|
+
....: def vertices(self, n): return [n]
|
|
385
|
+
....: def is_P_edge(self, v, w): return w == v + 1
|
|
386
|
+
....: def is_Q_edge(self, v, w): return w == v + 1
|
|
387
|
+
|
|
388
|
+
Are they really dual? ::
|
|
389
|
+
|
|
390
|
+
sage: RulePascal()._check_duality(3)
|
|
391
|
+
Traceback (most recent call last):
|
|
392
|
+
...
|
|
393
|
+
ValueError: D U - U D differs from 1 I for vertex 3:
|
|
394
|
+
D U = [3]
|
|
395
|
+
U D + 1 I = [3, 3]
|
|
396
|
+
|
|
397
|
+
With our current definition, duality fails - in fact, there are no
|
|
398
|
+
dual graded graphs on the integers without multiple edges.
|
|
399
|
+
Consequently, also the backward rule cannot work as ``backward_rule``
|
|
400
|
+
requires additional information (the edge labels as arguments).
|
|
401
|
+
|
|
402
|
+
Let us thus continue with the example from Section 4.7 of [Fom1995]_
|
|
403
|
+
instead, which defines dual graded graphs with multiple edges on the
|
|
404
|
+
integers. The color ``self.zero_edge``, which defaults to ``0`` is
|
|
405
|
+
reserved for degenerate edges, but may be abused for the unique edge
|
|
406
|
+
if one of the graphs has no multiple edges. For greater clarity in
|
|
407
|
+
this example we set it to ``None``::
|
|
408
|
+
|
|
409
|
+
sage: class RulePascal(Rule):
|
|
410
|
+
....: zero = 0
|
|
411
|
+
....: has_multiple_edges = True
|
|
412
|
+
....: zero_edge = None
|
|
413
|
+
....: def rank(self, v): return v
|
|
414
|
+
....: def vertices(self, n): return [n]
|
|
415
|
+
....: def is_P_edge(self, v, w): return [0] if w == v + 1 else []
|
|
416
|
+
....: def is_Q_edge(self, v, w): return list(range(w)) if w == v+1 else []
|
|
417
|
+
|
|
418
|
+
We verify these are `1` dual at level `5`::
|
|
419
|
+
|
|
420
|
+
sage: RulePascal()._check_duality(5)
|
|
421
|
+
|
|
422
|
+
Finally, let us provide the backward rule. The arguments of the rule
|
|
423
|
+
are vertices together with the edge labels now, specifying the path
|
|
424
|
+
from the lower left to the upper right of the cell. The horizontal
|
|
425
|
+
edges come from `Q`, whereas the vertical edges come from `P`.
|
|
426
|
+
|
|
427
|
+
Thus, the definition in Section 4.7 of [Fom1995]_ translates as
|
|
428
|
+
follows::
|
|
429
|
+
|
|
430
|
+
sage: class RulePascal(Rule):
|
|
431
|
+
....: zero = 0
|
|
432
|
+
....: has_multiple_edges = True
|
|
433
|
+
....: zero_edge = None
|
|
434
|
+
....: def rank(self, v): return v
|
|
435
|
+
....: def vertices(self, n): return [n]
|
|
436
|
+
....: def is_P_edge(self, v, w): return [0] if w == v + 1 else []
|
|
437
|
+
....: def is_Q_edge(self, v, w): return list(range(w)) if w == v+1 else []
|
|
438
|
+
....: def backward_rule(self, y, g, z, h, x):
|
|
439
|
+
....: if g is None:
|
|
440
|
+
....: return (0, x, None, 0)
|
|
441
|
+
....: if h is None:
|
|
442
|
+
....: return (None, y, g, 0)
|
|
443
|
+
....: if g == 0:
|
|
444
|
+
....: return (None, y, None, 1)
|
|
445
|
+
....: else:
|
|
446
|
+
....: return (0, x-1, g-1, 0)
|
|
447
|
+
|
|
448
|
+
The labels are now alternating between vertices and edge-colors::
|
|
449
|
+
|
|
450
|
+
sage: GrowthDiagram(RulePascal(), labels=[0,0,1,0,2,0,1,0,0])
|
|
451
|
+
1 0
|
|
452
|
+
0 1
|
|
453
|
+
|
|
454
|
+
sage: GrowthDiagram(RulePascal(), labels=[0,0,1,1,2,0,1,0,0])
|
|
455
|
+
0 1
|
|
456
|
+
1 0
|
|
457
|
+
"""
|
|
458
|
+
|
|
459
|
+
# ****************************************************************************
|
|
460
|
+
# Copyright (C) 2017 Martin Rubey <martin.rubey at tuwien.ac.at>
|
|
461
|
+
# 2017 Travis Scrimshaw <tcscrims at gmail.com>
|
|
462
|
+
#
|
|
463
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
464
|
+
#
|
|
465
|
+
# This code is distributed in the hope that it will be useful,
|
|
466
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
467
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
468
|
+
# General Public License for more details.
|
|
469
|
+
#
|
|
470
|
+
# The full text of the GPL is available at:
|
|
471
|
+
#
|
|
472
|
+
# https://www.gnu.org/licenses/
|
|
473
|
+
# ***************************************************************************
|
|
474
|
+
from __future__ import annotations
|
|
475
|
+
from copy import copy
|
|
476
|
+
from itertools import zip_longest
|
|
477
|
+
|
|
478
|
+
from sage.structure.sage_object import SageObject
|
|
479
|
+
from sage.structure.unique_representation import UniqueRepresentation
|
|
480
|
+
from sage.combinat.words.word import Word
|
|
481
|
+
from sage.combinat.words.words import Words
|
|
482
|
+
from sage.combinat.binary_tree import BinaryTree, BinaryTrees, LabelledBinaryTree
|
|
483
|
+
from sage.combinat.composition import Compositions
|
|
484
|
+
from sage.combinat.partition import _Partitions, Partitions
|
|
485
|
+
from sage.combinat.skew_partition import SkewPartition
|
|
486
|
+
from sage.combinat.skew_tableau import SkewTableau
|
|
487
|
+
from sage.combinat.core import Core, Cores
|
|
488
|
+
from sage.combinat.k_tableau import WeakTableau, StrongTableau
|
|
489
|
+
from sage.combinat.shifted_primed_tableau import ShiftedPrimedTableau
|
|
490
|
+
from sage.misc.lazy_import import lazy_import
|
|
491
|
+
|
|
492
|
+
lazy_import('sage.graphs.digraph', 'DiGraph')
|
|
493
|
+
lazy_import('sage.combinat.posets.posets', 'Poset')
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def _make_partition(l):
|
|
497
|
+
"""
|
|
498
|
+
Return the list as a partition.
|
|
499
|
+
|
|
500
|
+
This is intended to be fast, so checks are bypassed.
|
|
501
|
+
|
|
502
|
+
TESTS::
|
|
503
|
+
|
|
504
|
+
sage: from sage.combinat.growth import _make_partition
|
|
505
|
+
sage: p = _make_partition([3,2,1,0]); p
|
|
506
|
+
[3, 2, 1]
|
|
507
|
+
|
|
508
|
+
sage: p.parent()
|
|
509
|
+
Partitions
|
|
510
|
+
"""
|
|
511
|
+
return _Partitions.element_class(_Partitions, l)
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
class GrowthDiagram(SageObject):
|
|
515
|
+
r"""
|
|
516
|
+
A generalized Schensted growth diagram in the sense of Fomin.
|
|
517
|
+
|
|
518
|
+
Growth diagrams were introduced by Sergey Fomin [Fom1994]_,
|
|
519
|
+
[Fom1995]_ and provide a vast generalization of the
|
|
520
|
+
Robinson-Schensted-Knuth (RSK) correspondence between matrices
|
|
521
|
+
with nonnegative integer entries and pairs of semistandard Young
|
|
522
|
+
tableaux of the same shape.
|
|
523
|
+
|
|
524
|
+
A growth diagram is based on the notion of *dual graded graphs*,
|
|
525
|
+
a pair of digraphs `P, Q` (multiple edges being allowed) on the
|
|
526
|
+
same set of vertices `V`, that satisfy the following conditions:
|
|
527
|
+
|
|
528
|
+
* the graphs are graded, that is, there is a function `\rho:
|
|
529
|
+
V \to \NN`, such that for any edge `(v, w)` of `P` and also
|
|
530
|
+
of `Q` we have `\rho(w) = \rho(v) + 1`,
|
|
531
|
+
|
|
532
|
+
* there is a vertex `0` with rank zero, and
|
|
533
|
+
|
|
534
|
+
* there is a positive integer `r` such that `DU = UD + rI` on the
|
|
535
|
+
free `\ZZ`-module `\ZZ[V]`, where `D` is the down operator of
|
|
536
|
+
`Q`, assigning to each vertex the formal sum of its
|
|
537
|
+
predecessors, `U` is the up operator of `P`, assigning to each
|
|
538
|
+
vertex the formal sum of its successors, and `I` is the
|
|
539
|
+
identity operator.
|
|
540
|
+
|
|
541
|
+
Growth diagrams are defined by providing a pair of local rules: a
|
|
542
|
+
'forward' rule, whose input are three vertices `y`, `t` and `x`
|
|
543
|
+
of the dual graded graphs and an integer, and whose output is a
|
|
544
|
+
fourth vertex `z`. This rule should be invertible in the
|
|
545
|
+
following sense: there is a so-called 'backward' rule that
|
|
546
|
+
recovers the integer and `t` given `y`, `z` and `x`.
|
|
547
|
+
|
|
548
|
+
All implemented growth diagram rules are available by
|
|
549
|
+
``GrowthDiagram.rules.<tab>``. The current list is:
|
|
550
|
+
|
|
551
|
+
- :class:`~sage.combinat.growth.RuleRSK` -- RSK
|
|
552
|
+
- :class:`~sage.combinat.growth.RuleBurge` -- a variation of RSK
|
|
553
|
+
originally due to Burge
|
|
554
|
+
- :class:`~sage.combinat.growth.RuleBinaryWord` -- a correspondence
|
|
555
|
+
producing binary words originally due to Viennot
|
|
556
|
+
- :class:`~sage.combinat.growth.RuleDomino` -- a correspondence
|
|
557
|
+
producing domino tableaux originally due to Barbasch and Vogan
|
|
558
|
+
- :class:`~sage.combinat.growth.RuleShiftedShapes` -- a correspondence
|
|
559
|
+
for shifted shapes, where the original insertion algorithm is due
|
|
560
|
+
to Sagan and Worley, and Haiman.
|
|
561
|
+
- :class:`~sage.combinat.growth.RuleSylvester` -- the Sylvester
|
|
562
|
+
correspondence, producing binary trees
|
|
563
|
+
- :class:`~sage.combinat.growth.RuleYoungFibonacci` -- the
|
|
564
|
+
Young-Fibonacci correspondence
|
|
565
|
+
- :class:`~sage.combinat.growth.RuleLLMS` -- LLMS insertion
|
|
566
|
+
|
|
567
|
+
INPUT:
|
|
568
|
+
|
|
569
|
+
- ``rule`` -- :class:`~sage.combinat.growth.Rule`;
|
|
570
|
+
the growth diagram rule
|
|
571
|
+
|
|
572
|
+
- ``filling`` -- (optional) a dictionary whose keys are coordinates
|
|
573
|
+
and values are integers, a list of lists of integers, or a word
|
|
574
|
+
with integer values; if a word, then negative letters but without
|
|
575
|
+
repetitions are allowed and interpreted as coloured permutations
|
|
576
|
+
|
|
577
|
+
- ``shape`` -- (optional) a (possibly skew) partition
|
|
578
|
+
|
|
579
|
+
- ``labels`` -- (optional) a list that specifies a path whose length
|
|
580
|
+
in the half-perimeter of the shape; more details given below
|
|
581
|
+
|
|
582
|
+
If ``filling`` is not given, then the growth diagram is determined
|
|
583
|
+
by applying the backward rule to ``labels`` decorating the
|
|
584
|
+
boundary opposite of the origin of the ``shape``. In this case,
|
|
585
|
+
``labels`` are interpreted as labelling the boundary opposite of
|
|
586
|
+
the origin.
|
|
587
|
+
|
|
588
|
+
Otherwise, ``shape`` is inferred from ``filling`` or ``labels`` if
|
|
589
|
+
possible and ``labels`` is set to ``rule.zero`` if not specified.
|
|
590
|
+
Here, ``labels`` are labelling the boundary on the side of the origin.
|
|
591
|
+
|
|
592
|
+
For ``labels``, if ``rule.has_multiple_edges`` is ``True``, then the
|
|
593
|
+
elements should be of the form `(v_1, e_1, \ldots, e_{n-1}, v_n)`,
|
|
594
|
+
where `n` is the half-perimeter of ``shape``, and `(v_{i-1}, e_i, v_i)`
|
|
595
|
+
is an edge in the dual graded graph for all `i`. Otherwise, it is a
|
|
596
|
+
list of `n` vertices.
|
|
597
|
+
|
|
598
|
+
.. NOTE::
|
|
599
|
+
|
|
600
|
+
Coordinates are of the form ``(col, row)`` where the origin is
|
|
601
|
+
in the upper left, to be consistent with permutation matrices
|
|
602
|
+
and skew tableaux (in English convention). This is different
|
|
603
|
+
from Fomin's convention, who uses a Cartesian coordinate system.
|
|
604
|
+
|
|
605
|
+
Conventions are chosen such that for permutations, the same
|
|
606
|
+
growth diagram is constructed when passing the permutation
|
|
607
|
+
matrix instead.
|
|
608
|
+
|
|
609
|
+
EXAMPLES:
|
|
610
|
+
|
|
611
|
+
We create a growth diagram using the forward RSK rule and a permutation::
|
|
612
|
+
|
|
613
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
614
|
+
sage: pi = Permutation([4, 1, 2, 3])
|
|
615
|
+
sage: G = GrowthDiagram(RuleRSK, pi); G
|
|
616
|
+
0 1 0 0
|
|
617
|
+
0 0 1 0
|
|
618
|
+
0 0 0 1
|
|
619
|
+
1 0 0 0
|
|
620
|
+
sage: G.out_labels()
|
|
621
|
+
[[], [1], [1, 1], [2, 1], [3, 1], [3], [2], [1], []]
|
|
622
|
+
|
|
623
|
+
Passing the permutation matrix instead gives the same result::
|
|
624
|
+
|
|
625
|
+
sage: G = GrowthDiagram(RuleRSK, pi.to_matrix()) # needs sage.modules
|
|
626
|
+
sage: ascii_art([G.P_symbol(), G.Q_symbol()]) # needs sage.modules
|
|
627
|
+
[ 1 2 3 1 3 4 ]
|
|
628
|
+
[ 4 , 2 ]
|
|
629
|
+
|
|
630
|
+
We give the same example but using a skew shape::
|
|
631
|
+
|
|
632
|
+
sage: shape = SkewPartition([[4,4,4,2],[1,1]])
|
|
633
|
+
sage: G = GrowthDiagram(RuleRSK, pi, shape=shape); G
|
|
634
|
+
. 1 0 0
|
|
635
|
+
. 0 1 0
|
|
636
|
+
0 0 0 1
|
|
637
|
+
1 0
|
|
638
|
+
sage: G.out_labels()
|
|
639
|
+
[[], [1], [1, 1], [1], [2], [3], [2], [1], []]
|
|
640
|
+
|
|
641
|
+
We construct a growth diagram using the backwards RSK rule by
|
|
642
|
+
specifying the labels::
|
|
643
|
+
|
|
644
|
+
sage: GrowthDiagram(RuleRSK, labels=G.out_labels())
|
|
645
|
+
0 1 0 0
|
|
646
|
+
0 0 1 0
|
|
647
|
+
0 0 0 1
|
|
648
|
+
1 0
|
|
649
|
+
"""
|
|
650
|
+
|
|
651
|
+
def __init__(self, rule, filling=None, shape=None, labels=None):
|
|
652
|
+
r"""
|
|
653
|
+
Initialize ``self``.
|
|
654
|
+
|
|
655
|
+
TESTS::
|
|
656
|
+
|
|
657
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
658
|
+
sage: w = [3,3,2,4,1]; G = GrowthDiagram(RuleRSK, w)
|
|
659
|
+
sage: [G.P_symbol(), G.Q_symbol()]
|
|
660
|
+
[[[1, 3, 4], [2], [3]], [[1, 2, 4], [3], [5]]]
|
|
661
|
+
sage: RSK(w)
|
|
662
|
+
[[[1, 3, 4], [2], [3]], [[1, 2, 4], [3], [5]]]
|
|
663
|
+
|
|
664
|
+
sage: TestSuite(G).run()
|
|
665
|
+
|
|
666
|
+
sage: GrowthDiagram(RuleRSK)
|
|
667
|
+
Traceback (most recent call last):
|
|
668
|
+
...
|
|
669
|
+
ValueError: please provide a filling or a sequence of labels
|
|
670
|
+
"""
|
|
671
|
+
if not isinstance(rule, Rule):
|
|
672
|
+
raise TypeError("the rule must be an instance of Rule")
|
|
673
|
+
self.rule = rule
|
|
674
|
+
|
|
675
|
+
if filling is None:
|
|
676
|
+
if labels is None:
|
|
677
|
+
raise ValueError("please provide a filling or a sequence of labels")
|
|
678
|
+
|
|
679
|
+
labels = self._process_labels(labels)
|
|
680
|
+
|
|
681
|
+
if shape is None:
|
|
682
|
+
shape = self._shape_from_labels(labels)
|
|
683
|
+
|
|
684
|
+
self._lambda, self._mu = self._process_shape(shape)
|
|
685
|
+
self._out_labels = labels
|
|
686
|
+
self._check_labels(self._out_labels)
|
|
687
|
+
self._shrink()
|
|
688
|
+
else:
|
|
689
|
+
self._filling, (self._lambda, self._mu) = self._process_filling_and_shape(filling, shape)
|
|
690
|
+
|
|
691
|
+
if labels is None:
|
|
692
|
+
rule = self.rule
|
|
693
|
+
if rule.has_multiple_edges:
|
|
694
|
+
self._in_labels = [rule.zero, rule.zero_edge]*(self.half_perimeter()-1) + [rule.zero]
|
|
695
|
+
else:
|
|
696
|
+
self._in_labels = [rule.zero] * self.half_perimeter()
|
|
697
|
+
else:
|
|
698
|
+
labels = self._process_labels(labels)
|
|
699
|
+
self._in_labels = labels
|
|
700
|
+
|
|
701
|
+
self._check_labels(self._in_labels)
|
|
702
|
+
self._grow()
|
|
703
|
+
|
|
704
|
+
def filling(self):
|
|
705
|
+
r"""
|
|
706
|
+
Return the filling of the diagram as a dictionary.
|
|
707
|
+
|
|
708
|
+
EXAMPLES::
|
|
709
|
+
|
|
710
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
711
|
+
sage: G = GrowthDiagram(RuleRSK, [[0,1,0], [1,0,2]])
|
|
712
|
+
sage: G.filling()
|
|
713
|
+
{(0, 1): 1, (1, 0): 1, (2, 1): 2}
|
|
714
|
+
"""
|
|
715
|
+
return self._filling
|
|
716
|
+
|
|
717
|
+
def conjugate(self):
|
|
718
|
+
r"""
|
|
719
|
+
Return the conjugate growth diagram of ``self``.
|
|
720
|
+
|
|
721
|
+
This is the growth diagram with the filling reflected over the
|
|
722
|
+
main diagonal.
|
|
723
|
+
|
|
724
|
+
The sequence of labels along the boundary on the side of the
|
|
725
|
+
origin is the reversal of the corresponding sequence of the
|
|
726
|
+
original growth diagram.
|
|
727
|
+
|
|
728
|
+
When the filling is a permutation, the conjugate filling
|
|
729
|
+
corresponds to its inverse.
|
|
730
|
+
|
|
731
|
+
EXAMPLES::
|
|
732
|
+
|
|
733
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
734
|
+
sage: G = GrowthDiagram(RuleRSK, [[0,1,0], [1,0,2]])
|
|
735
|
+
sage: Gc = G.conjugate()
|
|
736
|
+
sage: (Gc.P_symbol(), Gc.Q_symbol()) == (G.Q_symbol(), G.P_symbol())
|
|
737
|
+
True
|
|
738
|
+
|
|
739
|
+
TESTS:
|
|
740
|
+
|
|
741
|
+
Check that labels and shape are handled correctly::
|
|
742
|
+
|
|
743
|
+
sage: o = [[2,1],[2,2],[3,2],[4,2],[4,1],[4,1,1],[3,1,1],[3,1],[3,2],[3,1],[2,1]]
|
|
744
|
+
sage: l = [o[i//2] if is_even(i) else min(o[(i-1)//2],o[(i+1)//2])
|
|
745
|
+
....: for i in range(2*len(o)-1)]
|
|
746
|
+
sage: la = list(range(len(o)-2, 0, -1))
|
|
747
|
+
sage: G = RuleRSK(labels=l[1:-1], shape=la)
|
|
748
|
+
sage: G.out_labels() == G.conjugate().out_labels()[::-1]
|
|
749
|
+
True
|
|
750
|
+
"""
|
|
751
|
+
F = {(j,i): v for (i,j),v in self._filling.items()}
|
|
752
|
+
return GrowthDiagram(self.rule,
|
|
753
|
+
filling=F,
|
|
754
|
+
shape=self.shape().conjugate(),
|
|
755
|
+
labels=self.in_labels()[::-1])
|
|
756
|
+
|
|
757
|
+
def rotate(self):
|
|
758
|
+
r"""
|
|
759
|
+
Return the growth diagram with the filling rotated by 180 degrees.
|
|
760
|
+
|
|
761
|
+
The rotated growth diagram is initialized with
|
|
762
|
+
``labels=None``, that is, all labels along the boundary on
|
|
763
|
+
the side of the origin are set to ``rule.zero``.
|
|
764
|
+
|
|
765
|
+
For RSK-growth diagrams and rectangular fillings, this
|
|
766
|
+
corresponds to evacuation of the `P`- and the `Q`-symbol.
|
|
767
|
+
|
|
768
|
+
EXAMPLES::
|
|
769
|
+
|
|
770
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
771
|
+
sage: G = GrowthDiagram(RuleRSK, [[0,1,0], [1,0,2]])
|
|
772
|
+
sage: Gc = G.rotate()
|
|
773
|
+
sage: ascii_art([Gc.P_symbol(), Gc.Q_symbol()])
|
|
774
|
+
[ 1 1 1 1 1 2 ]
|
|
775
|
+
[ 2 , 3 ]
|
|
776
|
+
|
|
777
|
+
sage: ascii_art([Tableau(t).evacuation()
|
|
778
|
+
....: for t in [G.P_symbol(), G.Q_symbol()]])
|
|
779
|
+
[ 1 1 1 1 1 2 ]
|
|
780
|
+
[ 2 , 3 ]
|
|
781
|
+
|
|
782
|
+
TESTS:
|
|
783
|
+
|
|
784
|
+
Check that shape is handled correctly::
|
|
785
|
+
|
|
786
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
787
|
+
sage: G = GrowthDiagram(RuleRSK,
|
|
788
|
+
....: filling={(0,2):1, (3,1):2, (2,1):3},
|
|
789
|
+
....: shape=SkewPartition([[5,5,5,3],[3,1]]))
|
|
790
|
+
sage: G
|
|
791
|
+
. . . 0 0
|
|
792
|
+
. 0 3 2 0
|
|
793
|
+
1 0 0 0 0
|
|
794
|
+
0 0 0
|
|
795
|
+
sage: G.rotate()
|
|
796
|
+
. . 0 0 0
|
|
797
|
+
0 0 0 0 1
|
|
798
|
+
0 2 3 0
|
|
799
|
+
0 0
|
|
800
|
+
"""
|
|
801
|
+
l = self._lambda[0]
|
|
802
|
+
h = len(self._lambda)
|
|
803
|
+
shape_lambda = [l - p for p in self._mu] + [l] * (h - len(self._mu))
|
|
804
|
+
shape_mu = [l - p for p in self._lambda]
|
|
805
|
+
shape = SkewPartition([shape_lambda[::-1], shape_mu[::-1]])
|
|
806
|
+
F = {(l-i-1, h-j-1): v for (i,j),v in self._filling.items()}
|
|
807
|
+
return GrowthDiagram(self.rule,
|
|
808
|
+
filling=F,
|
|
809
|
+
shape=shape)
|
|
810
|
+
|
|
811
|
+
def half_perimeter(self):
|
|
812
|
+
r"""
|
|
813
|
+
Return half the perimeter of the shape of the growth diagram.
|
|
814
|
+
|
|
815
|
+
TESTS::
|
|
816
|
+
|
|
817
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
818
|
+
sage: G = GrowthDiagram(RuleRSK, {(0,1):1, (2,0):1}, SkewPartition([[3,1],[1]])); G
|
|
819
|
+
. 0 1
|
|
820
|
+
1
|
|
821
|
+
sage: G.half_perimeter()
|
|
822
|
+
6
|
|
823
|
+
"""
|
|
824
|
+
# Assume that ``self._lambda`` is already set.
|
|
825
|
+
if not self._lambda:
|
|
826
|
+
return 1
|
|
827
|
+
return self._lambda[0] + len(self._lambda) + 1
|
|
828
|
+
|
|
829
|
+
def shape(self):
|
|
830
|
+
r"""
|
|
831
|
+
Return the shape of the growth diagram as a skew partition.
|
|
832
|
+
|
|
833
|
+
.. WARNING::
|
|
834
|
+
|
|
835
|
+
In the literature the label on the corner opposite of the
|
|
836
|
+
origin of a rectangular filling is often called the shape
|
|
837
|
+
of the filling. This method returns the shape of the
|
|
838
|
+
region instead.
|
|
839
|
+
|
|
840
|
+
EXAMPLES::
|
|
841
|
+
|
|
842
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
843
|
+
sage: GrowthDiagram(RuleRSK, [1]).shape()
|
|
844
|
+
[1] / []
|
|
845
|
+
"""
|
|
846
|
+
return SkewPartition([self._lambda, self._mu])
|
|
847
|
+
|
|
848
|
+
def out_labels(self):
|
|
849
|
+
r"""
|
|
850
|
+
Return the labels along the boundary opposite of the origin.
|
|
851
|
+
|
|
852
|
+
EXAMPLES::
|
|
853
|
+
|
|
854
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
855
|
+
sage: G = GrowthDiagram(RuleRSK, [[0,1,0], [1,0,2]])
|
|
856
|
+
sage: G.out_labels()
|
|
857
|
+
[[], [1], [1, 1], [3, 1], [1], []]
|
|
858
|
+
"""
|
|
859
|
+
return self._out_labels
|
|
860
|
+
|
|
861
|
+
def in_labels(self):
|
|
862
|
+
r"""
|
|
863
|
+
Return the labels along the boundary on the side of the origin.
|
|
864
|
+
|
|
865
|
+
EXAMPLES::
|
|
866
|
+
|
|
867
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
868
|
+
sage: G = GrowthDiagram(RuleRSK, labels=[[2,2],[3,2],[3,3],[3,2]]); G
|
|
869
|
+
1 0
|
|
870
|
+
sage: G.in_labels()
|
|
871
|
+
[[2, 2], [2, 2], [2, 2], [3, 2]]
|
|
872
|
+
"""
|
|
873
|
+
return self._in_labels
|
|
874
|
+
|
|
875
|
+
def P_symbol(self):
|
|
876
|
+
r"""
|
|
877
|
+
Return the labels along the vertical boundary of a rectangular
|
|
878
|
+
growth diagram as a generalized standard tableau.
|
|
879
|
+
|
|
880
|
+
EXAMPLES::
|
|
881
|
+
|
|
882
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
883
|
+
sage: G = GrowthDiagram(RuleRSK, [[0,1,0], [1,0,2]])
|
|
884
|
+
sage: ascii_art([G.P_symbol(), G.Q_symbol()])
|
|
885
|
+
[ 1 2 2 1 3 3 ]
|
|
886
|
+
[ 2 , 2 ]
|
|
887
|
+
"""
|
|
888
|
+
return self.rule.P_symbol(self.P_chain())
|
|
889
|
+
|
|
890
|
+
def Q_symbol(self):
|
|
891
|
+
r"""
|
|
892
|
+
Return the labels along the horizontal boundary of a rectangular
|
|
893
|
+
growth diagram as a generalized standard tableau.
|
|
894
|
+
|
|
895
|
+
EXAMPLES::
|
|
896
|
+
|
|
897
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
898
|
+
sage: G = GrowthDiagram(RuleRSK, [[0,1,0], [1,0,2]])
|
|
899
|
+
sage: ascii_art([G.P_symbol(), G.Q_symbol()])
|
|
900
|
+
[ 1 2 2 1 3 3 ]
|
|
901
|
+
[ 2 , 2 ]
|
|
902
|
+
"""
|
|
903
|
+
return self.rule.Q_symbol(self.Q_chain())
|
|
904
|
+
|
|
905
|
+
def P_chain(self):
|
|
906
|
+
r"""
|
|
907
|
+
Return the labels along the vertical boundary of a rectangular
|
|
908
|
+
growth diagram.
|
|
909
|
+
|
|
910
|
+
EXAMPLES::
|
|
911
|
+
|
|
912
|
+
sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
|
|
913
|
+
sage: G = GrowthDiagram(BinaryWord, [4, 1, 2, 3])
|
|
914
|
+
sage: G.P_chain()
|
|
915
|
+
[word: , word: 1, word: 11, word: 111, word: 1011]
|
|
916
|
+
|
|
917
|
+
Check that :issue:`25631` is fixed::
|
|
918
|
+
|
|
919
|
+
sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
|
|
920
|
+
sage: BinaryWord(filling = {}).P_chain()
|
|
921
|
+
[word: ]
|
|
922
|
+
"""
|
|
923
|
+
if not self.is_rectangular():
|
|
924
|
+
raise ValueError("the P symbol is only defined for rectangular shapes")
|
|
925
|
+
if self._lambda:
|
|
926
|
+
if self.rule.has_multiple_edges:
|
|
927
|
+
r = 2*self._lambda[0]
|
|
928
|
+
else:
|
|
929
|
+
r = self._lambda[0]
|
|
930
|
+
else:
|
|
931
|
+
r = 0
|
|
932
|
+
return self._out_labels[r:][::-1]
|
|
933
|
+
|
|
934
|
+
def Q_chain(self):
|
|
935
|
+
r"""
|
|
936
|
+
Return the labels along the horizontal boundary of a rectangular
|
|
937
|
+
growth diagram.
|
|
938
|
+
|
|
939
|
+
EXAMPLES::
|
|
940
|
+
|
|
941
|
+
sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
|
|
942
|
+
sage: G = GrowthDiagram(BinaryWord, [[0,1,0,0], [0,0,1,0], [0,0,0,1], [1,0,0,0]])
|
|
943
|
+
sage: G.Q_chain()
|
|
944
|
+
[word: , word: 1, word: 10, word: 101, word: 1011]
|
|
945
|
+
|
|
946
|
+
Check that :issue:`25631` is fixed::
|
|
947
|
+
|
|
948
|
+
sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
|
|
949
|
+
sage: BinaryWord(filling = {}).Q_chain()
|
|
950
|
+
[word: ]
|
|
951
|
+
"""
|
|
952
|
+
if not self.is_rectangular():
|
|
953
|
+
raise ValueError("the Q symbol is only defined for rectangular shapes")
|
|
954
|
+
if self._lambda:
|
|
955
|
+
if self.rule.has_multiple_edges:
|
|
956
|
+
r = 2*self._lambda[0]+1
|
|
957
|
+
else:
|
|
958
|
+
r = self._lambda[0]+1
|
|
959
|
+
else:
|
|
960
|
+
r = 1
|
|
961
|
+
return self._out_labels[:r]
|
|
962
|
+
|
|
963
|
+
def is_rectangular(self):
|
|
964
|
+
r"""
|
|
965
|
+
Return ``True`` if the shape of the growth diagram is rectangular.
|
|
966
|
+
|
|
967
|
+
EXAMPLES::
|
|
968
|
+
|
|
969
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
970
|
+
sage: GrowthDiagram(RuleRSK, [2,3,1]).is_rectangular()
|
|
971
|
+
True
|
|
972
|
+
sage: GrowthDiagram(RuleRSK, [[1,0,1],[0,1]]).is_rectangular()
|
|
973
|
+
False
|
|
974
|
+
"""
|
|
975
|
+
return (all(x == 0 for x in self._mu)
|
|
976
|
+
and all(x == self._lambda[0] for x in self._lambda))
|
|
977
|
+
|
|
978
|
+
def to_word(self):
|
|
979
|
+
r"""
|
|
980
|
+
Return the filling as a word, if the shape is rectangular and
|
|
981
|
+
there is at most one nonzero entry in each column, which must
|
|
982
|
+
be 1.
|
|
983
|
+
|
|
984
|
+
EXAMPLES::
|
|
985
|
+
|
|
986
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
987
|
+
sage: w = [3,3,2,4,1]; G = GrowthDiagram(RuleRSK, w)
|
|
988
|
+
sage: G
|
|
989
|
+
0 0 0 0 1
|
|
990
|
+
0 0 1 0 0
|
|
991
|
+
1 1 0 0 0
|
|
992
|
+
0 0 0 1 0
|
|
993
|
+
sage: G.to_word()
|
|
994
|
+
[3, 3, 2, 4, 1]
|
|
995
|
+
"""
|
|
996
|
+
if not self.is_rectangular():
|
|
997
|
+
raise ValueError("can only convert fillings of rectangular shapes to words")
|
|
998
|
+
w = [0] * self._lambda[0]
|
|
999
|
+
for ((i,j), v) in self._filling.items():
|
|
1000
|
+
if v != 0:
|
|
1001
|
+
if v == 1:
|
|
1002
|
+
if w[i] == 0:
|
|
1003
|
+
w[i] = j+1
|
|
1004
|
+
else:
|
|
1005
|
+
raise ValueError("can only convert fillings with at"
|
|
1006
|
+
" most one entry per column to words")
|
|
1007
|
+
elif v == -1:
|
|
1008
|
+
if w[i] == 0:
|
|
1009
|
+
w[i] = -(j+1)
|
|
1010
|
+
else:
|
|
1011
|
+
raise ValueError("can only convert fillings with at"
|
|
1012
|
+
" most one entry per column to words")
|
|
1013
|
+
else:
|
|
1014
|
+
raise ValueError("can only convert 0-1 fillings to words;"
|
|
1015
|
+
" try 'to_biword'")
|
|
1016
|
+
return w
|
|
1017
|
+
|
|
1018
|
+
def to_biword(self):
|
|
1019
|
+
r"""
|
|
1020
|
+
Return the filling as a biword, if the shape is rectangular.
|
|
1021
|
+
|
|
1022
|
+
EXAMPLES::
|
|
1023
|
+
|
|
1024
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
1025
|
+
sage: P = Tableau([[1,2,2],[2]])
|
|
1026
|
+
sage: Q = Tableau([[1,3,3],[2]])
|
|
1027
|
+
sage: bw = RSK_inverse(P, Q); bw
|
|
1028
|
+
[[1, 2, 3, 3], [2, 1, 2, 2]]
|
|
1029
|
+
sage: G = GrowthDiagram(RuleRSK, labels=Q.to_chain()[:-1]+P.to_chain()[::-1]); G
|
|
1030
|
+
0 1 0
|
|
1031
|
+
1 0 2
|
|
1032
|
+
|
|
1033
|
+
sage: P = SemistandardTableau([[1, 1, 2], [2]])
|
|
1034
|
+
sage: Q = SemistandardTableau([[1, 2, 2], [2]])
|
|
1035
|
+
sage: G = GrowthDiagram(RuleRSK, labels=Q.to_chain()[:-1]+P.to_chain()[::-1]); G
|
|
1036
|
+
0 2
|
|
1037
|
+
1 1
|
|
1038
|
+
sage: G.to_biword()
|
|
1039
|
+
([1, 2, 2, 2], [2, 1, 1, 2])
|
|
1040
|
+
sage: RSK([1, 2, 2, 2], [2, 1, 1, 2])
|
|
1041
|
+
[[[1, 1, 2], [2]], [[1, 2, 2], [2]]]
|
|
1042
|
+
"""
|
|
1043
|
+
if not self.is_rectangular():
|
|
1044
|
+
raise ValueError("can only convert fillings of rectangular shapes to words")
|
|
1045
|
+
w1 = []
|
|
1046
|
+
w2 = []
|
|
1047
|
+
for ((i,j), v) in sorted(self._filling.items()):
|
|
1048
|
+
if v >= 0:
|
|
1049
|
+
w1.extend([i+1]*v)
|
|
1050
|
+
w2.extend([j+1]*v)
|
|
1051
|
+
else:
|
|
1052
|
+
raise ValueError("can only convert fillings with"
|
|
1053
|
+
" nonnegative entries to words")
|
|
1054
|
+
return (w1, w2)
|
|
1055
|
+
|
|
1056
|
+
def __iter__(self):
|
|
1057
|
+
r"""
|
|
1058
|
+
Return the rows of the filling.
|
|
1059
|
+
|
|
1060
|
+
TESTS::
|
|
1061
|
+
|
|
1062
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
1063
|
+
sage: G = GrowthDiagram(RuleRSK, {(0,1):1, (1,0):1}, SkewPartition([[2,1],[1]]))
|
|
1064
|
+
sage: list(G)
|
|
1065
|
+
[[None, 1], [1]]
|
|
1066
|
+
|
|
1067
|
+
sage: pi = Permutation([2,3,1,6,4,5])
|
|
1068
|
+
sage: G = GrowthDiagram(RuleRSK, pi)
|
|
1069
|
+
sage: list(G)
|
|
1070
|
+
[[0, 0, 1, 0, 0, 0],
|
|
1071
|
+
[1, 0, 0, 0, 0, 0],
|
|
1072
|
+
[0, 1, 0, 0, 0, 0],
|
|
1073
|
+
[0, 0, 0, 0, 1, 0],
|
|
1074
|
+
[0, 0, 0, 0, 0, 1],
|
|
1075
|
+
[0, 0, 0, 1, 0, 0]]
|
|
1076
|
+
"""
|
|
1077
|
+
return ([None]*self._mu[r] + [self._filling.get((self._mu[r]+j,r), 0)
|
|
1078
|
+
for j in range(self._lambda[r]-self._mu[r])]
|
|
1079
|
+
for r in range(len(self._lambda)))
|
|
1080
|
+
|
|
1081
|
+
def _repr_(self):
|
|
1082
|
+
r"""
|
|
1083
|
+
Return a string with the filling of the growth diagram
|
|
1084
|
+
as a skew tableau.
|
|
1085
|
+
|
|
1086
|
+
TESTS::
|
|
1087
|
+
|
|
1088
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
1089
|
+
sage: GrowthDiagram(RuleRSK, {(0,1):1, (1,0):1}, SkewPartition([[2,1],[1]]))
|
|
1090
|
+
. 1
|
|
1091
|
+
1
|
|
1092
|
+
|
|
1093
|
+
sage: GrowthDiagram(RuleRSK, {(0,1):1, (2,0):1}, SkewPartition([[3,1],[1]]))
|
|
1094
|
+
. 0 1
|
|
1095
|
+
1
|
|
1096
|
+
"""
|
|
1097
|
+
return SkewTableau(expr=[self._mu,
|
|
1098
|
+
[[self._filling.get((self._mu[r]+j,r), 0)
|
|
1099
|
+
for j in range(self._lambda[r]-self._mu[r])]
|
|
1100
|
+
for r in range(len(self._lambda))][::-1]])._repr_diagram()
|
|
1101
|
+
|
|
1102
|
+
def __eq__(self, other):
|
|
1103
|
+
r"""
|
|
1104
|
+
Return ``True`` if the growth diagram ``other`` has the same
|
|
1105
|
+
shape and the same filling as ``self``.
|
|
1106
|
+
|
|
1107
|
+
EXAMPLES:
|
|
1108
|
+
|
|
1109
|
+
Equality ignores zeros in fillings::
|
|
1110
|
+
|
|
1111
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
1112
|
+
sage: G1 = GrowthDiagram(RuleRSK, {(0, 1): 1, (1, 0): 1})
|
|
1113
|
+
sage: G2 = GrowthDiagram(RuleRSK, {(0, 0): 0, (0, 1): 1, (1, 0): 1})
|
|
1114
|
+
sage: G1 == G2
|
|
1115
|
+
True
|
|
1116
|
+
|
|
1117
|
+
Growth diagrams with different shapes are different::
|
|
1118
|
+
|
|
1119
|
+
sage: G1 = GrowthDiagram(RuleRSK, [[0,1,0],[1,0]])
|
|
1120
|
+
sage: G2 = GrowthDiagram(RuleRSK, [[0,1,0],[1]])
|
|
1121
|
+
sage: G1 == G2
|
|
1122
|
+
False
|
|
1123
|
+
|
|
1124
|
+
Growth diagrams with different rules are different::
|
|
1125
|
+
|
|
1126
|
+
sage: G1 = GrowthDiagram(RuleRSK, {(0, 1): 1, (1, 0): 1})
|
|
1127
|
+
sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
|
|
1128
|
+
sage: G2 = GrowthDiagram(BinaryWord, {(0, 1): 1, (1, 0): 1})
|
|
1129
|
+
sage: G1 == G2
|
|
1130
|
+
False
|
|
1131
|
+
"""
|
|
1132
|
+
return (type(self) is type(other) and
|
|
1133
|
+
self.rule == other.rule and
|
|
1134
|
+
self._lambda == other._lambda and
|
|
1135
|
+
self._mu == other._mu and
|
|
1136
|
+
self._filling == other._filling)
|
|
1137
|
+
|
|
1138
|
+
def __ne__(self, other):
|
|
1139
|
+
r"""
|
|
1140
|
+
Return ``True`` if the growth diagram ``other`` does not have the
|
|
1141
|
+
same shape and the same filling as ``self``.
|
|
1142
|
+
|
|
1143
|
+
TESTS:
|
|
1144
|
+
|
|
1145
|
+
Equality ignores zeros in fillings::
|
|
1146
|
+
|
|
1147
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
1148
|
+
sage: G1 = GrowthDiagram(RuleRSK, {(0, 1): 1, (1, 0): 1})
|
|
1149
|
+
sage: G2 = GrowthDiagram(RuleRSK, {(0, 0): 0, (0, 1): 1, (1, 0): 1})
|
|
1150
|
+
sage: G1 != G2
|
|
1151
|
+
False
|
|
1152
|
+
|
|
1153
|
+
Growth diagrams with different shapes are different::
|
|
1154
|
+
|
|
1155
|
+
sage: G1 = GrowthDiagram(RuleRSK, [[0,1,0],[1,0]])
|
|
1156
|
+
sage: G2 = GrowthDiagram(RuleRSK, [[0,1,0],[1]])
|
|
1157
|
+
sage: G1 != G2
|
|
1158
|
+
True
|
|
1159
|
+
|
|
1160
|
+
Growth diagrams with different rules are different::
|
|
1161
|
+
|
|
1162
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
1163
|
+
sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
|
|
1164
|
+
sage: G1 = GrowthDiagram(RuleRSK, {(0, 1): 1, (1, 0): 1})
|
|
1165
|
+
sage: G2 = GrowthDiagram(BinaryWord, {(0, 1): 1, (1, 0): 1})
|
|
1166
|
+
sage: G1 != G2
|
|
1167
|
+
True
|
|
1168
|
+
"""
|
|
1169
|
+
return not (self == other)
|
|
1170
|
+
|
|
1171
|
+
def _process_labels(self, labels):
|
|
1172
|
+
r"""
|
|
1173
|
+
Return the list of labels such that each element has the
|
|
1174
|
+
correct type from the rule.
|
|
1175
|
+
|
|
1176
|
+
.. WARNING::
|
|
1177
|
+
|
|
1178
|
+
Assumes that ``self.rule`` is set.
|
|
1179
|
+
|
|
1180
|
+
EXAMPLES::
|
|
1181
|
+
|
|
1182
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
1183
|
+
sage: labels = [[], [1], [1,1], [1], []]
|
|
1184
|
+
sage: G = GrowthDiagram(RuleRSK, labels=labels) # indirect doctest
|
|
1185
|
+
sage: G.out_labels()[2].parent()
|
|
1186
|
+
Partitions
|
|
1187
|
+
"""
|
|
1188
|
+
rule = self.rule
|
|
1189
|
+
if rule.has_multiple_edges:
|
|
1190
|
+
return [rule.normalize_vertex(val) if i % 2 == 0 else val
|
|
1191
|
+
for i, val in enumerate(labels)]
|
|
1192
|
+
else:
|
|
1193
|
+
return [rule.normalize_vertex(la) for la in labels]
|
|
1194
|
+
|
|
1195
|
+
def _shape_from_labels(self, labels):
|
|
1196
|
+
r"""
|
|
1197
|
+
Determine the shape of the growth diagram given a list of labels
|
|
1198
|
+
during initialization.
|
|
1199
|
+
|
|
1200
|
+
The shape can be determined from the labels if the size of
|
|
1201
|
+
each label differs from the size of its successor.
|
|
1202
|
+
|
|
1203
|
+
Otherwise raise an error.
|
|
1204
|
+
|
|
1205
|
+
.. WARNING::
|
|
1206
|
+
|
|
1207
|
+
Assumes that ``self.rule`` and ``self.rank`` is set.
|
|
1208
|
+
|
|
1209
|
+
TESTS::
|
|
1210
|
+
|
|
1211
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
1212
|
+
sage: labels = [[],[2],[1],[],[1],[]]
|
|
1213
|
+
sage: G = GrowthDiagram(RuleRSK, labels=labels); G
|
|
1214
|
+
0 1
|
|
1215
|
+
1
|
|
1216
|
+
1
|
|
1217
|
+
sage: G._shape_from_labels(G.out_labels())
|
|
1218
|
+
[2, 1, 1]
|
|
1219
|
+
|
|
1220
|
+
sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
|
|
1221
|
+
sage: Shifted({(0, 0): 1}).out_labels()
|
|
1222
|
+
[[], 1, [1], 0, []]
|
|
1223
|
+
sage: Shifted(labels=[[], 1, [2], 0, []]) # indirect doctest
|
|
1224
|
+
Traceback (most recent call last):
|
|
1225
|
+
...
|
|
1226
|
+
ValueError: [] has smaller rank than [2] but there is no edge of color 1 in Q
|
|
1227
|
+
"""
|
|
1228
|
+
# we can determine the shape even if is_P_edge is not implemented
|
|
1229
|
+
rule = self.rule
|
|
1230
|
+
is_P_edge = getattr(rule, "is_P_edge", None)
|
|
1231
|
+
is_Q_edge = getattr(rule, "is_Q_edge", None)
|
|
1232
|
+
if rule.has_multiple_edges:
|
|
1233
|
+
def right_left_multi(la, mu, e) -> int:
|
|
1234
|
+
if rule.rank(la) < rule.rank(mu):
|
|
1235
|
+
if is_Q_edge is not None and e not in is_Q_edge(la, mu):
|
|
1236
|
+
raise ValueError("%s has smaller rank than %s but there is no edge of color %s in Q" % (la, mu, e))
|
|
1237
|
+
return 1
|
|
1238
|
+
elif rule.rank(la) > rule.rank(mu):
|
|
1239
|
+
if is_P_edge is not None and e not in is_P_edge(mu, la):
|
|
1240
|
+
raise ValueError("%s has smaller rank than %s but there is no edge of color %s in P" % (mu, la, e))
|
|
1241
|
+
return 0
|
|
1242
|
+
raise ValueError("can only determine the shape of the growth"
|
|
1243
|
+
" diagram if ranks of successive labels differ")
|
|
1244
|
+
return _Partitions.from_zero_one([right_left_multi(labels[i], labels[i+2], labels[i+1])
|
|
1245
|
+
for i in range(0, len(labels)-2, 2)])
|
|
1246
|
+
else:
|
|
1247
|
+
def right_left(la, mu) -> int:
|
|
1248
|
+
if rule.rank(la) < rule.rank(mu):
|
|
1249
|
+
if is_Q_edge is not None and not is_Q_edge(la, mu):
|
|
1250
|
+
raise ValueError("%s has smaller rank than %s but is not covered by it in Q" % (la, mu))
|
|
1251
|
+
return 1
|
|
1252
|
+
elif rule.rank(la) > rule.rank(mu):
|
|
1253
|
+
if is_P_edge is not None and not is_P_edge(mu, la):
|
|
1254
|
+
raise ValueError("%s has smaller rank than %s but is not covered by it in P" % (mu, la))
|
|
1255
|
+
return 0
|
|
1256
|
+
raise ValueError("can only determine the shape of the growth"
|
|
1257
|
+
" diagram if ranks of successive labels differ")
|
|
1258
|
+
return _Partitions.from_zero_one([right_left(labels[i], labels[i+1])
|
|
1259
|
+
for i in range(len(labels)-1)])
|
|
1260
|
+
|
|
1261
|
+
def _check_labels(self, labels):
|
|
1262
|
+
r"""
|
|
1263
|
+
Check sanity of the parameter ``labels``.
|
|
1264
|
+
|
|
1265
|
+
.. WARNING::
|
|
1266
|
+
|
|
1267
|
+
Assumes that ``self.rule`` and ``self._lambda`` is set.
|
|
1268
|
+
|
|
1269
|
+
TESTS::
|
|
1270
|
+
|
|
1271
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
1272
|
+
sage: GrowthDiagram(RuleRSK, shape=[1], labels=[[], [1]]) # indirect doctest
|
|
1273
|
+
Traceback (most recent call last):
|
|
1274
|
+
...
|
|
1275
|
+
ValueError: the number of labels is 2, but for this shape we need 3
|
|
1276
|
+
|
|
1277
|
+
sage: GrowthDiagram(RuleRSK, labels=[[], [1], [2], [2,1]]) # indirect doctest
|
|
1278
|
+
Traceback (most recent call last):
|
|
1279
|
+
...
|
|
1280
|
+
ValueError: the number of labels is 4, but for this shape we need 1
|
|
1281
|
+
|
|
1282
|
+
.. TODO::
|
|
1283
|
+
|
|
1284
|
+
Can we do something more sensible when the chain
|
|
1285
|
+
of labels is strictly increasing?
|
|
1286
|
+
"""
|
|
1287
|
+
half_perimeter = self.half_perimeter()
|
|
1288
|
+
if self.rule.has_multiple_edges:
|
|
1289
|
+
if not (len(labels) % 2):
|
|
1290
|
+
raise ValueError("only a list of odd length can specify a path, but %s has even length" % len(labels))
|
|
1291
|
+
path_length = (len(labels) + 1) / 2
|
|
1292
|
+
else:
|
|
1293
|
+
path_length = len(labels)
|
|
1294
|
+
|
|
1295
|
+
if path_length != half_perimeter:
|
|
1296
|
+
raise ValueError("the number of labels is %s, but for this shape we need %s"
|
|
1297
|
+
% (path_length, half_perimeter))
|
|
1298
|
+
|
|
1299
|
+
def _process_shape(self, shape):
|
|
1300
|
+
r"""
|
|
1301
|
+
Return a pair of partitions as lists describing the region
|
|
1302
|
+
of the growth diagram.
|
|
1303
|
+
|
|
1304
|
+
TESTS:
|
|
1305
|
+
|
|
1306
|
+
``shape`` is a skew partition::
|
|
1307
|
+
|
|
1308
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
1309
|
+
sage: filling = []
|
|
1310
|
+
sage: shape = SkewPartition([[4,2,1,1],[2,1,1]])
|
|
1311
|
+
sage: G = GrowthDiagram(RuleRSK, filling, shape) # indirect doctest
|
|
1312
|
+
sage: G._lambda, G._mu
|
|
1313
|
+
([4, 2, 1, 1], [2, 1, 1, 0])
|
|
1314
|
+
|
|
1315
|
+
``shape`` is a partition::
|
|
1316
|
+
|
|
1317
|
+
sage: filling = []
|
|
1318
|
+
sage: shape = Partition([3,2,1,1])
|
|
1319
|
+
sage: G = GrowthDiagram(RuleRSK, filling, shape) # indirect doctest
|
|
1320
|
+
sage: G._lambda, G._mu
|
|
1321
|
+
([3, 2, 1, 1], [0, 0, 0, 0])
|
|
1322
|
+
"""
|
|
1323
|
+
try:
|
|
1324
|
+
shape = _Partitions(shape)
|
|
1325
|
+
except ValueError:
|
|
1326
|
+
try:
|
|
1327
|
+
shape = SkewPartition(shape)
|
|
1328
|
+
except ValueError:
|
|
1329
|
+
raise ValueError("cannot make sense of shape %s" % shape)
|
|
1330
|
+
return ( list(shape[0]),
|
|
1331
|
+
list(shape[1]) + [0]*(len(shape[0])-len(shape[1])) )
|
|
1332
|
+
return (list(shape), [0]*len(shape))
|
|
1333
|
+
|
|
1334
|
+
def _process_filling_and_shape(self, filling, shape):
|
|
1335
|
+
r"""
|
|
1336
|
+
Return a dict ``F`` such that ``F[(i,j)]`` is the element in row
|
|
1337
|
+
``i`` and column ``j`` and a pair of partitions describing the
|
|
1338
|
+
region of the growth diagram.
|
|
1339
|
+
|
|
1340
|
+
TESTS:
|
|
1341
|
+
|
|
1342
|
+
``filling`` is a dict of coordinates::
|
|
1343
|
+
|
|
1344
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
1345
|
+
sage: pi = Permutation([2,3,1,6,4,5])
|
|
1346
|
+
sage: G = GrowthDiagram(RuleRSK, {(i,pi[i]-1):1 for i in range(len(pi))}) # indirect doctest
|
|
1347
|
+
sage: G._filling
|
|
1348
|
+
{(0, 1): 1, (1, 2): 1, (2, 0): 1, (3, 5): 1, (4, 3): 1, (5, 4): 1}
|
|
1349
|
+
sage: G.shape()
|
|
1350
|
+
[6, 6, 6, 6, 6, 6] / []
|
|
1351
|
+
|
|
1352
|
+
``filling`` is a dict of dicts::
|
|
1353
|
+
|
|
1354
|
+
sage: G = GrowthDiagram(RuleRSK, {i:{pi[i]-1:1} for i in range(len(pi))}) # indirect doctest
|
|
1355
|
+
sage: G._filling
|
|
1356
|
+
{(0, 1): 1, (1, 2): 1, (2, 0): 1, (3, 5): 1, (4, 3): 1, (5, 4): 1}
|
|
1357
|
+
sage: G.shape()
|
|
1358
|
+
[6, 6, 6, 6, 6, 6] / []
|
|
1359
|
+
|
|
1360
|
+
``filling`` is a matrix::
|
|
1361
|
+
|
|
1362
|
+
sage: G = GrowthDiagram(RuleRSK, pi.to_matrix()) # indirect doctest # needs sage.modules
|
|
1363
|
+
sage: G._filling # needs sage.modules
|
|
1364
|
+
{(0, 1): 1, (1, 2): 1, (2, 0): 1, (3, 5): 1, (4, 3): 1, (5, 4): 1}
|
|
1365
|
+
sage: G.shape() # needs sage.modules
|
|
1366
|
+
[6, 6, 6, 6, 6, 6] / []
|
|
1367
|
+
|
|
1368
|
+
``filling`` is a permutation::
|
|
1369
|
+
|
|
1370
|
+
sage: G = GrowthDiagram(RuleRSK, pi) # indirect doctest
|
|
1371
|
+
sage: G._filling
|
|
1372
|
+
{(0, 1): 1, (1, 2): 1, (2, 0): 1, (3, 5): 1, (4, 3): 1, (5, 4): 1}
|
|
1373
|
+
sage: G.shape()
|
|
1374
|
+
[6, 6, 6, 6, 6, 6] / []
|
|
1375
|
+
|
|
1376
|
+
``filling`` is a list::
|
|
1377
|
+
|
|
1378
|
+
sage: G = GrowthDiagram(RuleRSK, [3,1,4,1,5]) # indirect doctest
|
|
1379
|
+
sage: G._filling
|
|
1380
|
+
{(0, 2): 1, (1, 0): 1, (2, 3): 1, (3, 0): 1, (4, 4): 1}
|
|
1381
|
+
sage: G.shape()
|
|
1382
|
+
[5, 5, 5, 5, 5] / []
|
|
1383
|
+
|
|
1384
|
+
``filling`` is a list of lists::
|
|
1385
|
+
|
|
1386
|
+
sage: G = GrowthDiagram(RuleRSK, [[1,0,1],[0,1]]) # indirect doctest
|
|
1387
|
+
sage: G._filling
|
|
1388
|
+
{(0, 0): 1, (1, 1): 1, (2, 0): 1}
|
|
1389
|
+
sage: G.shape()
|
|
1390
|
+
[3, 2] / []
|
|
1391
|
+
|
|
1392
|
+
``filling`` is a list of lists and shape is given::
|
|
1393
|
+
|
|
1394
|
+
sage: G = GrowthDiagram(RuleRSK, [[1,0,1],[0,1]], # indirect doctest
|
|
1395
|
+
....: shape=SkewPartition([[3,2],[1]]))
|
|
1396
|
+
sage: G._filling
|
|
1397
|
+
{(0, 0): 1, (1, 1): 1, (2, 0): 1}
|
|
1398
|
+
sage: G.shape()
|
|
1399
|
+
[3, 2] / [1]
|
|
1400
|
+
|
|
1401
|
+
``filling`` is empty and shape is ``None``::
|
|
1402
|
+
|
|
1403
|
+
sage: G = GrowthDiagram(RuleRSK, {})
|
|
1404
|
+
sage: (G.filling(), G.shape())
|
|
1405
|
+
({}, [] / [])
|
|
1406
|
+
"""
|
|
1407
|
+
if isinstance(filling, dict):
|
|
1408
|
+
try:
|
|
1409
|
+
v = next(iter(filling.values()))
|
|
1410
|
+
if isinstance(v, dict):
|
|
1411
|
+
# it is a dict of dicts
|
|
1412
|
+
F = dict()
|
|
1413
|
+
for (i, row) in filling.items():
|
|
1414
|
+
for (j, v) in row.items():
|
|
1415
|
+
if v != 0:
|
|
1416
|
+
F[(i,j)] = int(v)
|
|
1417
|
+
else:
|
|
1418
|
+
# it is dict of coordinates
|
|
1419
|
+
F = {(i,j): v for ((i,j), v) in filling.items()
|
|
1420
|
+
if v != 0}
|
|
1421
|
+
except StopIteration:
|
|
1422
|
+
# it is an empty dict of coordinates
|
|
1423
|
+
F = filling
|
|
1424
|
+
|
|
1425
|
+
else:
|
|
1426
|
+
# it is a sequence
|
|
1427
|
+
F = dict()
|
|
1428
|
+
try:
|
|
1429
|
+
# it is a sequence of sequences
|
|
1430
|
+
for i, row in enumerate(filling):
|
|
1431
|
+
for j, v in enumerate(row):
|
|
1432
|
+
if v != 0:
|
|
1433
|
+
F[j,i] = int(v)
|
|
1434
|
+
if shape is None:
|
|
1435
|
+
shape = [len(row) for row in filling]
|
|
1436
|
+
|
|
1437
|
+
except TypeError:
|
|
1438
|
+
# it is a word - for convenience we allow signed words
|
|
1439
|
+
for i, l in enumerate(filling):
|
|
1440
|
+
if l > 0:
|
|
1441
|
+
F[i, l-1] = 1
|
|
1442
|
+
else:
|
|
1443
|
+
F[i, -l-1] = -1
|
|
1444
|
+
|
|
1445
|
+
if shape is None:
|
|
1446
|
+
if F == {}:
|
|
1447
|
+
shape = []
|
|
1448
|
+
else:
|
|
1449
|
+
# find bounding rectangle of ``filling``
|
|
1450
|
+
max_row = max(i for i, _ in F)+1
|
|
1451
|
+
max_col = max(j for _, j in F)+1
|
|
1452
|
+
shape = [max_row] * max_col
|
|
1453
|
+
|
|
1454
|
+
return (F, self._process_shape(shape))
|
|
1455
|
+
|
|
1456
|
+
def _grow(self):
|
|
1457
|
+
r"""
|
|
1458
|
+
Compute the labels on the boundary opposite of the origin, given
|
|
1459
|
+
the filling.
|
|
1460
|
+
|
|
1461
|
+
TESTS::
|
|
1462
|
+
|
|
1463
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
1464
|
+
sage: pi = Permutation([1])
|
|
1465
|
+
sage: G = GrowthDiagram(RuleRSK, pi) # indirect doctest
|
|
1466
|
+
sage: G._out_labels
|
|
1467
|
+
[[], [1], []]
|
|
1468
|
+
|
|
1469
|
+
sage: pi = Permutation([1,2])
|
|
1470
|
+
sage: G = GrowthDiagram(RuleRSK, pi) # indirect doctest
|
|
1471
|
+
sage: G._out_labels
|
|
1472
|
+
[[], [1], [2], [1], []]
|
|
1473
|
+
|
|
1474
|
+
sage: pi = Permutation([2,1])
|
|
1475
|
+
sage: G = GrowthDiagram(RuleRSK, pi) # indirect doctest
|
|
1476
|
+
sage: G._out_labels
|
|
1477
|
+
[[], [1], [1, 1], [1], []]
|
|
1478
|
+
|
|
1479
|
+
sage: G = GrowthDiagram(RuleRSK, {(0,1):1, (1,0):1}, SkewPartition([[2,1],[1]])) # indirect doctest
|
|
1480
|
+
sage: G._out_labels
|
|
1481
|
+
[[], [1], [], [1], []]
|
|
1482
|
+
|
|
1483
|
+
sage: G = GrowthDiagram(RuleRSK, {(1,1):1}, SkewPartition([[2,2],[1]]), labels=[[],[],[1],[],[]]) # indirect doctest
|
|
1484
|
+
sage: G._out_labels
|
|
1485
|
+
[[], [1], [2], [1], []]
|
|
1486
|
+
|
|
1487
|
+
sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
|
|
1488
|
+
sage: G = GrowthDiagram(BinaryWord, {(1,1):1}, SkewPartition([[2,2],[1]]), labels=[[],[],[1],[],[]]) # indirect doctest
|
|
1489
|
+
sage: G._out_labels
|
|
1490
|
+
[word: , word: 1, word: 11, word: 1, word: ]
|
|
1491
|
+
"""
|
|
1492
|
+
labels = list(self._in_labels)
|
|
1493
|
+
l = len(self._lambda)
|
|
1494
|
+
rule = self.rule
|
|
1495
|
+
if rule.has_multiple_edges:
|
|
1496
|
+
for r in range(l):
|
|
1497
|
+
for c in range(self._mu[r]+l-r, self._lambda[r]+l-r):
|
|
1498
|
+
j = r
|
|
1499
|
+
i = c-l+r
|
|
1500
|
+
(labels[2*c-1],
|
|
1501
|
+
labels[2*c],
|
|
1502
|
+
labels[2*c+1]) = rule.forward_rule(labels[2*c-2],
|
|
1503
|
+
labels[2*c-1],
|
|
1504
|
+
labels[2*c],
|
|
1505
|
+
labels[2*c+1],
|
|
1506
|
+
labels[2*c+2],
|
|
1507
|
+
self._filling.get((i,j), 0))
|
|
1508
|
+
else:
|
|
1509
|
+
for r in range(l):
|
|
1510
|
+
for c in range(self._mu[r]+l-r, self._lambda[r]+l-r):
|
|
1511
|
+
j = r
|
|
1512
|
+
i = c-l+r
|
|
1513
|
+
labels[c] = rule.forward_rule(labels[c-1],
|
|
1514
|
+
labels[c],
|
|
1515
|
+
labels[c+1],
|
|
1516
|
+
self._filling.get((i,j), 0))
|
|
1517
|
+
|
|
1518
|
+
self._out_labels = labels
|
|
1519
|
+
|
|
1520
|
+
def _shrink(self):
|
|
1521
|
+
r"""
|
|
1522
|
+
Compute the labels on the boundary near the origin, and the filling.
|
|
1523
|
+
|
|
1524
|
+
TESTS::
|
|
1525
|
+
|
|
1526
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
1527
|
+
sage: filling = [[0,0,1,0,0,0,0], [0,1,0,0,0,0,0], [1,0,0,0,0,0,0],
|
|
1528
|
+
....: [0,0,0,1,0,0,0], [0,0,0,0,0,0,1],
|
|
1529
|
+
....: [0,0,0,0,0,1,0], [0,0,0,0,1,0,0]]
|
|
1530
|
+
sage: G = GrowthDiagram(RuleRSK, filling)
|
|
1531
|
+
sage: list(GrowthDiagram(RuleRSK, labels=G._out_labels)) == filling
|
|
1532
|
+
True
|
|
1533
|
+
|
|
1534
|
+
sage: labels = [[], [1], []]
|
|
1535
|
+
sage: G = GrowthDiagram(RuleRSK, labels=labels) # indirect doctest
|
|
1536
|
+
sage: G._filling
|
|
1537
|
+
{(0, 0): 1}
|
|
1538
|
+
sage: G._in_labels
|
|
1539
|
+
[[], [], []]
|
|
1540
|
+
|
|
1541
|
+
sage: labels = [[], [1], [2], [2,1], [1,1], [1], []]
|
|
1542
|
+
sage: G = GrowthDiagram(RuleRSK, labels=labels) # indirect doctest
|
|
1543
|
+
sage: G._filling
|
|
1544
|
+
{(0, 1): 1, (1, 2): 1, (2, 0): 1}
|
|
1545
|
+
sage: G._in_labels
|
|
1546
|
+
[[], [], [], [], [], [], []]
|
|
1547
|
+
|
|
1548
|
+
sage: labels = [[], [1], [2], [3], [3, 1], [3, 2], [4, 2], [4, 1], [3, 1], [2, 1], [1, 1], [1], []]
|
|
1549
|
+
sage: G = GrowthDiagram(RuleRSK, labels=labels) # indirect doctest
|
|
1550
|
+
sage: G._filling
|
|
1551
|
+
{(0, 1): 1, (1, 2): 1, (2, 5): 1, (3, 0): 1, (4, 3): 1, (5, 4): 1}
|
|
1552
|
+
|
|
1553
|
+
sage: labels = [[],[1],[1],[2],[2],[2,1],[2]]
|
|
1554
|
+
sage: G = GrowthDiagram(RuleRSK, labels=labels)
|
|
1555
|
+
Traceback (most recent call last):
|
|
1556
|
+
...
|
|
1557
|
+
ValueError: can only determine the shape of the growth diagram
|
|
1558
|
+
if ranks of successive labels differ
|
|
1559
|
+
sage: G = GrowthDiagram(RuleRSK, shape=[3,2,1], labels=labels) # indirect doctest
|
|
1560
|
+
sage: G._filling
|
|
1561
|
+
{(1, 0): 1}
|
|
1562
|
+
sage: G._in_labels
|
|
1563
|
+
[[], [], [], [], [1], [1], [2]]
|
|
1564
|
+
|
|
1565
|
+
sage: labels = [[], [1],[1],[2],[2],[2,1],[2],[2,1],[1,1],[2,1],[1,1]]
|
|
1566
|
+
sage: G = GrowthDiagram(RuleRSK, shape=[5,4,3,2,1], labels=labels) # indirect doctest
|
|
1567
|
+
sage: G._filling
|
|
1568
|
+
{(1, 2): 1, (2, 1): 1, (4, 0): 1}
|
|
1569
|
+
sage: G._in_labels
|
|
1570
|
+
[[], [], [], [], [], [], [1], [1], [1], [1, 1], [1, 1]]
|
|
1571
|
+
|
|
1572
|
+
sage: labels = [[], [1],[1],[2],[2],[2,1],[2],[2,1],[1,1],[2,1],[1,1]]
|
|
1573
|
+
sage: G = GrowthDiagram(RuleRSK, shape=SkewPartition([[5,4,3,2,1],[3,2,1]]), labels=labels) # indirect doctest
|
|
1574
|
+
sage: G._filling
|
|
1575
|
+
{(1, 2): 1, (2, 1): 1, (4, 0): 1}
|
|
1576
|
+
sage: G._in_labels
|
|
1577
|
+
[[], [], [], [1], [1], [1], [1], [1], [1], [1, 1], [1, 1]]
|
|
1578
|
+
"""
|
|
1579
|
+
F = dict()
|
|
1580
|
+
labels = list(self._out_labels)
|
|
1581
|
+
l = len(self._lambda)
|
|
1582
|
+
rule = self.rule
|
|
1583
|
+
if rule.has_multiple_edges:
|
|
1584
|
+
for r in range(l):
|
|
1585
|
+
for c in range(self._lambda[l-r-1]+r, self._mu[l-r-1]+r, -1):
|
|
1586
|
+
j = l-r-1
|
|
1587
|
+
i = c-r-1
|
|
1588
|
+
(labels[2*c-1],
|
|
1589
|
+
labels[2*c],
|
|
1590
|
+
labels[2*c+1], v) = rule.backward_rule(labels[2*c-2],
|
|
1591
|
+
labels[2*c-1],
|
|
1592
|
+
labels[2*c],
|
|
1593
|
+
labels[2*c+1],
|
|
1594
|
+
labels[2*c+2])
|
|
1595
|
+
if v != 0:
|
|
1596
|
+
F[(i,j)] = v
|
|
1597
|
+
|
|
1598
|
+
else:
|
|
1599
|
+
for r in range(l):
|
|
1600
|
+
for c in range(self._lambda[l-r-1]+r, self._mu[l-r-1]+r, -1):
|
|
1601
|
+
j = l-r-1
|
|
1602
|
+
i = c-r-1
|
|
1603
|
+
labels[c], v = rule.backward_rule(labels[c-1],
|
|
1604
|
+
labels[c],
|
|
1605
|
+
labels[c+1])
|
|
1606
|
+
if v != 0:
|
|
1607
|
+
F[(i,j)] = v
|
|
1608
|
+
|
|
1609
|
+
self._in_labels = labels
|
|
1610
|
+
self._filling = F
|
|
1611
|
+
|
|
1612
|
+
######################################################################
|
|
1613
|
+
# ABC for rules of growth diagrams
|
|
1614
|
+
######################################################################
|
|
1615
|
+
|
|
1616
|
+
|
|
1617
|
+
class Rule(UniqueRepresentation):
|
|
1618
|
+
r"""
|
|
1619
|
+
Generic base class for a rule for a growth diagram.
|
|
1620
|
+
|
|
1621
|
+
Subclasses may provide the following attributes:
|
|
1622
|
+
|
|
1623
|
+
- ``zero`` -- the zero element of the vertices of the graphs
|
|
1624
|
+
|
|
1625
|
+
- ``r`` -- (default: 1) the parameter in the equation `DU - UD = rI`
|
|
1626
|
+
|
|
1627
|
+
- ``has_multiple_edges`` -- boolean (default: ``False``); if the dual
|
|
1628
|
+
graded graph has multiple edges and therefore edges are
|
|
1629
|
+
triples consisting of two vertices and a label
|
|
1630
|
+
|
|
1631
|
+
- ``zero_edge`` -- (default: 0) the zero label of the
|
|
1632
|
+
edges of the graphs used for degenerate edges. It is
|
|
1633
|
+
allowed to use this label also for other edges.
|
|
1634
|
+
|
|
1635
|
+
Subclasses may provide the following methods:
|
|
1636
|
+
|
|
1637
|
+
- ``normalize_vertex`` -- a function that converts its input to a
|
|
1638
|
+
vertex
|
|
1639
|
+
|
|
1640
|
+
- ``vertices`` -- a function that takes a nonnegative integer
|
|
1641
|
+
as input and returns the list of vertices on this rank
|
|
1642
|
+
|
|
1643
|
+
- ``rank`` -- the rank function of the dual graded graphs
|
|
1644
|
+
|
|
1645
|
+
- ``forward_rule`` -- a function with input ``(y, t, x,
|
|
1646
|
+
content)`` or ``(y, e, t, f, x, content)`` if
|
|
1647
|
+
``has_multiple_edges`` is ``True``. ``(y, e, t)`` is an
|
|
1648
|
+
edge in the graph `P`, ``(t, f, x)`` an edge in the graph
|
|
1649
|
+
``Q``. It should return the fourth vertex ``z``, or, if
|
|
1650
|
+
``has_multiple_edges`` is ``True``, the path ``(g, z, h)``
|
|
1651
|
+
from ``y`` to ``x``.
|
|
1652
|
+
|
|
1653
|
+
- ``backward_rule`` -- a function with input ``(y, z, x)`` or
|
|
1654
|
+
``(y, g, z, h, x)`` if ``has_multiple_edges`` is ``True``.
|
|
1655
|
+
``(y, g, z)`` is an edge in the graph `Q`, ``(z, h, x)`` an
|
|
1656
|
+
edge in the graph ``P``. It should return the fourth
|
|
1657
|
+
vertex and the content ``(t, content)``, or, if
|
|
1658
|
+
``has_multiple_edges`` is ``True``, the path from ``y`` to
|
|
1659
|
+
``x`` and the content as ``(e, t, f, content)``.
|
|
1660
|
+
|
|
1661
|
+
- ``is_P_edge``, ``is_Q_edge`` -- functions that take two
|
|
1662
|
+
vertices as arguments and return ``True`` or ``False``, or,
|
|
1663
|
+
if multiple edges are allowed, the list of edge labels of
|
|
1664
|
+
the edges from the first vertex to the second in the
|
|
1665
|
+
respective graded graph. These are only used for checking
|
|
1666
|
+
user input and providing the dual graded graph, and are
|
|
1667
|
+
therefore not mandatory.
|
|
1668
|
+
|
|
1669
|
+
Note that the class :class:`GrowthDiagram` is able to use
|
|
1670
|
+
partially implemented subclasses just fine. Suppose that
|
|
1671
|
+
``MyRule`` is such a subclass. Then:
|
|
1672
|
+
|
|
1673
|
+
- ``GrowthDiagram(MyRule, my_filling)`` requires only an
|
|
1674
|
+
implementation of ``forward_rule``, ``zero`` and possibly
|
|
1675
|
+
``has_multiple_edges``.
|
|
1676
|
+
|
|
1677
|
+
- ``GrowthDiagram(MyRule, labels=my_labels, shape=my_shape)``
|
|
1678
|
+
requires only an implementation of ``backward_rule`` and
|
|
1679
|
+
possibly ``has_multiple_edges``, provided that the labels
|
|
1680
|
+
``my_labels`` are given as needed by ``backward_rule``.
|
|
1681
|
+
|
|
1682
|
+
- ``GrowthDiagram(MyRule, labels=my_labels)`` additionally needs
|
|
1683
|
+
an implementation of ``rank`` to deduce the shape.
|
|
1684
|
+
|
|
1685
|
+
In particular, this allows to implement rules which do not quite
|
|
1686
|
+
fit Fomin's notion of dual graded graphs. An example would be
|
|
1687
|
+
Bloom and Saracino's variant of the RSK correspondence [BS2012]_,
|
|
1688
|
+
where a backward rule is not available.
|
|
1689
|
+
|
|
1690
|
+
Similarly:
|
|
1691
|
+
|
|
1692
|
+
- ``MyRule.P_graph`` only requires an implementation of
|
|
1693
|
+
``vertices``, ``is_P_edge`` and possibly ``has_multiple_edges``
|
|
1694
|
+
is required, mutatis mutandis for ``MyRule.Q_graph``.
|
|
1695
|
+
|
|
1696
|
+
- ``MyRule._check_duality`` requires ``P_graph`` and ``Q_graph``.
|
|
1697
|
+
|
|
1698
|
+
In particular, this allows to work with dual graded graphs
|
|
1699
|
+
without local rules.
|
|
1700
|
+
"""
|
|
1701
|
+
has_multiple_edges = False # override when necessary
|
|
1702
|
+
zero_edge = 0 # override when necessary
|
|
1703
|
+
r = 1 # override when necessary
|
|
1704
|
+
|
|
1705
|
+
def normalize_vertex(self, v): # override when necessary
|
|
1706
|
+
r"""
|
|
1707
|
+
Return ``v`` as a vertex of the dual graded graph.
|
|
1708
|
+
|
|
1709
|
+
This is a default implementation, returning its argument.
|
|
1710
|
+
|
|
1711
|
+
EXAMPLES::
|
|
1712
|
+
|
|
1713
|
+
sage: from sage.combinat.growth import Rule
|
|
1714
|
+
sage: Rule().normalize_vertex("hello") == "hello"
|
|
1715
|
+
True
|
|
1716
|
+
"""
|
|
1717
|
+
return v
|
|
1718
|
+
|
|
1719
|
+
def __call__(self, *args, **kwds):
|
|
1720
|
+
r"""
|
|
1721
|
+
Return the growth diagram corresponding to the parameters.
|
|
1722
|
+
|
|
1723
|
+
This provides a shorthand for calling :class:`GrowthDiagram`
|
|
1724
|
+
directly.
|
|
1725
|
+
|
|
1726
|
+
EXAMPLES::
|
|
1727
|
+
|
|
1728
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
1729
|
+
sage: RuleRSK([2,3,1], shape=[3,2,2])
|
|
1730
|
+
0 0 1
|
|
1731
|
+
1 0
|
|
1732
|
+
0 1
|
|
1733
|
+
|
|
1734
|
+
sage: RuleRSK(labels=[[], [1], [2], [1], [], [1], []])
|
|
1735
|
+
0 0 1
|
|
1736
|
+
1 0
|
|
1737
|
+
0 1
|
|
1738
|
+
"""
|
|
1739
|
+
return GrowthDiagram(self, *args, **kwds)
|
|
1740
|
+
|
|
1741
|
+
def _check_duality(self, n):
|
|
1742
|
+
r"""
|
|
1743
|
+
Raise an error if the graphs are not `r`-dual at level ``n``.
|
|
1744
|
+
|
|
1745
|
+
`P` and `Q` are `r`-dual if `DU = UD + rI` on the free
|
|
1746
|
+
`\ZZ`-module `\ZZ[V]`, where `D` is the down operator of `Q`,
|
|
1747
|
+
assigning to each vertex the formal sum of its predecessors,
|
|
1748
|
+
`U` is the up operator of `P`, assigning to each vertex the
|
|
1749
|
+
formal sum of its successors, and `I` is the identity
|
|
1750
|
+
operator.
|
|
1751
|
+
|
|
1752
|
+
INPUT:
|
|
1753
|
+
|
|
1754
|
+
- ``n`` -- positive integer specifying which rank of
|
|
1755
|
+
the graph to test
|
|
1756
|
+
|
|
1757
|
+
EXAMPLES:
|
|
1758
|
+
|
|
1759
|
+
For binary words, we have indeed provided dual graded graphs::
|
|
1760
|
+
|
|
1761
|
+
sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
|
|
1762
|
+
sage: BinaryWord._check_duality(3)
|
|
1763
|
+
|
|
1764
|
+
The following two graphs are not `1`-dual::
|
|
1765
|
+
|
|
1766
|
+
sage: from sage.combinat.growth import Rule
|
|
1767
|
+
sage: class RuleWrong(Rule):
|
|
1768
|
+
....: def vertices(self, n): return Partitions(n)
|
|
1769
|
+
....: def is_Q_edge(self, v, w):
|
|
1770
|
+
....: return (v, w) in [([1],[2]), ([2],[3])]
|
|
1771
|
+
....: def is_P_edge(self, v, w):
|
|
1772
|
+
....: return (v, w) in [([1],[2]), ([1],[1,1]), ([2],[3])]
|
|
1773
|
+
|
|
1774
|
+
sage: RuleWrong()._check_duality(2)
|
|
1775
|
+
Traceback (most recent call last):
|
|
1776
|
+
...
|
|
1777
|
+
ValueError: D U - U D differs from 1 I for vertex [2]:
|
|
1778
|
+
D U = [[2]]
|
|
1779
|
+
U D + 1 I = [[1, 1], [2], [2]]
|
|
1780
|
+
"""
|
|
1781
|
+
if self.has_multiple_edges:
|
|
1782
|
+
def check_vertex(w, P, Q):
|
|
1783
|
+
DUw = [v[0] for uw in P.outgoing_edges(w) for v in Q.incoming_edges(uw[1])]
|
|
1784
|
+
UDw = [v[1] for lw in Q.incoming_edges(w) for v in P.outgoing_edges(lw[0])]
|
|
1785
|
+
UDw.extend([w]*self.r)
|
|
1786
|
+
if sorted(DUw) != sorted(UDw):
|
|
1787
|
+
raise ValueError("D U - U D differs from %s I for vertex %s:\n"
|
|
1788
|
+
"D U = %s\n"
|
|
1789
|
+
"U D + %s I = %s"
|
|
1790
|
+
% (self.r, w, DUw, self.r, UDw))
|
|
1791
|
+
else:
|
|
1792
|
+
def check_vertex(w, P, Q):
|
|
1793
|
+
DUw = [v for uw in P.upper_covers(w) for v in Q.lower_covers(uw)]
|
|
1794
|
+
UDw = [v for lw in Q.lower_covers(w) for v in P.upper_covers(lw)]
|
|
1795
|
+
UDw.extend([w]*self.r)
|
|
1796
|
+
if sorted(DUw) != sorted(UDw):
|
|
1797
|
+
raise ValueError("D U - U D differs from %s I for vertex %s:\n"
|
|
1798
|
+
"D U = %s\n"
|
|
1799
|
+
"U D + %s I = %s"
|
|
1800
|
+
% (self.r, w, DUw, self.r, UDw))
|
|
1801
|
+
|
|
1802
|
+
P = self.P_graph(n + 2)
|
|
1803
|
+
Q = self.Q_graph(n + 2)
|
|
1804
|
+
for w in self.vertices(n):
|
|
1805
|
+
check_vertex(w, P, Q)
|
|
1806
|
+
|
|
1807
|
+
def P_graph(self, n):
|
|
1808
|
+
r"""
|
|
1809
|
+
Return the first ``n`` levels of the first dual graded graph.
|
|
1810
|
+
|
|
1811
|
+
The non-degenerate edges in the vertical direction come from
|
|
1812
|
+
this graph.
|
|
1813
|
+
|
|
1814
|
+
EXAMPLES::
|
|
1815
|
+
|
|
1816
|
+
sage: Domino = GrowthDiagram.rules.Domino()
|
|
1817
|
+
sage: Domino.P_graph(3)
|
|
1818
|
+
Finite poset containing 8 elements
|
|
1819
|
+
"""
|
|
1820
|
+
if self.has_multiple_edges:
|
|
1821
|
+
D = DiGraph([(x,y,e) for k in range(n-1)
|
|
1822
|
+
for x in self.vertices(k)
|
|
1823
|
+
for y in self.vertices(k+1)
|
|
1824
|
+
for e in self.is_P_edge(x, y)], multiedges=True)
|
|
1825
|
+
# unfortunately, layout_acyclic will not show multiple edges
|
|
1826
|
+
# D.layout_default = D.layout_acyclic
|
|
1827
|
+
return D
|
|
1828
|
+
else:
|
|
1829
|
+
return Poset(([w for k in range(n) for w in self.vertices(k)],
|
|
1830
|
+
self.is_P_edge), cover_relations=True)
|
|
1831
|
+
|
|
1832
|
+
def Q_graph(self, n):
|
|
1833
|
+
r"""
|
|
1834
|
+
Return the first ``n`` levels of the second dual graded graph.
|
|
1835
|
+
|
|
1836
|
+
The non-degenerate edges in the horizontal direction come
|
|
1837
|
+
from this graph.
|
|
1838
|
+
|
|
1839
|
+
EXAMPLES::
|
|
1840
|
+
|
|
1841
|
+
sage: Domino = GrowthDiagram.rules.Domino()
|
|
1842
|
+
sage: Q = Domino.Q_graph(3); Q
|
|
1843
|
+
Finite poset containing 8 elements
|
|
1844
|
+
|
|
1845
|
+
sage: Q.upper_covers(Partition([1,1]))
|
|
1846
|
+
[[1, 1, 1, 1], [3, 1], [2, 2]]
|
|
1847
|
+
"""
|
|
1848
|
+
if self.has_multiple_edges:
|
|
1849
|
+
D = DiGraph([(x, y, e) for k in range(n - 1)
|
|
1850
|
+
for x in self.vertices(k)
|
|
1851
|
+
for y in self.vertices(k + 1)
|
|
1852
|
+
for e in self.is_Q_edge(x, y)], multiedges=True)
|
|
1853
|
+
# unfortunately, layout_acyclic will not show multiple edges
|
|
1854
|
+
# D.layout_default = D.layout_acyclic
|
|
1855
|
+
return D
|
|
1856
|
+
else:
|
|
1857
|
+
return Poset(([w for k in range(n) for w in self.vertices(k)],
|
|
1858
|
+
self.is_Q_edge), cover_relations=True)
|
|
1859
|
+
|
|
1860
|
+
######################################################################
|
|
1861
|
+
# Specific rules of growth diagrams
|
|
1862
|
+
######################################################################
|
|
1863
|
+
|
|
1864
|
+
|
|
1865
|
+
class RuleShiftedShapes(Rule):
|
|
1866
|
+
r"""
|
|
1867
|
+
A class modelling the Schensted correspondence for shifted
|
|
1868
|
+
shapes.
|
|
1869
|
+
|
|
1870
|
+
This agrees with Sagan [Sag1987]_ and Worley's [Wor1984]_, and
|
|
1871
|
+
Haiman's [Hai1989]_ insertion algorithms, see Proposition 4.5.2
|
|
1872
|
+
of [Fom1995]_.
|
|
1873
|
+
|
|
1874
|
+
EXAMPLES::
|
|
1875
|
+
|
|
1876
|
+
sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
|
|
1877
|
+
sage: GrowthDiagram(Shifted, [3,1,2])
|
|
1878
|
+
0 1 0
|
|
1879
|
+
0 0 1
|
|
1880
|
+
1 0 0
|
|
1881
|
+
|
|
1882
|
+
The vertices of the dual graded graph are shifted shapes::
|
|
1883
|
+
|
|
1884
|
+
sage: Shifted.vertices(3)
|
|
1885
|
+
Partitions of the integer 3 satisfying constraints max_slope=-1
|
|
1886
|
+
|
|
1887
|
+
Let us check the example just before Corollary 3.2 in [Sag1987]_.
|
|
1888
|
+
Note that, instead of passing the rule to :class:`GrowthDiagram`,
|
|
1889
|
+
we can also call the rule to create growth diagrams::
|
|
1890
|
+
|
|
1891
|
+
sage: G = Shifted([2,6,5,1,7,4,3])
|
|
1892
|
+
sage: G.P_chain()
|
|
1893
|
+
[[], 0, [1], 0, [2], 0, [3], 0, [3, 1], 0, [3, 2], 0, [4, 2], 0, [5, 2]]
|
|
1894
|
+
sage: G.Q_chain()
|
|
1895
|
+
[[], 1, [1], 2, [2], 1, [2, 1], 3, [3, 1], 2, [4, 1], 3, [4, 2], 3, [5, 2]]
|
|
1896
|
+
|
|
1897
|
+
TESTS::
|
|
1898
|
+
|
|
1899
|
+
sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
|
|
1900
|
+
sage: Shifted.zero
|
|
1901
|
+
[]
|
|
1902
|
+
|
|
1903
|
+
sage: Shifted._check_duality(4)
|
|
1904
|
+
|
|
1905
|
+
Check that the rules are bijective::
|
|
1906
|
+
|
|
1907
|
+
sage: all(Shifted(labels=Shifted(pi).out_labels()).to_word() == pi
|
|
1908
|
+
....: for pi in Permutations(5))
|
|
1909
|
+
True
|
|
1910
|
+
sage: pi = Permutations(10).random_element()
|
|
1911
|
+
sage: G = Shifted(pi)
|
|
1912
|
+
sage: list(Shifted(labels=G.out_labels())) == list(G)
|
|
1913
|
+
True
|
|
1914
|
+
"""
|
|
1915
|
+
zero = _make_partition([])
|
|
1916
|
+
has_multiple_edges = True
|
|
1917
|
+
|
|
1918
|
+
def normalize_vertex(self, v):
|
|
1919
|
+
r"""
|
|
1920
|
+
Return ``v`` as a partition.
|
|
1921
|
+
|
|
1922
|
+
EXAMPLES::
|
|
1923
|
+
|
|
1924
|
+
sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
|
|
1925
|
+
sage: Shifted.normalize_vertex([3,1]).parent()
|
|
1926
|
+
Partitions
|
|
1927
|
+
"""
|
|
1928
|
+
return _make_partition(v)
|
|
1929
|
+
|
|
1930
|
+
def vertices(self, n):
|
|
1931
|
+
r"""
|
|
1932
|
+
Return the vertices of the dual graded graph on level ``n``.
|
|
1933
|
+
|
|
1934
|
+
EXAMPLES::
|
|
1935
|
+
|
|
1936
|
+
sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
|
|
1937
|
+
sage: Shifted.vertices(3)
|
|
1938
|
+
Partitions of the integer 3 satisfying constraints max_slope=-1
|
|
1939
|
+
"""
|
|
1940
|
+
if n == 0:
|
|
1941
|
+
return [self.zero]
|
|
1942
|
+
else:
|
|
1943
|
+
return Partitions(n, max_slope=-1)
|
|
1944
|
+
|
|
1945
|
+
def rank(self, v):
|
|
1946
|
+
r"""
|
|
1947
|
+
Return the rank of ``v``: the size of the shifted partition.
|
|
1948
|
+
|
|
1949
|
+
EXAMPLES::
|
|
1950
|
+
|
|
1951
|
+
sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
|
|
1952
|
+
sage: Shifted.rank(Shifted.vertices(3)[0])
|
|
1953
|
+
3
|
|
1954
|
+
"""
|
|
1955
|
+
return v.size()
|
|
1956
|
+
|
|
1957
|
+
def is_Q_edge(self, v, w):
|
|
1958
|
+
r"""
|
|
1959
|
+
Return whether ``(v, w)`` is a `Q`-edge of ``self``.
|
|
1960
|
+
|
|
1961
|
+
``(v, w)`` is an edge if ``w`` is obtained from ``v`` by adding a
|
|
1962
|
+
cell. It is a black (color 1) edge, if the cell is on the
|
|
1963
|
+
diagonal, otherwise it can be blue or red (color 2 or 3).
|
|
1964
|
+
|
|
1965
|
+
EXAMPLES::
|
|
1966
|
+
|
|
1967
|
+
sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
|
|
1968
|
+
sage: v = Shifted.vertices(2)[0]; v
|
|
1969
|
+
[2]
|
|
1970
|
+
sage: [(w, Shifted.is_Q_edge(v, w)) for w in Shifted.vertices(3)]
|
|
1971
|
+
[([3], [2, 3]), ([2, 1], [1])]
|
|
1972
|
+
sage: all(Shifted.is_Q_edge(v, w) == [] for w in Shifted.vertices(4))
|
|
1973
|
+
True
|
|
1974
|
+
"""
|
|
1975
|
+
if self.rank(v) + 1 != self.rank(w):
|
|
1976
|
+
return []
|
|
1977
|
+
try:
|
|
1978
|
+
l = SkewPartition([w, v]).cells()
|
|
1979
|
+
except ValueError:
|
|
1980
|
+
return []
|
|
1981
|
+
else:
|
|
1982
|
+
if l[0][1] == 0:
|
|
1983
|
+
return [1] # black
|
|
1984
|
+
else:
|
|
1985
|
+
return [2,3] # blue, red
|
|
1986
|
+
|
|
1987
|
+
def is_P_edge(self, v, w):
|
|
1988
|
+
r"""
|
|
1989
|
+
Return whether ``(v, w)`` is a `P`-edge of ``self``.
|
|
1990
|
+
|
|
1991
|
+
``(v, w)`` is an edge if ``w`` contains ``v``.
|
|
1992
|
+
|
|
1993
|
+
EXAMPLES::
|
|
1994
|
+
|
|
1995
|
+
sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
|
|
1996
|
+
sage: v = Shifted.vertices(2)[0]; v
|
|
1997
|
+
[2]
|
|
1998
|
+
sage: [w for w in Shifted.vertices(3) if Shifted.is_P_edge(v, w)]
|
|
1999
|
+
[[3], [2, 1]]
|
|
2000
|
+
"""
|
|
2001
|
+
if self.rank(v) + 1 != self.rank(w):
|
|
2002
|
+
return []
|
|
2003
|
+
return [0] if w.contains(v) else []
|
|
2004
|
+
|
|
2005
|
+
def P_symbol(self, P_chain):
|
|
2006
|
+
r"""
|
|
2007
|
+
Return the labels along the vertical boundary of a rectangular
|
|
2008
|
+
growth diagram as a shifted tableau.
|
|
2009
|
+
|
|
2010
|
+
EXAMPLES:
|
|
2011
|
+
|
|
2012
|
+
Check the example just before Corollary 3.2 in [Sag1987]_::
|
|
2013
|
+
|
|
2014
|
+
sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
|
|
2015
|
+
sage: G = Shifted([2,6,5,1,7,4,3])
|
|
2016
|
+
sage: G.P_symbol().pp()
|
|
2017
|
+
1 2 3 6 7
|
|
2018
|
+
4 5
|
|
2019
|
+
|
|
2020
|
+
Check the example just before Corollary 8.2 in [SS1990]_::
|
|
2021
|
+
|
|
2022
|
+
sage: T = ShiftedPrimedTableau([[4],[1],[5]], skew=[3,1])
|
|
2023
|
+
sage: T.pp()
|
|
2024
|
+
. . . 4
|
|
2025
|
+
. 1
|
|
2026
|
+
5
|
|
2027
|
+
sage: U = ShiftedPrimedTableau([[1],[3.5],[5]], skew=[3,1])
|
|
2028
|
+
sage: U.pp()
|
|
2029
|
+
. . . 1
|
|
2030
|
+
. 4'
|
|
2031
|
+
5
|
|
2032
|
+
sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
|
|
2033
|
+
sage: labels = [mu if is_even(i) else 0
|
|
2034
|
+
....: for i, mu in enumerate(T.to_chain()[::-1])] + U.to_chain()[1:]
|
|
2035
|
+
sage: G = Shifted({(1,2):1, (2,1):1}, shape=[5,5,5,5,5], labels=labels)
|
|
2036
|
+
sage: G.P_symbol().pp()
|
|
2037
|
+
. . . . 2
|
|
2038
|
+
. . 1 3
|
|
2039
|
+
. 4 5
|
|
2040
|
+
"""
|
|
2041
|
+
chain = P_chain[::2]
|
|
2042
|
+
shape = chain[-1]
|
|
2043
|
+
T = [[None for _ in range(r)] for r in shape]
|
|
2044
|
+
for i in range(1,len(chain)):
|
|
2045
|
+
la = chain[i]
|
|
2046
|
+
mu = chain[i-1]
|
|
2047
|
+
mu += [0]*(len(la) - len(mu))
|
|
2048
|
+
|
|
2049
|
+
for r in range(len(la)):
|
|
2050
|
+
for c in range(mu[r], la[r]):
|
|
2051
|
+
T[r][c] = i
|
|
2052
|
+
|
|
2053
|
+
skew = _make_partition([row.count(None) for row in T])
|
|
2054
|
+
T = [[e for e in row if e is not None] for row in T]
|
|
2055
|
+
return ShiftedPrimedTableau(T, skew=skew)
|
|
2056
|
+
|
|
2057
|
+
def Q_symbol(self, Q_chain):
|
|
2058
|
+
r"""
|
|
2059
|
+
Return the labels along the horizontal boundary of a rectangular
|
|
2060
|
+
growth diagram as a skew tableau.
|
|
2061
|
+
|
|
2062
|
+
EXAMPLES:
|
|
2063
|
+
|
|
2064
|
+
Check the example just before Corollary 3.2 in [Sag1987]_::
|
|
2065
|
+
|
|
2066
|
+
sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
|
|
2067
|
+
sage: G = Shifted([2,6,5,1,7,4,3])
|
|
2068
|
+
sage: G.Q_symbol().pp()
|
|
2069
|
+
1 2 4' 5 7'
|
|
2070
|
+
3 6'
|
|
2071
|
+
|
|
2072
|
+
Check the example just before Corollary 8.2 in [SS1990]_::
|
|
2073
|
+
|
|
2074
|
+
sage: T = ShiftedPrimedTableau([[4],[1],[5]], skew=[3,1])
|
|
2075
|
+
sage: T.pp()
|
|
2076
|
+
. . . 4
|
|
2077
|
+
. 1
|
|
2078
|
+
5
|
|
2079
|
+
sage: U = ShiftedPrimedTableau([[1],[3.5],[5]], skew=[3,1])
|
|
2080
|
+
sage: U.pp()
|
|
2081
|
+
. . . 1
|
|
2082
|
+
. 4'
|
|
2083
|
+
5
|
|
2084
|
+
sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
|
|
2085
|
+
sage: labels = [mu if is_even(i) else 0
|
|
2086
|
+
....: for i, mu in enumerate(T.to_chain()[::-1])] + U.to_chain()[1:]
|
|
2087
|
+
sage: G = Shifted({(1,2):1, (2,1):1}, shape=[5,5,5,5,5], labels=labels)
|
|
2088
|
+
sage: G.Q_symbol().pp()
|
|
2089
|
+
. . . . 2
|
|
2090
|
+
. . 1 4'
|
|
2091
|
+
. 3' 5'
|
|
2092
|
+
"""
|
|
2093
|
+
chain = Q_chain
|
|
2094
|
+
shape = chain[-1]
|
|
2095
|
+
T = [[None for _ in range(r)] for r in shape]
|
|
2096
|
+
for i in range(1,(len(chain)+1)//2):
|
|
2097
|
+
la = chain[2*i]
|
|
2098
|
+
if chain[2*i-1] == 3:
|
|
2099
|
+
prime = 0.5
|
|
2100
|
+
else:
|
|
2101
|
+
prime = 0
|
|
2102
|
+
mu = chain[2*(i-1)]
|
|
2103
|
+
mu += [0]*(len(la) - len(mu))
|
|
2104
|
+
|
|
2105
|
+
for r in range(len(la)):
|
|
2106
|
+
for c in range(mu[r], la[r]):
|
|
2107
|
+
T[r][c] = i - prime
|
|
2108
|
+
|
|
2109
|
+
skew = _make_partition([row.count(None) for row in T])
|
|
2110
|
+
T = [[e for e in row if e is not None] for row in T]
|
|
2111
|
+
return ShiftedPrimedTableau(T, skew=skew)
|
|
2112
|
+
|
|
2113
|
+
def forward_rule(self, y, e, t, f, x, content):
|
|
2114
|
+
r"""
|
|
2115
|
+
Return the output path given two incident edges and the content.
|
|
2116
|
+
|
|
2117
|
+
See [Fom1995]_ Lemma 4.5.1, page 38.
|
|
2118
|
+
|
|
2119
|
+
INPUT:
|
|
2120
|
+
|
|
2121
|
+
- ``y``, ``e``, ``t``, ``f``, ``x`` -- a path of three partitions and
|
|
2122
|
+
two colors from a cell in a growth diagram, labelled as::
|
|
2123
|
+
|
|
2124
|
+
t f x
|
|
2125
|
+
e
|
|
2126
|
+
y
|
|
2127
|
+
|
|
2128
|
+
- ``content`` -- `0` or `1`; the content of the cell
|
|
2129
|
+
|
|
2130
|
+
OUTPUT:
|
|
2131
|
+
|
|
2132
|
+
The two colors and the fourth partition ``g``, ``z``, ``h``
|
|
2133
|
+
according to Sagan-Worley insertion.
|
|
2134
|
+
|
|
2135
|
+
EXAMPLES::
|
|
2136
|
+
|
|
2137
|
+
sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
|
|
2138
|
+
sage: Shifted.forward_rule([], 0, [], 0, [], 1)
|
|
2139
|
+
(1, [1], 0)
|
|
2140
|
+
|
|
2141
|
+
sage: Shifted.forward_rule([1], 0, [1], 0, [1], 1)
|
|
2142
|
+
(2, [2], 0)
|
|
2143
|
+
|
|
2144
|
+
if ``x != y``::
|
|
2145
|
+
|
|
2146
|
+
sage: Shifted.forward_rule([3], 0, [2], 1, [2,1], 0)
|
|
2147
|
+
(1, [3, 1], 0)
|
|
2148
|
+
|
|
2149
|
+
sage: Shifted.forward_rule([2,1], 0, [2], 2, [3], 0)
|
|
2150
|
+
(2, [3, 1], 0)
|
|
2151
|
+
|
|
2152
|
+
if ``x == y != t``::
|
|
2153
|
+
|
|
2154
|
+
sage: Shifted.forward_rule([3], 0, [2], 2, [3], 0)
|
|
2155
|
+
(1, [3, 1], 0)
|
|
2156
|
+
|
|
2157
|
+
sage: Shifted.forward_rule([3,1], 0, [2,1], 2, [3,1], 0)
|
|
2158
|
+
(2, [3, 2], 0)
|
|
2159
|
+
|
|
2160
|
+
sage: Shifted.forward_rule([2,1], 0, [2], 1, [2,1], 0)
|
|
2161
|
+
(3, [3, 1], 0)
|
|
2162
|
+
|
|
2163
|
+
sage: Shifted.forward_rule([3], 0, [2], 3, [3], 0)
|
|
2164
|
+
(3, [4], 0)
|
|
2165
|
+
"""
|
|
2166
|
+
if e != 0:
|
|
2167
|
+
raise ValueError("the P-graph should not be colored")
|
|
2168
|
+
h = 0
|
|
2169
|
+
if x == t == y:
|
|
2170
|
+
if f != 0:
|
|
2171
|
+
raise ValueError("degenerate edge f should have color 0")
|
|
2172
|
+
if content == 0:
|
|
2173
|
+
g, z = 0, x
|
|
2174
|
+
elif content == 1:
|
|
2175
|
+
if not x:
|
|
2176
|
+
g, z = 1, _Partitions(x).add_cell(0) # black
|
|
2177
|
+
else:
|
|
2178
|
+
g, z = 2, _make_partition(x).add_cell(0) # blue
|
|
2179
|
+
else:
|
|
2180
|
+
raise NotImplementedError
|
|
2181
|
+
elif content != 0:
|
|
2182
|
+
raise ValueError("for y=%s, t=%s, x=%s, the content should be 0 but is %s"
|
|
2183
|
+
% (y, t, x, content))
|
|
2184
|
+
elif x != t == y:
|
|
2185
|
+
g, z = f, x
|
|
2186
|
+
elif x == t != y:
|
|
2187
|
+
if f != 0:
|
|
2188
|
+
raise ValueError("degenerate edge f should have color 0")
|
|
2189
|
+
g, z = f, y
|
|
2190
|
+
else:
|
|
2191
|
+
if x != y:
|
|
2192
|
+
row = SkewPartition([x, t]).cells()[0][0]
|
|
2193
|
+
g, z = f, _make_partition(y).add_cell(row)
|
|
2194
|
+
elif x == y != t and f == 2: # blue
|
|
2195
|
+
row = 1+SkewPartition([x, t]).cells()[0][0]
|
|
2196
|
+
if row == len(y):
|
|
2197
|
+
g, z = 1, _make_partition(y).add_cell(row) # black
|
|
2198
|
+
else:
|
|
2199
|
+
g, z = 2, _make_partition(y).add_cell(row) # blue
|
|
2200
|
+
elif x == y != t and f in [1, 3]: # black or red
|
|
2201
|
+
c = SkewPartition([x, t]).cells()[0]
|
|
2202
|
+
col = c[0] + c[1] + 1
|
|
2203
|
+
for i in range(len(y)):
|
|
2204
|
+
if i + y[i] == col:
|
|
2205
|
+
z = y[:i] + [y[i] + 1] + y[i + 1:]
|
|
2206
|
+
break
|
|
2207
|
+
g = 3
|
|
2208
|
+
else:
|
|
2209
|
+
raise NotImplementedError
|
|
2210
|
+
return g, _make_partition(z), h
|
|
2211
|
+
|
|
2212
|
+
def backward_rule(self, y, g, z, h, x):
|
|
2213
|
+
r"""
|
|
2214
|
+
Return the input path and the content given two incident edges.
|
|
2215
|
+
|
|
2216
|
+
See [Fom1995]_ Lemma 4.5.1, page 38.
|
|
2217
|
+
|
|
2218
|
+
INPUT:
|
|
2219
|
+
|
|
2220
|
+
- ``y``, ``g``, ``z``, ``h``, ``x`` -- a path of three partitions and
|
|
2221
|
+
two colors from a cell in a growth diagram, labelled as::
|
|
2222
|
+
|
|
2223
|
+
x
|
|
2224
|
+
h
|
|
2225
|
+
y g z
|
|
2226
|
+
|
|
2227
|
+
OUTPUT:
|
|
2228
|
+
|
|
2229
|
+
A tuple ``(e, t, f, content)`` consisting of the shape ``t``
|
|
2230
|
+
of the fourth word, the colours of the incident edges and the
|
|
2231
|
+
content of the cell according to Sagan - Worley insertion.
|
|
2232
|
+
|
|
2233
|
+
EXAMPLES::
|
|
2234
|
+
|
|
2235
|
+
sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
|
|
2236
|
+
sage: Shifted.backward_rule([], 1, [1], 0, [])
|
|
2237
|
+
(0, [], 0, 1)
|
|
2238
|
+
|
|
2239
|
+
sage: Shifted.backward_rule([1], 2, [2], 0, [1])
|
|
2240
|
+
(0, [1], 0, 1)
|
|
2241
|
+
|
|
2242
|
+
if ``x != y``::
|
|
2243
|
+
|
|
2244
|
+
sage: Shifted.backward_rule([3], 1, [3, 1], 0, [2,1])
|
|
2245
|
+
(0, [2], 1, 0)
|
|
2246
|
+
|
|
2247
|
+
sage: Shifted.backward_rule([2,1], 2, [3, 1], 0, [3])
|
|
2248
|
+
(0, [2], 2, 0)
|
|
2249
|
+
|
|
2250
|
+
if ``x == y != t``::
|
|
2251
|
+
|
|
2252
|
+
sage: Shifted.backward_rule([3], 1, [3, 1], 0, [3])
|
|
2253
|
+
(0, [2], 2, 0)
|
|
2254
|
+
|
|
2255
|
+
sage: Shifted.backward_rule([3,1], 2, [3, 2], 0, [3,1])
|
|
2256
|
+
(0, [2, 1], 2, 0)
|
|
2257
|
+
|
|
2258
|
+
sage: Shifted.backward_rule([2,1], 3, [3, 1], 0, [2,1])
|
|
2259
|
+
(0, [2], 1, 0)
|
|
2260
|
+
|
|
2261
|
+
sage: Shifted.backward_rule([3], 3, [4], 0, [3])
|
|
2262
|
+
(0, [2], 3, 0)
|
|
2263
|
+
"""
|
|
2264
|
+
if h != 0:
|
|
2265
|
+
raise ValueError("the P-graph should not be colored")
|
|
2266
|
+
|
|
2267
|
+
if x == y == z:
|
|
2268
|
+
if g != 0:
|
|
2269
|
+
raise ValueError("degenerate edge g should have color 0")
|
|
2270
|
+
return (0, x, 0, 0)
|
|
2271
|
+
elif x == z != y:
|
|
2272
|
+
return (0, y, g, 0)
|
|
2273
|
+
elif x != z == y:
|
|
2274
|
+
if g != 0:
|
|
2275
|
+
raise ValueError("degenerate edge g should have color 0")
|
|
2276
|
+
return (0, x, 0, 0)
|
|
2277
|
+
else:
|
|
2278
|
+
if x != y:
|
|
2279
|
+
row = SkewPartition([z, x]).cells()[0][0]
|
|
2280
|
+
return (0, _make_partition(y).remove_cell(row), g, 0)
|
|
2281
|
+
else:
|
|
2282
|
+
row, col = SkewPartition([z, x]).cells()[0]
|
|
2283
|
+
if row > 0 and g in [1, 2]: # black or blue
|
|
2284
|
+
return (0, _make_partition(y).remove_cell(row-1), 2, 0)
|
|
2285
|
+
elif row == 0 and g in [1, 2]: # black or blue
|
|
2286
|
+
return (0, y, 0, 1)
|
|
2287
|
+
else:
|
|
2288
|
+
# find last cell in column col-1
|
|
2289
|
+
for i in range(len(y)-1,-1,-1):
|
|
2290
|
+
if i + y[i] == col + row:
|
|
2291
|
+
if y[i] == 1:
|
|
2292
|
+
t = y[:i]
|
|
2293
|
+
return (0, t, 1, 0)
|
|
2294
|
+
else:
|
|
2295
|
+
t = y[:i] + [y[i]-1] + y[i+1:]
|
|
2296
|
+
return (0, t, 3, 0)
|
|
2297
|
+
raise ValueError("this should not happen")
|
|
2298
|
+
|
|
2299
|
+
|
|
2300
|
+
class RuleLLMS(Rule):
|
|
2301
|
+
r"""
|
|
2302
|
+
A rule modelling the Schensted correspondence for affine
|
|
2303
|
+
permutations.
|
|
2304
|
+
|
|
2305
|
+
EXAMPLES::
|
|
2306
|
+
|
|
2307
|
+
sage: LLMS3 = GrowthDiagram.rules.LLMS(3)
|
|
2308
|
+
sage: GrowthDiagram(LLMS3, [3,1,2])
|
|
2309
|
+
0 1 0
|
|
2310
|
+
0 0 1
|
|
2311
|
+
1 0 0
|
|
2312
|
+
|
|
2313
|
+
The vertices of the dual graded graph are
|
|
2314
|
+
:class:`~sage.combinat.core.Cores`::
|
|
2315
|
+
|
|
2316
|
+
sage: LLMS3.vertices(4)
|
|
2317
|
+
3-Cores of length 4
|
|
2318
|
+
|
|
2319
|
+
Let us check example of Figure 1 in [LS2007]_. Note that,
|
|
2320
|
+
instead of passing the rule to :class:`GrowthDiagram`, we can
|
|
2321
|
+
also call the rule to create growth diagrams::
|
|
2322
|
+
|
|
2323
|
+
sage: G = LLMS3([4,1,2,6,3,5]); G
|
|
2324
|
+
0 1 0 0 0 0
|
|
2325
|
+
0 0 1 0 0 0
|
|
2326
|
+
0 0 0 0 1 0
|
|
2327
|
+
1 0 0 0 0 0
|
|
2328
|
+
0 0 0 0 0 1
|
|
2329
|
+
0 0 0 1 0 0
|
|
2330
|
+
|
|
2331
|
+
The :meth:`P_symbol` is a
|
|
2332
|
+
:class:`~sage.combinat.k_tableau.StrongTableau`::
|
|
2333
|
+
|
|
2334
|
+
sage: G.P_symbol().pp()
|
|
2335
|
+
-1 -2 -3 -5
|
|
2336
|
+
3 5
|
|
2337
|
+
-4 -6
|
|
2338
|
+
5
|
|
2339
|
+
6
|
|
2340
|
+
|
|
2341
|
+
The :meth:`Q_symbol` is a
|
|
2342
|
+
:class:`~sage.combinat.k_tableau.WeakTableau`::
|
|
2343
|
+
|
|
2344
|
+
sage: G.Q_symbol().pp()
|
|
2345
|
+
1 3 4 5
|
|
2346
|
+
2 5
|
|
2347
|
+
3 6
|
|
2348
|
+
5
|
|
2349
|
+
6
|
|
2350
|
+
|
|
2351
|
+
Let us also check Example 6.2 in [LLMSSZ2013]_::
|
|
2352
|
+
|
|
2353
|
+
sage: G = LLMS3([4,1,3,2])
|
|
2354
|
+
sage: G.P_symbol().pp()
|
|
2355
|
+
-1 -2 3
|
|
2356
|
+
-3
|
|
2357
|
+
-4
|
|
2358
|
+
|
|
2359
|
+
sage: G.Q_symbol().pp()
|
|
2360
|
+
1 3 4
|
|
2361
|
+
2
|
|
2362
|
+
3
|
|
2363
|
+
|
|
2364
|
+
TESTS::
|
|
2365
|
+
|
|
2366
|
+
sage: LLMS3 = GrowthDiagram.rules.LLMS(3)
|
|
2367
|
+
sage: LLMS3.zero
|
|
2368
|
+
[]
|
|
2369
|
+
"""
|
|
2370
|
+
zero_edge = None # to prevent confusion with the edge labelled with content 0
|
|
2371
|
+
has_multiple_edges = True
|
|
2372
|
+
|
|
2373
|
+
def __init__(self, k):
|
|
2374
|
+
r"""
|
|
2375
|
+
Initialize ``self``.
|
|
2376
|
+
|
|
2377
|
+
TESTS::
|
|
2378
|
+
|
|
2379
|
+
sage: LLMS3 = GrowthDiagram.rules.LLMS(3)
|
|
2380
|
+
sage: TestSuite(LLMS3).run()
|
|
2381
|
+
"""
|
|
2382
|
+
self.k = k
|
|
2383
|
+
self.zero = Core([], k)
|
|
2384
|
+
|
|
2385
|
+
def normalize_vertex(self, v):
|
|
2386
|
+
r"""
|
|
2387
|
+
Convert ``v`` to a `k`-core.
|
|
2388
|
+
|
|
2389
|
+
EXAMPLES::
|
|
2390
|
+
|
|
2391
|
+
sage: LLMS3 = GrowthDiagram.rules.LLMS(3)
|
|
2392
|
+
sage: LLMS3.normalize_vertex([3,1]).parent()
|
|
2393
|
+
3-Cores of length 3
|
|
2394
|
+
"""
|
|
2395
|
+
return Core(v, self.k)
|
|
2396
|
+
|
|
2397
|
+
def rank(self, v):
|
|
2398
|
+
r"""
|
|
2399
|
+
Return the rank of ``v``: the length of the core.
|
|
2400
|
+
|
|
2401
|
+
EXAMPLES::
|
|
2402
|
+
|
|
2403
|
+
sage: LLMS3 = GrowthDiagram.rules.LLMS(3)
|
|
2404
|
+
sage: LLMS3.rank(LLMS3.vertices(3)[0])
|
|
2405
|
+
3
|
|
2406
|
+
"""
|
|
2407
|
+
return v.length()
|
|
2408
|
+
|
|
2409
|
+
def vertices(self, n):
|
|
2410
|
+
r"""
|
|
2411
|
+
Return the vertices of the dual graded graph on level ``n``.
|
|
2412
|
+
|
|
2413
|
+
EXAMPLES::
|
|
2414
|
+
|
|
2415
|
+
sage: LLMS3 = GrowthDiagram.rules.LLMS(3)
|
|
2416
|
+
sage: LLMS3.vertices(2)
|
|
2417
|
+
3-Cores of length 2
|
|
2418
|
+
"""
|
|
2419
|
+
return Cores(self.k, length=n)
|
|
2420
|
+
|
|
2421
|
+
def is_Q_edge(self, v, w):
|
|
2422
|
+
r"""
|
|
2423
|
+
Return whether ``(v, w)`` is a `Q`-edge of ``self``.
|
|
2424
|
+
|
|
2425
|
+
``(v, w)`` is an edge if ``w`` is a weak cover of ``v``, see
|
|
2426
|
+
:meth:`~sage.combinat.core.Core.weak_covers()`.
|
|
2427
|
+
|
|
2428
|
+
EXAMPLES::
|
|
2429
|
+
|
|
2430
|
+
sage: # needs sage.modules
|
|
2431
|
+
sage: LLMS4 = GrowthDiagram.rules.LLMS(4)
|
|
2432
|
+
sage: v = LLMS4.vertices(3)[1]; v
|
|
2433
|
+
[2, 1]
|
|
2434
|
+
sage: [w for w in LLMS4.vertices(4) if len(LLMS4.is_Q_edge(v, w)) > 0]
|
|
2435
|
+
[[2, 2], [3, 1, 1]]
|
|
2436
|
+
sage: all(LLMS4.is_Q_edge(v, w) == [] for w in LLMS4.vertices(5))
|
|
2437
|
+
True
|
|
2438
|
+
"""
|
|
2439
|
+
return [None] if w in v.weak_covers() else []
|
|
2440
|
+
|
|
2441
|
+
def is_P_edge(self, v, w):
|
|
2442
|
+
r"""
|
|
2443
|
+
Return whether ``(v, w)`` is a `P`-edge of ``self``.
|
|
2444
|
+
|
|
2445
|
+
For two k-cores v and w containing v, there are as many edges as
|
|
2446
|
+
there are components in the skew partition w/v. These
|
|
2447
|
+
components are ribbons, and therefore contain a unique cell
|
|
2448
|
+
with maximal content. We index the edge with this content.
|
|
2449
|
+
|
|
2450
|
+
EXAMPLES::
|
|
2451
|
+
|
|
2452
|
+
sage: LLMS4 = GrowthDiagram.rules.LLMS(4)
|
|
2453
|
+
sage: v = LLMS4.vertices(2)[0]; v
|
|
2454
|
+
[2]
|
|
2455
|
+
sage: [(w, LLMS4.is_P_edge(v, w)) for w in LLMS4.vertices(3)]
|
|
2456
|
+
[([3], [2]), ([2, 1], [-1]), ([1, 1, 1], [])]
|
|
2457
|
+
sage: all(LLMS4.is_P_edge(v, w) == [] for w in LLMS4.vertices(4))
|
|
2458
|
+
True
|
|
2459
|
+
"""
|
|
2460
|
+
if w in v.strong_covers():
|
|
2461
|
+
T = SkewPartition([w.to_partition(), v.to_partition()])
|
|
2462
|
+
return [max([j-i for i,j in c]) for c in T.cell_poset().connected_components()]
|
|
2463
|
+
else:
|
|
2464
|
+
return []
|
|
2465
|
+
|
|
2466
|
+
def P_symbol(self, P_chain):
|
|
2467
|
+
r"""
|
|
2468
|
+
Return the labels along the vertical boundary of a
|
|
2469
|
+
rectangular growth diagram as a skew
|
|
2470
|
+
:class:`~sage.combinat.k_tableau.StrongTableau`.
|
|
2471
|
+
|
|
2472
|
+
EXAMPLES::
|
|
2473
|
+
|
|
2474
|
+
sage: LLMS4 = GrowthDiagram.rules.LLMS(4)
|
|
2475
|
+
sage: G = LLMS4([3,4,1,2])
|
|
2476
|
+
sage: G.P_symbol().pp()
|
|
2477
|
+
-1 -2
|
|
2478
|
+
-3 -4
|
|
2479
|
+
"""
|
|
2480
|
+
C = P_chain
|
|
2481
|
+
T = SkewTableau(chain=C[::2])
|
|
2482
|
+
S = T.to_list()
|
|
2483
|
+
for entry, content in enumerate(C[1::2], 1):
|
|
2484
|
+
for i,j in T.cells_containing(entry):
|
|
2485
|
+
if j-i == content:
|
|
2486
|
+
S[i][j] = -S[i][j]
|
|
2487
|
+
break
|
|
2488
|
+
return StrongTableau(S, self.k-1)
|
|
2489
|
+
|
|
2490
|
+
def Q_symbol(self, Q_chain):
|
|
2491
|
+
r"""
|
|
2492
|
+
Return the labels along the horizontal boundary of a
|
|
2493
|
+
rectangular growth diagram as a skew
|
|
2494
|
+
:class:`~sage.combinat.k_tableau.WeakTableau`.
|
|
2495
|
+
|
|
2496
|
+
EXAMPLES::
|
|
2497
|
+
|
|
2498
|
+
sage: LLMS4 = GrowthDiagram.rules.LLMS(4)
|
|
2499
|
+
sage: G = LLMS4([3,4,1,2])
|
|
2500
|
+
sage: G.Q_symbol().pp()
|
|
2501
|
+
1 2
|
|
2502
|
+
3 4
|
|
2503
|
+
"""
|
|
2504
|
+
return WeakTableau(SkewTableau(chain=Q_chain[::2]), self.k-1)
|
|
2505
|
+
|
|
2506
|
+
def forward_rule(self, y, e, t, f, x, content):
|
|
2507
|
+
r"""
|
|
2508
|
+
Return the output path given two incident edges and the content.
|
|
2509
|
+
|
|
2510
|
+
See [LS2007]_ Section 3.4 and [LLMSSZ2013]_ Section 6.3.
|
|
2511
|
+
|
|
2512
|
+
INPUT:
|
|
2513
|
+
|
|
2514
|
+
- ``y``, ``e``, ``t``, ``f``, ``x`` -- a path of three partitions and
|
|
2515
|
+
two colors from a cell in a growth diagram, labelled as::
|
|
2516
|
+
|
|
2517
|
+
t f x
|
|
2518
|
+
e
|
|
2519
|
+
y
|
|
2520
|
+
|
|
2521
|
+
- ``content`` -- `0` or `1`; the content of the cell
|
|
2522
|
+
|
|
2523
|
+
OUTPUT:
|
|
2524
|
+
|
|
2525
|
+
The two colors and the fourth partition g, z, h according to
|
|
2526
|
+
LLMS insertion.
|
|
2527
|
+
|
|
2528
|
+
EXAMPLES::
|
|
2529
|
+
|
|
2530
|
+
sage: LLMS3 = GrowthDiagram.rules.LLMS(3)
|
|
2531
|
+
sage: LLMS4 = GrowthDiagram.rules.LLMS(4)
|
|
2532
|
+
|
|
2533
|
+
sage: Z = LLMS3.zero
|
|
2534
|
+
sage: LLMS3.forward_rule(Z, None, Z, None, Z, 0)
|
|
2535
|
+
(None, [], None)
|
|
2536
|
+
|
|
2537
|
+
sage: LLMS3.forward_rule(Z, None, Z, None, Z, 1)
|
|
2538
|
+
(None, [1], 0)
|
|
2539
|
+
|
|
2540
|
+
sage: Y = Core([3,1,1], 3)
|
|
2541
|
+
sage: LLMS3.forward_rule(Y, None, Y, None, Y, 1)
|
|
2542
|
+
(None, [4, 2, 1, 1], 3)
|
|
2543
|
+
|
|
2544
|
+
if ``x != y``::
|
|
2545
|
+
|
|
2546
|
+
sage: Y = Core([1,1], 3); T = Core([1], 3); X = Core([2], 3)
|
|
2547
|
+
sage: LLMS3.forward_rule(Y, -1, T, None, X, 0)
|
|
2548
|
+
(None, [2, 1, 1], -1)
|
|
2549
|
+
|
|
2550
|
+
sage: Y = Core([2], 4); T = Core([1], 4); X = Core([1,1], 4)
|
|
2551
|
+
sage: LLMS4.forward_rule(Y, 1, T, None, X, 0)
|
|
2552
|
+
(None, [2, 1], 1)
|
|
2553
|
+
|
|
2554
|
+
sage: Y = Core([2,1,1], 3); T = Core([2], 3); X = Core([3,1], 3)
|
|
2555
|
+
sage: LLMS3.forward_rule(Y, -1, T, None, X, 0)
|
|
2556
|
+
(None, [3, 1, 1], -2)
|
|
2557
|
+
|
|
2558
|
+
if ``x == y != t``::
|
|
2559
|
+
|
|
2560
|
+
sage: Y = Core([1], 3); T = Core([], 3); X = Core([1], 3)
|
|
2561
|
+
sage: LLMS3.forward_rule(Y, 0, T, None, X, 0)
|
|
2562
|
+
(None, [1, 1], -1)
|
|
2563
|
+
|
|
2564
|
+
sage: Y = Core([1], 4); T = Core([], 4); X = Core([1], 4)
|
|
2565
|
+
sage: LLMS4.forward_rule(Y, 0, T, None, X, 0)
|
|
2566
|
+
(None, [1, 1], -1)
|
|
2567
|
+
|
|
2568
|
+
sage: Y = Core([2,1], 4); T = Core([1,1], 4); X = Core([2,1], 4)
|
|
2569
|
+
sage: LLMS4.forward_rule(Y, 1, T, None, X, 0)
|
|
2570
|
+
(None, [2, 2], 0)
|
|
2571
|
+
"""
|
|
2572
|
+
if f is not None:
|
|
2573
|
+
raise ValueError("the Q-graph should not be colored")
|
|
2574
|
+
g = None
|
|
2575
|
+
if x == t == y:
|
|
2576
|
+
if e is not None:
|
|
2577
|
+
raise ValueError("degenerate edge e should have color None")
|
|
2578
|
+
if content == 0:
|
|
2579
|
+
z, h = x, None
|
|
2580
|
+
elif content == 1:
|
|
2581
|
+
if t.size() == 0:
|
|
2582
|
+
z = t.affine_symmetric_group_simple_action(0)
|
|
2583
|
+
else:
|
|
2584
|
+
z = t.affine_symmetric_group_simple_action(t[0] % self.k)
|
|
2585
|
+
h = z[0] - 1
|
|
2586
|
+
else:
|
|
2587
|
+
assert False, "BUG in RuleLLMS"
|
|
2588
|
+
elif content != 0:
|
|
2589
|
+
raise ValueError("for y=%s, t=%s, x=%s, the content should be 0 but is %s"
|
|
2590
|
+
% (y, t, x, content))
|
|
2591
|
+
elif x != t == y:
|
|
2592
|
+
if e is not None:
|
|
2593
|
+
raise ValueError("degenerate edge e should have color None")
|
|
2594
|
+
z, h = x, e
|
|
2595
|
+
elif x == t != y:
|
|
2596
|
+
z, h = y, e
|
|
2597
|
+
else: # x != t and y != t
|
|
2598
|
+
qx = SkewPartition([x.to_partition(), t.to_partition()])
|
|
2599
|
+
qy = SkewPartition([y.to_partition(), t.to_partition()])
|
|
2600
|
+
if not all(c in qx.cells() for c in qy.cells()):
|
|
2601
|
+
res = [(j-i) % self.k for i, j in qx.cells()]
|
|
2602
|
+
assert len(set(res)) == 1
|
|
2603
|
+
r = res[0]
|
|
2604
|
+
z = y.affine_symmetric_group_simple_action(r)
|
|
2605
|
+
if e % self.k == r:
|
|
2606
|
+
h = e-1
|
|
2607
|
+
else:
|
|
2608
|
+
h = e
|
|
2609
|
+
elif x == y != t:
|
|
2610
|
+
# the addable cell with largest content at most e
|
|
2611
|
+
cprime = sorted([c for c in y.to_partition().addable_cells()
|
|
2612
|
+
if c[1]-c[0] <= e],
|
|
2613
|
+
key=lambda c: -(c[1]-c[0]))[0]
|
|
2614
|
+
h = cprime[1] - cprime[0]
|
|
2615
|
+
z = y.affine_symmetric_group_simple_action(h % self.k)
|
|
2616
|
+
|
|
2617
|
+
return g, z, h
|
|
2618
|
+
|
|
2619
|
+
|
|
2620
|
+
class RuleBinaryWord(Rule):
|
|
2621
|
+
r"""
|
|
2622
|
+
A rule modelling a Schensted-like correspondence for binary words.
|
|
2623
|
+
|
|
2624
|
+
EXAMPLES::
|
|
2625
|
+
|
|
2626
|
+
sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
|
|
2627
|
+
sage: GrowthDiagram(BinaryWord, [3,1,2])
|
|
2628
|
+
0 1 0
|
|
2629
|
+
0 0 1
|
|
2630
|
+
1 0 0
|
|
2631
|
+
|
|
2632
|
+
The vertices of the dual graded graph are binary words::
|
|
2633
|
+
|
|
2634
|
+
sage: BinaryWord.vertices(3)
|
|
2635
|
+
[word: 100, word: 101, word: 110, word: 111]
|
|
2636
|
+
|
|
2637
|
+
Note that, instead of passing the rule to :class:`GrowthDiagram`,
|
|
2638
|
+
we can also use call the rule to create growth diagrams. For
|
|
2639
|
+
example::
|
|
2640
|
+
|
|
2641
|
+
sage: BinaryWord([2,4,1,3]).P_chain()
|
|
2642
|
+
[word: , word: 1, word: 10, word: 101, word: 1101]
|
|
2643
|
+
sage: BinaryWord([2,4,1,3]).Q_chain()
|
|
2644
|
+
[word: , word: 1, word: 11, word: 110, word: 1101]
|
|
2645
|
+
|
|
2646
|
+
The Kleitman Greene invariant is the descent word, encoded by the
|
|
2647
|
+
positions of the zeros::
|
|
2648
|
+
|
|
2649
|
+
sage: pi = Permutation([4,1,8,3,6,5,2,7,9])
|
|
2650
|
+
sage: G = BinaryWord(pi); G
|
|
2651
|
+
0 1 0 0 0 0 0 0 0
|
|
2652
|
+
0 0 0 0 0 0 1 0 0
|
|
2653
|
+
0 0 0 1 0 0 0 0 0
|
|
2654
|
+
1 0 0 0 0 0 0 0 0
|
|
2655
|
+
0 0 0 0 0 1 0 0 0
|
|
2656
|
+
0 0 0 0 1 0 0 0 0
|
|
2657
|
+
0 0 0 0 0 0 0 1 0
|
|
2658
|
+
0 0 1 0 0 0 0 0 0
|
|
2659
|
+
0 0 0 0 0 0 0 0 1
|
|
2660
|
+
sage: pi.descents()
|
|
2661
|
+
[1, 3, 5, 6]
|
|
2662
|
+
|
|
2663
|
+
TESTS::
|
|
2664
|
+
|
|
2665
|
+
sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
|
|
2666
|
+
sage: BinaryWord.zero
|
|
2667
|
+
word:
|
|
2668
|
+
sage: G = BinaryWord(labels=[[1,1],[1,1,0],[0,1]])
|
|
2669
|
+
Traceback (most recent call last):
|
|
2670
|
+
...
|
|
2671
|
+
ValueError: 01 has smaller rank than 110 but is not covered by it in P
|
|
2672
|
+
|
|
2673
|
+
sage: G = BinaryWord(labels=[[1,1],[1,0,1],[0,1]])
|
|
2674
|
+
Traceback (most recent call last):
|
|
2675
|
+
...
|
|
2676
|
+
ValueError: 11 has smaller rank than 101 but is not covered by it in Q
|
|
2677
|
+
|
|
2678
|
+
Check duality::
|
|
2679
|
+
|
|
2680
|
+
sage: BinaryWord._check_duality(4)
|
|
2681
|
+
|
|
2682
|
+
Check that the rules are bijective::
|
|
2683
|
+
|
|
2684
|
+
sage: all(BinaryWord(labels=BinaryWord(pi).out_labels()).to_word()
|
|
2685
|
+
....: == pi for pi in Permutations(4))
|
|
2686
|
+
True
|
|
2687
|
+
sage: pi = Permutations(10).random_element()
|
|
2688
|
+
sage: G = BinaryWord(pi)
|
|
2689
|
+
sage: list(BinaryWord(labels=G.out_labels())) == list(G)
|
|
2690
|
+
True
|
|
2691
|
+
|
|
2692
|
+
Test that the Kleitman Greene invariant is indeed the descent word::
|
|
2693
|
+
|
|
2694
|
+
sage: r = 4
|
|
2695
|
+
sage: all(Word([0 if i in w.descents() else 1 for i in range(r)])
|
|
2696
|
+
....: == BinaryWord(w).out_labels()[r]
|
|
2697
|
+
....: for w in Permutations(r))
|
|
2698
|
+
True
|
|
2699
|
+
"""
|
|
2700
|
+
zero = Word([], alphabet=[0,1])
|
|
2701
|
+
|
|
2702
|
+
def normalize_vertex(self, v):
|
|
2703
|
+
r"""
|
|
2704
|
+
Return ``v`` as a binary word.
|
|
2705
|
+
|
|
2706
|
+
EXAMPLES::
|
|
2707
|
+
|
|
2708
|
+
sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
|
|
2709
|
+
sage: BinaryWord.normalize_vertex([0,1]).parent()
|
|
2710
|
+
Finite words over {0, 1}
|
|
2711
|
+
"""
|
|
2712
|
+
return Word(v, alphabet=[0,1])
|
|
2713
|
+
|
|
2714
|
+
def vertices(self, n):
|
|
2715
|
+
r"""
|
|
2716
|
+
Return the vertices of the dual graded graph on level ``n``.
|
|
2717
|
+
|
|
2718
|
+
EXAMPLES::
|
|
2719
|
+
|
|
2720
|
+
sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
|
|
2721
|
+
sage: BinaryWord.vertices(3)
|
|
2722
|
+
[word: 100, word: 101, word: 110, word: 111]
|
|
2723
|
+
"""
|
|
2724
|
+
if n == 0:
|
|
2725
|
+
return [self.zero]
|
|
2726
|
+
else:
|
|
2727
|
+
w1 = Word([1], [0,1])
|
|
2728
|
+
return [w1 + w for w in Words([0,1], n-1)]
|
|
2729
|
+
|
|
2730
|
+
def rank(self, v):
|
|
2731
|
+
r"""
|
|
2732
|
+
Return the rank of ``v``: number of letters of the word.
|
|
2733
|
+
|
|
2734
|
+
EXAMPLES::
|
|
2735
|
+
|
|
2736
|
+
sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
|
|
2737
|
+
sage: BinaryWord.rank(BinaryWord.vertices(3)[0])
|
|
2738
|
+
3
|
|
2739
|
+
"""
|
|
2740
|
+
return len(v)
|
|
2741
|
+
|
|
2742
|
+
def is_Q_edge(self, v, w):
|
|
2743
|
+
r"""
|
|
2744
|
+
Return whether ``(v, w)`` is a `Q`-edge of ``self``.
|
|
2745
|
+
|
|
2746
|
+
``(w, v)`` is an edge if ``w`` is obtained from ``v`` by
|
|
2747
|
+
appending a letter.
|
|
2748
|
+
|
|
2749
|
+
EXAMPLES::
|
|
2750
|
+
|
|
2751
|
+
sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
|
|
2752
|
+
sage: v = BinaryWord.vertices(2)[0]; v
|
|
2753
|
+
word: 10
|
|
2754
|
+
sage: [w for w in BinaryWord.vertices(3) if BinaryWord.is_Q_edge(v, w)]
|
|
2755
|
+
[word: 100, word: 101]
|
|
2756
|
+
sage: [w for w in BinaryWord.vertices(4) if BinaryWord.is_Q_edge(v, w)]
|
|
2757
|
+
[]
|
|
2758
|
+
"""
|
|
2759
|
+
return w[:-1] == v
|
|
2760
|
+
|
|
2761
|
+
def is_P_edge(self, v, w):
|
|
2762
|
+
r"""
|
|
2763
|
+
Return whether ``(v, w)`` is a `P`-edge of ``self``.
|
|
2764
|
+
|
|
2765
|
+
``(v, w)`` is an edge if ``v`` is obtained from ``w`` by
|
|
2766
|
+
deleting a letter.
|
|
2767
|
+
|
|
2768
|
+
EXAMPLES::
|
|
2769
|
+
|
|
2770
|
+
sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
|
|
2771
|
+
sage: v = BinaryWord.vertices(2)[1]; v
|
|
2772
|
+
word: 11
|
|
2773
|
+
sage: [w for w in BinaryWord.vertices(3) if BinaryWord.is_P_edge(v, w)]
|
|
2774
|
+
[word: 101, word: 110, word: 111]
|
|
2775
|
+
sage: [w for w in BinaryWord.vertices(4) if BinaryWord.is_P_edge(v, w)]
|
|
2776
|
+
[]
|
|
2777
|
+
"""
|
|
2778
|
+
return len(w) == len(v) + 1 and v.is_subword_of(w)
|
|
2779
|
+
|
|
2780
|
+
def forward_rule(self, y, t, x, content):
|
|
2781
|
+
r"""
|
|
2782
|
+
Return the output shape given three shapes and the content.
|
|
2783
|
+
|
|
2784
|
+
See [Fom1995]_ Lemma 4.6.1, page 40.
|
|
2785
|
+
|
|
2786
|
+
INPUT:
|
|
2787
|
+
|
|
2788
|
+
- ``y``, ``t``, ``x`` -- three binary words from a cell in a growth
|
|
2789
|
+
diagram, labelled as::
|
|
2790
|
+
|
|
2791
|
+
t x
|
|
2792
|
+
y
|
|
2793
|
+
|
|
2794
|
+
- ``content`` -- `0` or `1`; the content of the cell
|
|
2795
|
+
|
|
2796
|
+
OUTPUT:
|
|
2797
|
+
|
|
2798
|
+
The fourth binary word ``z`` according to Viennot's
|
|
2799
|
+
bijection [Vie1983]_.
|
|
2800
|
+
|
|
2801
|
+
EXAMPLES::
|
|
2802
|
+
|
|
2803
|
+
sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
|
|
2804
|
+
|
|
2805
|
+
sage: BinaryWord.forward_rule([], [], [], 1)
|
|
2806
|
+
word: 1
|
|
2807
|
+
|
|
2808
|
+
sage: BinaryWord.forward_rule([1], [1], [1], 1)
|
|
2809
|
+
word: 11
|
|
2810
|
+
|
|
2811
|
+
if ``x != y`` append last letter of ``x`` to ``y``::
|
|
2812
|
+
|
|
2813
|
+
sage: BinaryWord.forward_rule([1,0], [1], [1,1], 0)
|
|
2814
|
+
word: 101
|
|
2815
|
+
|
|
2816
|
+
if ``x == y != t`` append ``0`` to ``y``::
|
|
2817
|
+
|
|
2818
|
+
sage: BinaryWord.forward_rule([1,1], [1], [1,1], 0)
|
|
2819
|
+
word: 110
|
|
2820
|
+
"""
|
|
2821
|
+
if x == t == y:
|
|
2822
|
+
if content == 0:
|
|
2823
|
+
z = x
|
|
2824
|
+
elif content == 1:
|
|
2825
|
+
z = Word(list(y) + [1], alphabet=[0,1])
|
|
2826
|
+
else:
|
|
2827
|
+
raise NotImplementedError
|
|
2828
|
+
elif content != 0:
|
|
2829
|
+
raise ValueError("for y=%s, t=%s, x=%s, the content should be 0 but is %s"
|
|
2830
|
+
% (y, t, x, content))
|
|
2831
|
+
elif x != t == y:
|
|
2832
|
+
z = x
|
|
2833
|
+
elif x == t != y:
|
|
2834
|
+
z = y
|
|
2835
|
+
else:
|
|
2836
|
+
if x != y:
|
|
2837
|
+
z = Word(list(y) + [x[-1]], alphabet=[0,1])
|
|
2838
|
+
elif x == y != t:
|
|
2839
|
+
z = Word(list(y) + [0], alphabet=[0,1])
|
|
2840
|
+
else:
|
|
2841
|
+
raise NotImplementedError
|
|
2842
|
+
return z
|
|
2843
|
+
|
|
2844
|
+
def backward_rule(self, y, z, x):
|
|
2845
|
+
r"""
|
|
2846
|
+
Return the content and the input shape.
|
|
2847
|
+
|
|
2848
|
+
See [Fom1995]_ Lemma 4.6.1, page 40.
|
|
2849
|
+
|
|
2850
|
+
- ``y``, ``z``, ``x`` -- three binary words from a cell in a growth diagram,
|
|
2851
|
+
labelled as::
|
|
2852
|
+
|
|
2853
|
+
x
|
|
2854
|
+
y z
|
|
2855
|
+
|
|
2856
|
+
OUTPUT:
|
|
2857
|
+
|
|
2858
|
+
A pair ``(t, content)`` consisting of the shape of the fourth
|
|
2859
|
+
word and the content of the cell according to Viennot's
|
|
2860
|
+
bijection [Vie1983]_.
|
|
2861
|
+
|
|
2862
|
+
TESTS::
|
|
2863
|
+
|
|
2864
|
+
sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
|
|
2865
|
+
sage: w = [4,1,8,3,6,5,2,7,9]; G = GrowthDiagram(BinaryWord, w)
|
|
2866
|
+
sage: BinaryWord(labels=G.out_labels()).to_word() == w # indirect doctest
|
|
2867
|
+
True
|
|
2868
|
+
"""
|
|
2869
|
+
if x == y == z:
|
|
2870
|
+
return (x, 0)
|
|
2871
|
+
elif x == z != y:
|
|
2872
|
+
return (y, 0)
|
|
2873
|
+
elif x != z == y:
|
|
2874
|
+
return (x, 0)
|
|
2875
|
+
else:
|
|
2876
|
+
if x == y and len(z) > 0 and z[-1] == 1:
|
|
2877
|
+
return (x, 1)
|
|
2878
|
+
else:
|
|
2879
|
+
return (x[:-1], 0)
|
|
2880
|
+
|
|
2881
|
+
|
|
2882
|
+
class RuleSylvester(Rule):
|
|
2883
|
+
r"""
|
|
2884
|
+
A rule modelling a Schensted-like correspondence for binary trees.
|
|
2885
|
+
|
|
2886
|
+
EXAMPLES::
|
|
2887
|
+
|
|
2888
|
+
sage: Sylvester = GrowthDiagram.rules.Sylvester()
|
|
2889
|
+
sage: GrowthDiagram(Sylvester, [3,1,2])
|
|
2890
|
+
0 1 0
|
|
2891
|
+
0 0 1
|
|
2892
|
+
1 0 0
|
|
2893
|
+
|
|
2894
|
+
The vertices of the dual graded graph are
|
|
2895
|
+
:class:`~sage.combinat.binary_tree.BinaryTrees`::
|
|
2896
|
+
|
|
2897
|
+
sage: Sylvester.vertices(3)
|
|
2898
|
+
Binary trees of size 3
|
|
2899
|
+
|
|
2900
|
+
The :meth:`~sage.combinat.growth.Rule.P_graph` is also known as
|
|
2901
|
+
the bracket tree, the :meth:`~sage.combinat.growth.Rule.Q_graph`
|
|
2902
|
+
is the lattice of finite order ideals of the infinite binary
|
|
2903
|
+
tree, see Example 2.4.6 in [Fom1994]_.
|
|
2904
|
+
|
|
2905
|
+
For a permutation, the :meth:`P_symbol` is the binary search
|
|
2906
|
+
tree, the :meth:`Q_symbol` is the increasing tree corresponding
|
|
2907
|
+
to the inverse permutation. Note that, instead of passing the
|
|
2908
|
+
rule to :class:`GrowthDiagram`, we can also call the rule to
|
|
2909
|
+
create growth diagrams. From [Nze2007]_::
|
|
2910
|
+
|
|
2911
|
+
sage: pi = Permutation([3,5,1,4,2,6]); G = Sylvester(pi); G
|
|
2912
|
+
0 0 1 0 0 0
|
|
2913
|
+
0 0 0 0 1 0
|
|
2914
|
+
1 0 0 0 0 0
|
|
2915
|
+
0 0 0 1 0 0
|
|
2916
|
+
0 1 0 0 0 0
|
|
2917
|
+
0 0 0 0 0 1
|
|
2918
|
+
sage: ascii_art(G.P_symbol())
|
|
2919
|
+
__3__
|
|
2920
|
+
/ \
|
|
2921
|
+
1 5
|
|
2922
|
+
\ / \
|
|
2923
|
+
2 4 6
|
|
2924
|
+
sage: ascii_art(G.Q_symbol())
|
|
2925
|
+
__1__
|
|
2926
|
+
/ \
|
|
2927
|
+
3 2
|
|
2928
|
+
\ / \
|
|
2929
|
+
5 4 6
|
|
2930
|
+
|
|
2931
|
+
sage: all(Sylvester(pi).P_symbol() == pi.binary_search_tree()
|
|
2932
|
+
....: for pi in Permutations(5))
|
|
2933
|
+
True
|
|
2934
|
+
|
|
2935
|
+
sage: all(Sylvester(pi).Q_symbol() == pi.inverse().increasing_tree()
|
|
2936
|
+
....: for pi in Permutations(5))
|
|
2937
|
+
True
|
|
2938
|
+
|
|
2939
|
+
TESTS::
|
|
2940
|
+
|
|
2941
|
+
sage: Sylvester.zero
|
|
2942
|
+
.
|
|
2943
|
+
|
|
2944
|
+
sage: B = BinaryTree; R = B([None,[]]); L = B([[],None])
|
|
2945
|
+
sage: T = B([[],[]]); S = B([L,None])
|
|
2946
|
+
sage: G = Sylvester(labels=[R, T, R])
|
|
2947
|
+
Traceback (most recent call last):
|
|
2948
|
+
...
|
|
2949
|
+
ValueError: [., [., .]] has smaller rank than [[., .], [., .]]
|
|
2950
|
+
but is not covered by it in P
|
|
2951
|
+
|
|
2952
|
+
sage: G = Sylvester(labels=[R, S, R])
|
|
2953
|
+
Traceback (most recent call last):
|
|
2954
|
+
...
|
|
2955
|
+
ValueError: [., [., .]] has smaller rank than [[[., .], .], .]
|
|
2956
|
+
but is not covered by it in Q
|
|
2957
|
+
|
|
2958
|
+
Check duality::
|
|
2959
|
+
|
|
2960
|
+
sage: Sylvester._check_duality(4)
|
|
2961
|
+
|
|
2962
|
+
Check that the rules are bijective::
|
|
2963
|
+
|
|
2964
|
+
sage: all(Sylvester(labels=GrowthDiagram(Sylvester, pi).out_labels()).to_word()
|
|
2965
|
+
....: == pi for pi in Permutations(4))
|
|
2966
|
+
True
|
|
2967
|
+
sage: pi = Permutations(10).random_element()
|
|
2968
|
+
sage: G = GrowthDiagram(Sylvester, pi)
|
|
2969
|
+
sage: list(Sylvester(labels=G.out_labels())) == list(G)
|
|
2970
|
+
True
|
|
2971
|
+
"""
|
|
2972
|
+
zero = BinaryTree() # type:ignore
|
|
2973
|
+
|
|
2974
|
+
def normalize_vertex(self, v):
|
|
2975
|
+
r"""
|
|
2976
|
+
Return ``v`` as a binary tree.
|
|
2977
|
+
|
|
2978
|
+
EXAMPLES::
|
|
2979
|
+
|
|
2980
|
+
sage: Sylvester = GrowthDiagram.rules.Sylvester()
|
|
2981
|
+
sage: Sylvester.normalize_vertex([[],[]]).parent()
|
|
2982
|
+
Binary trees
|
|
2983
|
+
"""
|
|
2984
|
+
return BinaryTree(v)
|
|
2985
|
+
|
|
2986
|
+
def vertices(self, n):
|
|
2987
|
+
r"""
|
|
2988
|
+
Return the vertices of the dual graded graph on level ``n``.
|
|
2989
|
+
|
|
2990
|
+
EXAMPLES::
|
|
2991
|
+
|
|
2992
|
+
sage: Sylvester = GrowthDiagram.rules.Sylvester()
|
|
2993
|
+
sage: Sylvester.vertices(3)
|
|
2994
|
+
Binary trees of size 3
|
|
2995
|
+
"""
|
|
2996
|
+
return BinaryTrees(n)
|
|
2997
|
+
|
|
2998
|
+
def rank(self, v):
|
|
2999
|
+
r"""
|
|
3000
|
+
Return the rank of ``v``: the number of nodes of the tree.
|
|
3001
|
+
|
|
3002
|
+
EXAMPLES::
|
|
3003
|
+
|
|
3004
|
+
sage: Sylvester = GrowthDiagram.rules.Sylvester()
|
|
3005
|
+
sage: Sylvester.rank(Sylvester.vertices(3)[0])
|
|
3006
|
+
3
|
|
3007
|
+
"""
|
|
3008
|
+
return v.node_number()
|
|
3009
|
+
|
|
3010
|
+
def is_Q_edge(self, v, w):
|
|
3011
|
+
r"""
|
|
3012
|
+
Return whether ``(v, w)`` is a `Q`-edge of ``self``.
|
|
3013
|
+
|
|
3014
|
+
``(v, w)`` is an edge if ``v`` is a sub-tree of ``w`` with one
|
|
3015
|
+
node less.
|
|
3016
|
+
|
|
3017
|
+
EXAMPLES::
|
|
3018
|
+
|
|
3019
|
+
sage: Sylvester = GrowthDiagram.rules.Sylvester()
|
|
3020
|
+
sage: v = Sylvester.vertices(2)[1]; ascii_art(v)
|
|
3021
|
+
o
|
|
3022
|
+
/
|
|
3023
|
+
o
|
|
3024
|
+
sage: ascii_art([w for w in Sylvester.vertices(3) if Sylvester.is_Q_edge(v, w)])
|
|
3025
|
+
[ o , o, o ]
|
|
3026
|
+
[ / \ / / ]
|
|
3027
|
+
[ o o o o ]
|
|
3028
|
+
[ \ / ]
|
|
3029
|
+
[ o o ]
|
|
3030
|
+
sage: [w for w in Sylvester.vertices(4) if Sylvester.is_Q_edge(v, w)]
|
|
3031
|
+
[]
|
|
3032
|
+
"""
|
|
3033
|
+
def is_subtree(T1, T2):
|
|
3034
|
+
if T2.is_empty():
|
|
3035
|
+
return False
|
|
3036
|
+
elif T2[0].is_empty() and T2[1].is_empty():
|
|
3037
|
+
return T1.is_empty()
|
|
3038
|
+
elif T1.is_empty():
|
|
3039
|
+
return False
|
|
3040
|
+
else:
|
|
3041
|
+
return ((T1[0] == T2[0] and is_subtree(T1[1], T2[1])) or
|
|
3042
|
+
(T1[1] == T2[1] and is_subtree(T1[0], T2[0])))
|
|
3043
|
+
return is_subtree(v, w)
|
|
3044
|
+
|
|
3045
|
+
def is_P_edge(self, v, w):
|
|
3046
|
+
r"""
|
|
3047
|
+
Return whether ``(v, w)`` is a `P`-edge of ``self``.
|
|
3048
|
+
|
|
3049
|
+
``(v, w)`` is an edge if ``v`` is obtained from ``w`` by deleting
|
|
3050
|
+
its right-most node.
|
|
3051
|
+
|
|
3052
|
+
EXAMPLES::
|
|
3053
|
+
|
|
3054
|
+
sage: Sylvester = GrowthDiagram.rules.Sylvester()
|
|
3055
|
+
sage: v = Sylvester.vertices(2)[1]; ascii_art(v)
|
|
3056
|
+
o
|
|
3057
|
+
/
|
|
3058
|
+
o
|
|
3059
|
+
|
|
3060
|
+
sage: ascii_art([w for w in Sylvester.vertices(3) if Sylvester.is_P_edge(v, w)])
|
|
3061
|
+
[ o , o ]
|
|
3062
|
+
[ / \ / ]
|
|
3063
|
+
[ o o o ]
|
|
3064
|
+
[ / ]
|
|
3065
|
+
[ o ]
|
|
3066
|
+
|
|
3067
|
+
sage: [w for w in Sylvester.vertices(4) if Sylvester.is_P_edge(v, w)]
|
|
3068
|
+
[]
|
|
3069
|
+
"""
|
|
3070
|
+
if w.is_empty():
|
|
3071
|
+
return False
|
|
3072
|
+
else:
|
|
3073
|
+
return v == RuleSylvester._delete_right_most_node(w)
|
|
3074
|
+
|
|
3075
|
+
def P_symbol(self, P_chain):
|
|
3076
|
+
r"""
|
|
3077
|
+
Return the labels along the vertical boundary of a rectangular
|
|
3078
|
+
growth diagram as a labelled binary tree.
|
|
3079
|
+
|
|
3080
|
+
For permutations, this coincides with the binary search tree.
|
|
3081
|
+
|
|
3082
|
+
EXAMPLES::
|
|
3083
|
+
|
|
3084
|
+
sage: Sylvester = GrowthDiagram.rules.Sylvester()
|
|
3085
|
+
sage: pi = Permutation([2,4,3,1])
|
|
3086
|
+
sage: ascii_art(Sylvester(pi).P_symbol())
|
|
3087
|
+
_2_
|
|
3088
|
+
/ \
|
|
3089
|
+
1 4
|
|
3090
|
+
/
|
|
3091
|
+
3
|
|
3092
|
+
sage: Sylvester(pi).P_symbol() == pi.binary_search_tree()
|
|
3093
|
+
True
|
|
3094
|
+
|
|
3095
|
+
We can also do the skew version::
|
|
3096
|
+
|
|
3097
|
+
sage: B = BinaryTree; E = B(); N = B([])
|
|
3098
|
+
sage: ascii_art(Sylvester([3,2], shape=[3,3,3], labels=[N,N,N,E,E,E,N]).P_symbol())
|
|
3099
|
+
__1___
|
|
3100
|
+
/ \
|
|
3101
|
+
None 3
|
|
3102
|
+
/
|
|
3103
|
+
2
|
|
3104
|
+
"""
|
|
3105
|
+
def add_label(L, S, T, m):
|
|
3106
|
+
if T[0] == S:
|
|
3107
|
+
L = LabelledBinaryTree([L, None], m)
|
|
3108
|
+
else:
|
|
3109
|
+
assert T[0] == S[0]
|
|
3110
|
+
l = L.label()
|
|
3111
|
+
L = LabelledBinaryTree([L[0], add_label(L[1], S[1], T[1], m)], l)
|
|
3112
|
+
return L
|
|
3113
|
+
|
|
3114
|
+
L = LabelledBinaryTree(P_chain[0])
|
|
3115
|
+
for i in range(1, len(P_chain)):
|
|
3116
|
+
S, T = P_chain[i-1], P_chain[i]
|
|
3117
|
+
L = add_label(L, S, T, i)
|
|
3118
|
+
return L
|
|
3119
|
+
|
|
3120
|
+
def Q_symbol(self, Q_chain):
|
|
3121
|
+
r"""
|
|
3122
|
+
Return the labels along the vertical boundary of a rectangular
|
|
3123
|
+
growth diagram as a labelled binary tree.
|
|
3124
|
+
|
|
3125
|
+
For permutations, this coincides with the increasing tree.
|
|
3126
|
+
|
|
3127
|
+
EXAMPLES::
|
|
3128
|
+
|
|
3129
|
+
sage: Sylvester = GrowthDiagram.rules.Sylvester()
|
|
3130
|
+
sage: pi = Permutation([2,4,3,1])
|
|
3131
|
+
sage: ascii_art(Sylvester(pi).Q_symbol())
|
|
3132
|
+
_1_
|
|
3133
|
+
/ \
|
|
3134
|
+
4 2
|
|
3135
|
+
/
|
|
3136
|
+
3
|
|
3137
|
+
sage: Sylvester(pi).Q_symbol() == pi.inverse().increasing_tree()
|
|
3138
|
+
True
|
|
3139
|
+
|
|
3140
|
+
We can also do the skew version::
|
|
3141
|
+
|
|
3142
|
+
sage: B = BinaryTree; E = B(); N = B([])
|
|
3143
|
+
sage: ascii_art(Sylvester([3,2], shape=[3,3,3], labels=[N,N,N,E,E,E,N]).Q_symbol())
|
|
3144
|
+
_None_
|
|
3145
|
+
/ \
|
|
3146
|
+
3 1
|
|
3147
|
+
/
|
|
3148
|
+
2
|
|
3149
|
+
"""
|
|
3150
|
+
def add_label(L, S, T, m):
|
|
3151
|
+
if L.is_empty():
|
|
3152
|
+
assert T.node_number() == 1
|
|
3153
|
+
return LabelledBinaryTree([], m)
|
|
3154
|
+
l = L.label()
|
|
3155
|
+
if T[0] == S[0]:
|
|
3156
|
+
return LabelledBinaryTree([L[0], add_label(L[1], S[1], T[1], m)], l)
|
|
3157
|
+
else:
|
|
3158
|
+
return LabelledBinaryTree([add_label(L[0], S[0], T[0], m), L[1]], l)
|
|
3159
|
+
|
|
3160
|
+
L = LabelledBinaryTree(Q_chain[0])
|
|
3161
|
+
for i in range(1, len(Q_chain)):
|
|
3162
|
+
S, T = Q_chain[i-1], Q_chain[i]
|
|
3163
|
+
L = add_label(L, S, T, i)
|
|
3164
|
+
return L
|
|
3165
|
+
|
|
3166
|
+
@staticmethod
|
|
3167
|
+
def _delete_right_most_node(b):
|
|
3168
|
+
r"""
|
|
3169
|
+
Return the tree obtained by deleting the right most node from ``b``.
|
|
3170
|
+
|
|
3171
|
+
TESTS::
|
|
3172
|
+
|
|
3173
|
+
sage: Sylvester = GrowthDiagram.rules.Sylvester()
|
|
3174
|
+
sage: b = BinaryTree([]); b
|
|
3175
|
+
[., .]
|
|
3176
|
+
sage: Sylvester._delete_right_most_node(b)
|
|
3177
|
+
.
|
|
3178
|
+
sage: b = BinaryTree([[[], []], None]); ascii_art(b)
|
|
3179
|
+
o
|
|
3180
|
+
/
|
|
3181
|
+
o
|
|
3182
|
+
/ \
|
|
3183
|
+
o o
|
|
3184
|
+
sage: ascii_art(Sylvester._delete_right_most_node(b))
|
|
3185
|
+
o
|
|
3186
|
+
/ \
|
|
3187
|
+
o o
|
|
3188
|
+
"""
|
|
3189
|
+
if b.is_empty():
|
|
3190
|
+
raise ValueError("cannot delete right most node from empty tree")
|
|
3191
|
+
elif b[1].is_empty():
|
|
3192
|
+
return b[0]
|
|
3193
|
+
else:
|
|
3194
|
+
return BinaryTree([b[0], RuleSylvester._delete_right_most_node(b[1])])
|
|
3195
|
+
|
|
3196
|
+
def forward_rule(self, y, t, x, content):
|
|
3197
|
+
r"""
|
|
3198
|
+
Return the output shape given three shapes and the content.
|
|
3199
|
+
|
|
3200
|
+
See [Nze2007]_, page 9.
|
|
3201
|
+
|
|
3202
|
+
INPUT:
|
|
3203
|
+
|
|
3204
|
+
- ``y``, ``t``, ``x`` -- three binary trees from a cell in a growth
|
|
3205
|
+
diagram, labelled as::
|
|
3206
|
+
|
|
3207
|
+
t x
|
|
3208
|
+
y
|
|
3209
|
+
|
|
3210
|
+
- ``content`` -- `0` or `1`; the content of the cell
|
|
3211
|
+
|
|
3212
|
+
OUTPUT: the fourth binary tree ``z``
|
|
3213
|
+
|
|
3214
|
+
EXAMPLES::
|
|
3215
|
+
|
|
3216
|
+
sage: Sylvester = GrowthDiagram.rules.Sylvester()
|
|
3217
|
+
sage: B = BinaryTree; E = B(); N = B([]); L = B([[],None])
|
|
3218
|
+
sage: R = B([None,[]]); T = B([[],[]])
|
|
3219
|
+
|
|
3220
|
+
sage: ascii_art(Sylvester.forward_rule(E, E, E, 1))
|
|
3221
|
+
o
|
|
3222
|
+
sage: ascii_art(Sylvester.forward_rule(N, N, N, 1))
|
|
3223
|
+
o
|
|
3224
|
+
\
|
|
3225
|
+
o
|
|
3226
|
+
sage: ascii_art(Sylvester.forward_rule(L, L, L, 1))
|
|
3227
|
+
o
|
|
3228
|
+
/ \
|
|
3229
|
+
o o
|
|
3230
|
+
sage: ascii_art(Sylvester.forward_rule(R, R, R, 1))
|
|
3231
|
+
o
|
|
3232
|
+
\
|
|
3233
|
+
o
|
|
3234
|
+
\
|
|
3235
|
+
o
|
|
3236
|
+
|
|
3237
|
+
If ``y != x``, obtain ``z`` from ``y`` adding a node such
|
|
3238
|
+
that deleting the right most gives ``x``::
|
|
3239
|
+
|
|
3240
|
+
sage: ascii_art(Sylvester.forward_rule(R, N, L, 0))
|
|
3241
|
+
o
|
|
3242
|
+
/ \
|
|
3243
|
+
o o
|
|
3244
|
+
|
|
3245
|
+
sage: ascii_art(Sylvester.forward_rule(L, N, R, 0))
|
|
3246
|
+
o
|
|
3247
|
+
/
|
|
3248
|
+
o
|
|
3249
|
+
\
|
|
3250
|
+
o
|
|
3251
|
+
|
|
3252
|
+
If ``y == x != t``, obtain ``z`` from ``x`` by adding a node
|
|
3253
|
+
as left child to the right most node::
|
|
3254
|
+
|
|
3255
|
+
sage: ascii_art(Sylvester.forward_rule(N, E, N, 0))
|
|
3256
|
+
o
|
|
3257
|
+
/
|
|
3258
|
+
o
|
|
3259
|
+
sage: ascii_art(Sylvester.forward_rule(T, L, T, 0))
|
|
3260
|
+
_o_
|
|
3261
|
+
/ \
|
|
3262
|
+
o o
|
|
3263
|
+
/
|
|
3264
|
+
o
|
|
3265
|
+
sage: ascii_art(Sylvester.forward_rule(L, N, L, 0))
|
|
3266
|
+
o
|
|
3267
|
+
/
|
|
3268
|
+
o
|
|
3269
|
+
/
|
|
3270
|
+
o
|
|
3271
|
+
sage: ascii_art(Sylvester.forward_rule(R, N, R, 0))
|
|
3272
|
+
o
|
|
3273
|
+
\
|
|
3274
|
+
o
|
|
3275
|
+
/
|
|
3276
|
+
o
|
|
3277
|
+
"""
|
|
3278
|
+
def successors(b):
|
|
3279
|
+
r"""
|
|
3280
|
+
Return all trees obtained from ``b`` by adding a node.
|
|
3281
|
+
"""
|
|
3282
|
+
if b.is_empty():
|
|
3283
|
+
yield BinaryTree([])
|
|
3284
|
+
else:
|
|
3285
|
+
for t in successors(b[0]):
|
|
3286
|
+
yield BinaryTree([t, b[1]])
|
|
3287
|
+
for t in successors(b[1]):
|
|
3288
|
+
yield BinaryTree([b[0], t])
|
|
3289
|
+
|
|
3290
|
+
def union(y, x):
|
|
3291
|
+
r"""
|
|
3292
|
+
Return the unique tree obtained by adding a node to ``y`` such
|
|
3293
|
+
that deleting the right most node gives ``x``.
|
|
3294
|
+
"""
|
|
3295
|
+
for t in successors(y):
|
|
3296
|
+
if RuleSylvester._delete_right_most_node(t) == x:
|
|
3297
|
+
return t
|
|
3298
|
+
raise ValueError("could not find union of %s and %s" % (y,x))
|
|
3299
|
+
|
|
3300
|
+
if y == t == x:
|
|
3301
|
+
if content == 0:
|
|
3302
|
+
z = y
|
|
3303
|
+
elif content == 1:
|
|
3304
|
+
z = t.over(BinaryTree([]))
|
|
3305
|
+
else:
|
|
3306
|
+
raise NotImplementedError
|
|
3307
|
+
elif content != 0:
|
|
3308
|
+
raise ValueError("for y=%s, t=%s, x=%s, the content should be 0 but is %s" % (y, t, x, content))
|
|
3309
|
+
elif y != t == x:
|
|
3310
|
+
z = y
|
|
3311
|
+
elif y == t != x:
|
|
3312
|
+
z = x
|
|
3313
|
+
else:
|
|
3314
|
+
z = union(y, x)
|
|
3315
|
+
return z
|
|
3316
|
+
|
|
3317
|
+
def backward_rule(self, y, z, x):
|
|
3318
|
+
r"""
|
|
3319
|
+
Return the output shape given three shapes and the content.
|
|
3320
|
+
|
|
3321
|
+
See [Nze2007]_, page 9.
|
|
3322
|
+
|
|
3323
|
+
INPUT:
|
|
3324
|
+
|
|
3325
|
+
- ``y``, ``z``, ``x`` -- three binary trees from a cell in a growth
|
|
3326
|
+
diagram, labelled as::
|
|
3327
|
+
|
|
3328
|
+
x
|
|
3329
|
+
y z
|
|
3330
|
+
|
|
3331
|
+
OUTPUT:
|
|
3332
|
+
|
|
3333
|
+
A pair ``(t, content)`` consisting of the shape of the fourth
|
|
3334
|
+
binary tree t and the content of the cell.
|
|
3335
|
+
|
|
3336
|
+
EXAMPLES::
|
|
3337
|
+
|
|
3338
|
+
sage: Sylvester = GrowthDiagram.rules.Sylvester()
|
|
3339
|
+
sage: B = BinaryTree; E = B(); N = B([]); L = B([[],None])
|
|
3340
|
+
sage: R = B([None,[]]); T = B([[],[]])
|
|
3341
|
+
|
|
3342
|
+
sage: ascii_art(Sylvester.backward_rule(E, E, E))
|
|
3343
|
+
( , 0 )
|
|
3344
|
+
sage: ascii_art(Sylvester.backward_rule(N, N, N))
|
|
3345
|
+
( o, 0 )
|
|
3346
|
+
"""
|
|
3347
|
+
if x == y == z:
|
|
3348
|
+
return (x, 0)
|
|
3349
|
+
elif x == z != y:
|
|
3350
|
+
return (y, 0)
|
|
3351
|
+
elif x != z == y:
|
|
3352
|
+
return (x, 0)
|
|
3353
|
+
else:
|
|
3354
|
+
if x == y and z == x.over(BinaryTree([])):
|
|
3355
|
+
return (x, 1)
|
|
3356
|
+
else:
|
|
3357
|
+
t = RuleSylvester._delete_right_most_node(y)
|
|
3358
|
+
return (t, 0)
|
|
3359
|
+
|
|
3360
|
+
|
|
3361
|
+
class RuleYoungFibonacci(Rule):
|
|
3362
|
+
r"""
|
|
3363
|
+
A rule modelling a Schensted-like correspondence for
|
|
3364
|
+
Young-Fibonacci-tableaux.
|
|
3365
|
+
|
|
3366
|
+
EXAMPLES::
|
|
3367
|
+
|
|
3368
|
+
sage: YF = GrowthDiagram.rules.YoungFibonacci()
|
|
3369
|
+
sage: GrowthDiagram(YF, [3,1,2])
|
|
3370
|
+
0 1 0
|
|
3371
|
+
0 0 1
|
|
3372
|
+
1 0 0
|
|
3373
|
+
|
|
3374
|
+
The vertices of the dual graded graph are Fibonacci words -
|
|
3375
|
+
compositions into parts of size at most two::
|
|
3376
|
+
|
|
3377
|
+
sage: YF.vertices(4)
|
|
3378
|
+
[word: 22, word: 211, word: 121, word: 112, word: 1111]
|
|
3379
|
+
|
|
3380
|
+
Note that, instead of passing the rule to :class:`GrowthDiagram`,
|
|
3381
|
+
we can also use call the rule to create growth diagrams. For
|
|
3382
|
+
example::
|
|
3383
|
+
|
|
3384
|
+
sage: G = YF([2, 3, 7, 4, 1, 6, 5]); G
|
|
3385
|
+
0 0 0 0 1 0 0
|
|
3386
|
+
1 0 0 0 0 0 0
|
|
3387
|
+
0 1 0 0 0 0 0
|
|
3388
|
+
0 0 0 1 0 0 0
|
|
3389
|
+
0 0 0 0 0 0 1
|
|
3390
|
+
0 0 0 0 0 1 0
|
|
3391
|
+
0 0 1 0 0 0 0
|
|
3392
|
+
|
|
3393
|
+
The Kleitman Greene invariant is: take the last letter and the
|
|
3394
|
+
largest letter of the permutation and remove them. If they
|
|
3395
|
+
coincide write 1, otherwise write 2::
|
|
3396
|
+
|
|
3397
|
+
sage: G.P_chain()[-1]
|
|
3398
|
+
word: 21211
|
|
3399
|
+
|
|
3400
|
+
TESTS::
|
|
3401
|
+
|
|
3402
|
+
sage: YF = GrowthDiagram.rules.YoungFibonacci()
|
|
3403
|
+
sage: YF.zero
|
|
3404
|
+
word:
|
|
3405
|
+
|
|
3406
|
+
Check duality::
|
|
3407
|
+
|
|
3408
|
+
sage: YF._check_duality(4)
|
|
3409
|
+
|
|
3410
|
+
sage: G = YF(labels=[[1],[1,0],[1]])
|
|
3411
|
+
Traceback (most recent call last):
|
|
3412
|
+
...
|
|
3413
|
+
ValueError: 0 not in alphabet
|
|
3414
|
+
|
|
3415
|
+
sage: G = YF(labels=[[1,1],[1,2]])
|
|
3416
|
+
Traceback (most recent call last):
|
|
3417
|
+
...
|
|
3418
|
+
ValueError: 11 has smaller rank than 12 but is not covered by it in Q
|
|
3419
|
+
|
|
3420
|
+
sage: G = YF(labels=[[1,2],[1,1]])
|
|
3421
|
+
Traceback (most recent call last):
|
|
3422
|
+
...
|
|
3423
|
+
ValueError: 11 has smaller rank than 12 but is not covered by it in P
|
|
3424
|
+
|
|
3425
|
+
sage: all(YF(labels=YF(pi).out_labels()).to_word()
|
|
3426
|
+
....: == pi for pi in Permutations(4))
|
|
3427
|
+
True
|
|
3428
|
+
sage: pi = Permutations(10).random_element()
|
|
3429
|
+
sage: G = YF(pi)
|
|
3430
|
+
sage: list(YF(labels=G.out_labels())) == list(G)
|
|
3431
|
+
True
|
|
3432
|
+
"""
|
|
3433
|
+
zero = Word([], alphabet=[1,2])
|
|
3434
|
+
|
|
3435
|
+
def normalize_vertex(self, v):
|
|
3436
|
+
r"""
|
|
3437
|
+
Return ``v`` as a word with letters 1 and 2.
|
|
3438
|
+
|
|
3439
|
+
EXAMPLES::
|
|
3440
|
+
|
|
3441
|
+
sage: YF = GrowthDiagram.rules.YoungFibonacci()
|
|
3442
|
+
sage: YF.normalize_vertex([1,2,1]).parent()
|
|
3443
|
+
Finite words over {1, 2}
|
|
3444
|
+
"""
|
|
3445
|
+
return Word(v, alphabet=[1,2])
|
|
3446
|
+
|
|
3447
|
+
def vertices(self, n):
|
|
3448
|
+
r"""
|
|
3449
|
+
Return the vertices of the dual graded graph on level ``n``.
|
|
3450
|
+
|
|
3451
|
+
EXAMPLES::
|
|
3452
|
+
|
|
3453
|
+
sage: YF = GrowthDiagram.rules.YoungFibonacci()
|
|
3454
|
+
sage: YF.vertices(3)
|
|
3455
|
+
[word: 21, word: 12, word: 111]
|
|
3456
|
+
"""
|
|
3457
|
+
if n == 0:
|
|
3458
|
+
return [self.zero]
|
|
3459
|
+
else:
|
|
3460
|
+
return [Word(list(w), [1,2]) for w in Compositions(n, max_part=2)]
|
|
3461
|
+
|
|
3462
|
+
def rank(self, v):
|
|
3463
|
+
r"""
|
|
3464
|
+
Return the rank of ``v``: the size of the corresponding composition.
|
|
3465
|
+
|
|
3466
|
+
EXAMPLES::
|
|
3467
|
+
|
|
3468
|
+
sage: YF = GrowthDiagram.rules.YoungFibonacci()
|
|
3469
|
+
sage: YF.rank(YF.vertices(3)[0])
|
|
3470
|
+
3
|
|
3471
|
+
"""
|
|
3472
|
+
return sum(v)
|
|
3473
|
+
|
|
3474
|
+
def is_P_edge(self, v, w):
|
|
3475
|
+
r"""
|
|
3476
|
+
Return whether ``(v, w)`` is a `P`-edge of ``self``.
|
|
3477
|
+
|
|
3478
|
+
``(v, w)`` is an edge if ``v`` is obtained from ``w`` by deleting
|
|
3479
|
+
a ``1`` or replacing the left-most ``2`` by a ``1``.
|
|
3480
|
+
|
|
3481
|
+
EXAMPLES::
|
|
3482
|
+
|
|
3483
|
+
sage: YF = GrowthDiagram.rules.YoungFibonacci()
|
|
3484
|
+
sage: v = YF.vertices(5)[5]; v
|
|
3485
|
+
word: 1121
|
|
3486
|
+
sage: [w for w in YF.vertices(6) if YF.is_P_edge(v, w)]
|
|
3487
|
+
[word: 2121, word: 11121]
|
|
3488
|
+
sage: [w for w in YF.vertices(7) if YF.is_P_edge(v, w)]
|
|
3489
|
+
[]
|
|
3490
|
+
"""
|
|
3491
|
+
if sum(w) != sum(v) + 1:
|
|
3492
|
+
return False
|
|
3493
|
+
ell = len(v)
|
|
3494
|
+
w = list(w)
|
|
3495
|
+
for i in range(ell+1):
|
|
3496
|
+
d = list(v)
|
|
3497
|
+
d.insert(i, 1)
|
|
3498
|
+
if w == d:
|
|
3499
|
+
return True
|
|
3500
|
+
if i < ell and v[i] == 1:
|
|
3501
|
+
d = list(v)
|
|
3502
|
+
d[i] = 2
|
|
3503
|
+
if w == d:
|
|
3504
|
+
return True
|
|
3505
|
+
break
|
|
3506
|
+
return False
|
|
3507
|
+
|
|
3508
|
+
is_Q_edge = is_P_edge
|
|
3509
|
+
|
|
3510
|
+
def forward_rule(self, y, t, x, content):
|
|
3511
|
+
r"""
|
|
3512
|
+
Return the output shape given three shapes and the content.
|
|
3513
|
+
|
|
3514
|
+
See [Fom1995]_ Lemma 4.4.1, page 35.
|
|
3515
|
+
|
|
3516
|
+
INPUT:
|
|
3517
|
+
|
|
3518
|
+
- ``y``, ``t``, ``x`` -- three Fibonacci words from a
|
|
3519
|
+
cell in a growth diagram, labelled as::
|
|
3520
|
+
|
|
3521
|
+
t x
|
|
3522
|
+
y
|
|
3523
|
+
|
|
3524
|
+
- ``content`` -- `0` or `1`; the content of the cell
|
|
3525
|
+
|
|
3526
|
+
OUTPUT: the fourth Fibonacci word
|
|
3527
|
+
|
|
3528
|
+
EXAMPLES::
|
|
3529
|
+
|
|
3530
|
+
sage: YF = GrowthDiagram.rules.YoungFibonacci()
|
|
3531
|
+
|
|
3532
|
+
sage: YF.forward_rule([], [], [], 1)
|
|
3533
|
+
word: 1
|
|
3534
|
+
|
|
3535
|
+
sage: YF.forward_rule([1], [1], [1], 1)
|
|
3536
|
+
word: 11
|
|
3537
|
+
|
|
3538
|
+
sage: YF.forward_rule([1,2], [1], [1,1], 0)
|
|
3539
|
+
word: 21
|
|
3540
|
+
|
|
3541
|
+
sage: YF.forward_rule([1,1], [1], [1,1], 0)
|
|
3542
|
+
word: 21
|
|
3543
|
+
"""
|
|
3544
|
+
if x == t == y:
|
|
3545
|
+
if content == 0:
|
|
3546
|
+
r = x
|
|
3547
|
+
elif content == 1:
|
|
3548
|
+
r = Word([1] + list(y), alphabet=[1,2])
|
|
3549
|
+
else:
|
|
3550
|
+
raise NotImplementedError
|
|
3551
|
+
elif content != 0:
|
|
3552
|
+
raise ValueError("for y=%s, t=%s, x=%s, the content should be 0 but is %s"
|
|
3553
|
+
% (y, t, x, content))
|
|
3554
|
+
elif x == t:
|
|
3555
|
+
r = y
|
|
3556
|
+
elif y == t:
|
|
3557
|
+
r = x
|
|
3558
|
+
else:
|
|
3559
|
+
if x != t != y:
|
|
3560
|
+
r = Word([2] + list(t), alphabet=[1,2])
|
|
3561
|
+
else:
|
|
3562
|
+
raise NotImplementedError("for y=%s, t=%s, x=%s, content %s we have no rule"
|
|
3563
|
+
% (y, t, x, content))
|
|
3564
|
+
return r
|
|
3565
|
+
|
|
3566
|
+
def backward_rule(self, y, z, x):
|
|
3567
|
+
r"""
|
|
3568
|
+
Return the content and the input shape.
|
|
3569
|
+
|
|
3570
|
+
See [Fom1995]_ Lemma 4.4.1, page 35.
|
|
3571
|
+
|
|
3572
|
+
- ``y``, ``z``, ``x`` -- three Fibonacci words from a cell in a
|
|
3573
|
+
growth diagram, labelled as::
|
|
3574
|
+
|
|
3575
|
+
x
|
|
3576
|
+
y z
|
|
3577
|
+
|
|
3578
|
+
OUTPUT:
|
|
3579
|
+
|
|
3580
|
+
A pair ``(t, content)`` consisting of the shape of the fourth
|
|
3581
|
+
word and the content of the cell.
|
|
3582
|
+
|
|
3583
|
+
TESTS::
|
|
3584
|
+
|
|
3585
|
+
sage: YF = GrowthDiagram.rules.YoungFibonacci()
|
|
3586
|
+
sage: w = [4,1,8,3,6,5,2,7,9]; G = YF(w)
|
|
3587
|
+
sage: GrowthDiagram(YF, labels=G.out_labels()).to_word() == w # indirect doctest
|
|
3588
|
+
True
|
|
3589
|
+
"""
|
|
3590
|
+
if x == y == z:
|
|
3591
|
+
return (x, 0)
|
|
3592
|
+
elif x == z != y:
|
|
3593
|
+
return (y, 0)
|
|
3594
|
+
elif x != z == y:
|
|
3595
|
+
return (x, 0)
|
|
3596
|
+
else:
|
|
3597
|
+
if z[0] == 1:
|
|
3598
|
+
return (z[1:], 1)
|
|
3599
|
+
elif z[0] == 2:
|
|
3600
|
+
return (z[1:], 0)
|
|
3601
|
+
|
|
3602
|
+
|
|
3603
|
+
class RulePartitions(Rule):
|
|
3604
|
+
r"""
|
|
3605
|
+
A rule for growth diagrams on Young's lattice on integer
|
|
3606
|
+
partitions graded by size.
|
|
3607
|
+
|
|
3608
|
+
TESTS::
|
|
3609
|
+
|
|
3610
|
+
sage: Burge = GrowthDiagram.rules.Burge()
|
|
3611
|
+
sage: Burge.zero
|
|
3612
|
+
[]
|
|
3613
|
+
sage: G = GrowthDiagram(Burge, labels=[[1],[1]])
|
|
3614
|
+
Traceback (most recent call last):
|
|
3615
|
+
...
|
|
3616
|
+
ValueError: can only determine the shape of the growth diagram
|
|
3617
|
+
if ranks of successive labels differ
|
|
3618
|
+
"""
|
|
3619
|
+
zero = _make_partition([])
|
|
3620
|
+
|
|
3621
|
+
def vertices(self, n):
|
|
3622
|
+
r"""
|
|
3623
|
+
Return the vertices of the dual graded graph on level ``n``.
|
|
3624
|
+
|
|
3625
|
+
EXAMPLES::
|
|
3626
|
+
|
|
3627
|
+
sage: RSK = GrowthDiagram.rules.RSK()
|
|
3628
|
+
sage: RSK.vertices(3)
|
|
3629
|
+
Partitions of the integer 3
|
|
3630
|
+
"""
|
|
3631
|
+
return Partitions(n)
|
|
3632
|
+
|
|
3633
|
+
def normalize_vertex(self, v):
|
|
3634
|
+
r"""
|
|
3635
|
+
Return ``v`` as a partition.
|
|
3636
|
+
|
|
3637
|
+
EXAMPLES::
|
|
3638
|
+
|
|
3639
|
+
sage: RSK = GrowthDiagram.rules.RSK()
|
|
3640
|
+
sage: RSK.normalize_vertex([3,1]).parent()
|
|
3641
|
+
Partitions
|
|
3642
|
+
"""
|
|
3643
|
+
return _make_partition(v)
|
|
3644
|
+
|
|
3645
|
+
def rank(self, v):
|
|
3646
|
+
r"""
|
|
3647
|
+
Return the rank of ``v``: the size of the partition.
|
|
3648
|
+
|
|
3649
|
+
EXAMPLES::
|
|
3650
|
+
|
|
3651
|
+
sage: RSK = GrowthDiagram.rules.RSK()
|
|
3652
|
+
sage: RSK.rank(RSK.vertices(3)[0])
|
|
3653
|
+
3
|
|
3654
|
+
"""
|
|
3655
|
+
return v.size()
|
|
3656
|
+
|
|
3657
|
+
def P_symbol(self, P_chain):
|
|
3658
|
+
r"""
|
|
3659
|
+
Return the labels along the vertical boundary of a rectangular
|
|
3660
|
+
growth diagram as a (skew) tableau.
|
|
3661
|
+
|
|
3662
|
+
EXAMPLES::
|
|
3663
|
+
|
|
3664
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
3665
|
+
sage: G = RuleRSK([[0,1,0], [1,0,2]])
|
|
3666
|
+
sage: G.P_symbol().pp()
|
|
3667
|
+
1 2 2
|
|
3668
|
+
2
|
|
3669
|
+
"""
|
|
3670
|
+
return SkewTableau(chain=P_chain)
|
|
3671
|
+
|
|
3672
|
+
def Q_symbol(self, Q_chain):
|
|
3673
|
+
r"""
|
|
3674
|
+
Return the labels along the horizontal boundary of a rectangular
|
|
3675
|
+
growth diagram as a skew tableau.
|
|
3676
|
+
|
|
3677
|
+
EXAMPLES::
|
|
3678
|
+
|
|
3679
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
3680
|
+
sage: G = RuleRSK([[0,1,0], [1,0,2]])
|
|
3681
|
+
sage: G.Q_symbol().pp()
|
|
3682
|
+
1 3 3
|
|
3683
|
+
2
|
|
3684
|
+
"""
|
|
3685
|
+
return SkewTableau(chain=Q_chain)
|
|
3686
|
+
|
|
3687
|
+
|
|
3688
|
+
class RuleRSK(RulePartitions):
|
|
3689
|
+
r"""
|
|
3690
|
+
A rule modelling Robinson-Schensted-Knuth insertion.
|
|
3691
|
+
|
|
3692
|
+
EXAMPLES::
|
|
3693
|
+
|
|
3694
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
3695
|
+
sage: GrowthDiagram(RuleRSK, [3,2,1,2,3])
|
|
3696
|
+
0 0 1 0 0
|
|
3697
|
+
0 1 0 1 0
|
|
3698
|
+
1 0 0 0 1
|
|
3699
|
+
|
|
3700
|
+
The vertices of the dual graded graph are integer partitions::
|
|
3701
|
+
|
|
3702
|
+
sage: RuleRSK.vertices(3)
|
|
3703
|
+
Partitions of the integer 3
|
|
3704
|
+
|
|
3705
|
+
The local rules implemented provide the RSK correspondence
|
|
3706
|
+
between matrices with nonnegative integer entries and pairs of
|
|
3707
|
+
semistandard tableaux, the
|
|
3708
|
+
:meth:`~sage.combinat.growth.RulePartitions.P_symbol` and the
|
|
3709
|
+
:meth:`~sage.combinat.growth.RulePartitions.Q_symbol`. For
|
|
3710
|
+
permutations, it reduces to classical Schensted insertion.
|
|
3711
|
+
|
|
3712
|
+
Instead of passing the rule to :class:`GrowthDiagram`, we can
|
|
3713
|
+
also call the rule to create growth diagrams. For example::
|
|
3714
|
+
|
|
3715
|
+
sage: # needs sage.modules
|
|
3716
|
+
sage: m = matrix([[0,0,0,0,1],[1,1,0,2,0], [0,3,0,0,0]])
|
|
3717
|
+
sage: G = RuleRSK(m); G
|
|
3718
|
+
0 0 0 0 1
|
|
3719
|
+
1 1 0 2 0
|
|
3720
|
+
0 3 0 0 0
|
|
3721
|
+
sage: ascii_art([G.P_symbol(), G.Q_symbol()])
|
|
3722
|
+
[ 1 2 2 2 3 1 2 2 2 2 ]
|
|
3723
|
+
[ 2 3 4 4 ]
|
|
3724
|
+
[ 3 , 5 ]
|
|
3725
|
+
|
|
3726
|
+
For rectangular fillings, the Kleitman-Greene invariant is the
|
|
3727
|
+
shape of the :meth:`P_symbol` (or the :meth:`Q_symbol`). Put
|
|
3728
|
+
differently, it is the partition labelling the lower right corner
|
|
3729
|
+
of the filling (recall that we are using matrix coordinates). It
|
|
3730
|
+
can be computed alternatively as the partition
|
|
3731
|
+
`(\mu_1,\dots,\mu_n)`, where `\mu_1 + \dots + \mu_i` is the
|
|
3732
|
+
maximal sum of entries in a collection of `i` pairwise disjoint
|
|
3733
|
+
sequences of cells with weakly increasing coordinates.
|
|
3734
|
+
|
|
3735
|
+
For rectangular fillings, we could also use the (faster)
|
|
3736
|
+
implementation provided via :func:`~sage.combinat.rsk.RSK`.
|
|
3737
|
+
Because the of the coordinate conventions in
|
|
3738
|
+
:func:`~sage.combinat.rsk.RSK`, we have to transpose matrices::
|
|
3739
|
+
|
|
3740
|
+
sage: # needs sage.modules
|
|
3741
|
+
sage: [G.P_symbol(), G.Q_symbol()] == RSK(m.transpose())
|
|
3742
|
+
True
|
|
3743
|
+
sage: n = 5; l = [(pi, RuleRSK(pi)) for pi in Permutations(n)]
|
|
3744
|
+
sage: all([G.P_symbol(), G.Q_symbol()] == RSK(pi) for pi, G in l)
|
|
3745
|
+
True
|
|
3746
|
+
sage: n = 5; l = [(w, RuleRSK(w)) for w in Words([1,2,3], 5)]
|
|
3747
|
+
sage: all([G.P_symbol(), G.Q_symbol()] == RSK(pi) for pi, G in l)
|
|
3748
|
+
True
|
|
3749
|
+
"""
|
|
3750
|
+
|
|
3751
|
+
def forward_rule(self, y, t, x, content):
|
|
3752
|
+
r"""
|
|
3753
|
+
Return the output shape given three shapes and the content.
|
|
3754
|
+
|
|
3755
|
+
See [Kra2006]_ `(F^1 0)-(F^1 2)`.
|
|
3756
|
+
|
|
3757
|
+
INPUT:
|
|
3758
|
+
|
|
3759
|
+
- ``y``, ``t``, ``x`` -- three partitions from a cell in a
|
|
3760
|
+
growth diagram, labelled as::
|
|
3761
|
+
|
|
3762
|
+
t x
|
|
3763
|
+
y
|
|
3764
|
+
|
|
3765
|
+
- ``content`` -- nonnegative integer; the content of the cell
|
|
3766
|
+
|
|
3767
|
+
OUTPUT:
|
|
3768
|
+
|
|
3769
|
+
The fourth partition according to the Robinson-Schensted-Knuth
|
|
3770
|
+
correspondence.
|
|
3771
|
+
|
|
3772
|
+
EXAMPLES::
|
|
3773
|
+
|
|
3774
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
3775
|
+
sage: RuleRSK.forward_rule([2,1],[2,1],[2,1],1)
|
|
3776
|
+
[3, 1]
|
|
3777
|
+
|
|
3778
|
+
sage: RuleRSK.forward_rule([1],[],[2],2)
|
|
3779
|
+
[4, 1]
|
|
3780
|
+
"""
|
|
3781
|
+
carry = content
|
|
3782
|
+
z = []
|
|
3783
|
+
while True:
|
|
3784
|
+
if x == []:
|
|
3785
|
+
row1 = 0
|
|
3786
|
+
else:
|
|
3787
|
+
row1 = x[0]
|
|
3788
|
+
if y == []:
|
|
3789
|
+
row3 = 0
|
|
3790
|
+
else:
|
|
3791
|
+
row3 = y[0]
|
|
3792
|
+
newPart = max(row1, row3) + carry
|
|
3793
|
+
if newPart == 0:
|
|
3794
|
+
# returning this as a Partition costs a lot of time
|
|
3795
|
+
return z[::-1]
|
|
3796
|
+
else:
|
|
3797
|
+
z = [newPart] + z
|
|
3798
|
+
if t == []:
|
|
3799
|
+
carry = min(row1, row3)
|
|
3800
|
+
else:
|
|
3801
|
+
carry = min(row1, row3) - t[0]
|
|
3802
|
+
x = x[1:]
|
|
3803
|
+
t = t[1:]
|
|
3804
|
+
y = y[1:]
|
|
3805
|
+
|
|
3806
|
+
def backward_rule(self, y, z, x):
|
|
3807
|
+
r"""
|
|
3808
|
+
Return the content and the input shape.
|
|
3809
|
+
|
|
3810
|
+
See [Kra2006]_ `(B^1 0)-(B^1 2)`.
|
|
3811
|
+
|
|
3812
|
+
INPUT:
|
|
3813
|
+
|
|
3814
|
+
- ``y``, ``z``, ``x`` -- three partitions from a cell in a
|
|
3815
|
+
growth diagram, labelled as::
|
|
3816
|
+
|
|
3817
|
+
x
|
|
3818
|
+
y z
|
|
3819
|
+
|
|
3820
|
+
OUTPUT:
|
|
3821
|
+
|
|
3822
|
+
A pair ``(t, content)`` consisting of the shape of the fourth
|
|
3823
|
+
word according to the Robinson-Schensted-Knuth correspondence
|
|
3824
|
+
and the content of the cell.
|
|
3825
|
+
|
|
3826
|
+
TESTS::
|
|
3827
|
+
|
|
3828
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
3829
|
+
sage: w = [4,1,8,3,6,5,2,7,9]; G = RuleRSK(w)
|
|
3830
|
+
sage: GrowthDiagram(RuleRSK, labels=G._out_labels).to_word() == w # indirect doctest
|
|
3831
|
+
True
|
|
3832
|
+
"""
|
|
3833
|
+
carry = 0
|
|
3834
|
+
i = len(z)
|
|
3835
|
+
t = []
|
|
3836
|
+
while i > 0:
|
|
3837
|
+
if len(x) < i:
|
|
3838
|
+
row1 = 0
|
|
3839
|
+
else:
|
|
3840
|
+
row1 = x[i-1]
|
|
3841
|
+
if len(y) < i:
|
|
3842
|
+
row3 = 0
|
|
3843
|
+
else:
|
|
3844
|
+
row3 = y[i-1]
|
|
3845
|
+
t = [min(row1, row3) - carry] + t
|
|
3846
|
+
carry = z[i-1] - max(row1, row3)
|
|
3847
|
+
i = i-1
|
|
3848
|
+
return (_make_partition(t), carry)
|
|
3849
|
+
|
|
3850
|
+
|
|
3851
|
+
class RuleBurge(RulePartitions):
|
|
3852
|
+
r"""
|
|
3853
|
+
A rule modelling Burge insertion.
|
|
3854
|
+
|
|
3855
|
+
EXAMPLES::
|
|
3856
|
+
|
|
3857
|
+
sage: Burge = GrowthDiagram.rules.Burge()
|
|
3858
|
+
sage: GrowthDiagram(Burge, labels=[[],[1,1,1],[2,1,1,1],[2,1,1],[2,1],[1,1],[]])
|
|
3859
|
+
1 1
|
|
3860
|
+
0 1
|
|
3861
|
+
1 0
|
|
3862
|
+
1 0
|
|
3863
|
+
|
|
3864
|
+
The vertices of the dual graded graph are integer partitions::
|
|
3865
|
+
|
|
3866
|
+
sage: Burge.vertices(3)
|
|
3867
|
+
Partitions of the integer 3
|
|
3868
|
+
|
|
3869
|
+
The local rules implemented provide Burge's correspondence
|
|
3870
|
+
between matrices with nonnegative integer entries and pairs of
|
|
3871
|
+
semistandard tableaux, the
|
|
3872
|
+
:meth:`~sage.combinat.growth.RulePartitions.P_symbol` and the
|
|
3873
|
+
:meth:`~sage.combinat.growth.RulePartitions.Q_symbol`. For
|
|
3874
|
+
permutations, it reduces to classical Schensted insertion.
|
|
3875
|
+
|
|
3876
|
+
Instead of passing the rule to :class:`GrowthDiagram`, we can
|
|
3877
|
+
also call the rule to create growth diagrams. For example::
|
|
3878
|
+
|
|
3879
|
+
sage: # needs sage.modules
|
|
3880
|
+
sage: m = matrix([[2,0,0,1,0],[1,1,0,0,0], [0,0,0,0,3]])
|
|
3881
|
+
sage: G = Burge(m); G
|
|
3882
|
+
2 0 0 1 0
|
|
3883
|
+
1 1 0 0 0
|
|
3884
|
+
0 0 0 0 3
|
|
3885
|
+
sage: ascii_art([G.P_symbol(), G.Q_symbol()])
|
|
3886
|
+
[ 1 2 3 1 2 5 ]
|
|
3887
|
+
[ 1 3 1 5 ]
|
|
3888
|
+
[ 1 3 1 5 ]
|
|
3889
|
+
[ 2 , 4 ]
|
|
3890
|
+
|
|
3891
|
+
For rectangular fillings, the Kleitman-Greene invariant is the
|
|
3892
|
+
shape of the
|
|
3893
|
+
:meth:`~sage.combinat.growth.RulePartitions.P_symbol`. Put
|
|
3894
|
+
differently, it is the partition labelling the lower right corner
|
|
3895
|
+
of the filling (recall that we are using matrix coordinates). It
|
|
3896
|
+
can be computed alternatively as the transpose of the partition
|
|
3897
|
+
`(\mu_1, \ldots, \mu_n)`, where `\mu_1 + \cdots + \mu_i` is the
|
|
3898
|
+
maximal sum of entries in a collection of `i` pairwise disjoint
|
|
3899
|
+
sequences of cells with weakly decreasing row indices and weakly
|
|
3900
|
+
increasing column indices.
|
|
3901
|
+
"""
|
|
3902
|
+
|
|
3903
|
+
def forward_rule(self, y, t, x, content):
|
|
3904
|
+
r"""
|
|
3905
|
+
Return the output shape given three shapes and the content.
|
|
3906
|
+
|
|
3907
|
+
See [Kra2006]_ `(F^4 0)-(F^4 2)`.
|
|
3908
|
+
|
|
3909
|
+
INPUT:
|
|
3910
|
+
|
|
3911
|
+
- ``y``, ``t``, ``x`` -- three from a cell in a growth diagram,
|
|
3912
|
+
labelled as::
|
|
3913
|
+
|
|
3914
|
+
t x
|
|
3915
|
+
y
|
|
3916
|
+
|
|
3917
|
+
- ``content`` -- nonnegative integer; the content of the cell
|
|
3918
|
+
|
|
3919
|
+
OUTPUT: the fourth partition according to the Burge correspondence
|
|
3920
|
+
|
|
3921
|
+
EXAMPLES::
|
|
3922
|
+
|
|
3923
|
+
sage: Burge = GrowthDiagram.rules.Burge()
|
|
3924
|
+
sage: Burge.forward_rule([2,1],[2,1],[2,1],1)
|
|
3925
|
+
[3, 1]
|
|
3926
|
+
|
|
3927
|
+
sage: Burge.forward_rule([1],[],[2],2)
|
|
3928
|
+
[2, 1, 1, 1]
|
|
3929
|
+
"""
|
|
3930
|
+
# n is the maximal length of longest decreasing chain by
|
|
3931
|
+
# Kleitman-Greene's theorem
|
|
3932
|
+
n = content + len(x) + len(y)
|
|
3933
|
+
x += [0]*(n-len(x))
|
|
3934
|
+
y += [0]*(n-len(y))
|
|
3935
|
+
t += [0]*(n-len(t))
|
|
3936
|
+
z = [0]*n
|
|
3937
|
+
carry = content
|
|
3938
|
+
for i, (row1, row2, row3) in enumerate(zip(x, t, y)):
|
|
3939
|
+
s = min(int(row1 == row2 == row3), carry)
|
|
3940
|
+
new_part = max(row1, row3) + s
|
|
3941
|
+
if new_part:
|
|
3942
|
+
z[i] = new_part
|
|
3943
|
+
carry += -s + min(row1, row3) - row2
|
|
3944
|
+
else:
|
|
3945
|
+
break
|
|
3946
|
+
return _make_partition(z)
|
|
3947
|
+
|
|
3948
|
+
def backward_rule(self, y, z, x):
|
|
3949
|
+
r"""
|
|
3950
|
+
Return the content and the input shape.
|
|
3951
|
+
|
|
3952
|
+
See [Kra2006]_ `(B^4 0)-(B^4 2)`. (In the arXiv version of
|
|
3953
|
+
the article there is a typo: in the computation of carry in
|
|
3954
|
+
`(B^4 2)` , `\rho` must be replaced by `\lambda`).
|
|
3955
|
+
|
|
3956
|
+
INPUT:
|
|
3957
|
+
|
|
3958
|
+
- ``y``, ``z``, ``x`` -- three partitions from a cell in a
|
|
3959
|
+
growth diagram, labelled as::
|
|
3960
|
+
|
|
3961
|
+
x
|
|
3962
|
+
y z
|
|
3963
|
+
|
|
3964
|
+
OUTPUT:
|
|
3965
|
+
|
|
3966
|
+
A pair ``(t, content)`` consisting of the shape of the fourth
|
|
3967
|
+
partition according to the Burge correspondence and the content of
|
|
3968
|
+
the cell.
|
|
3969
|
+
|
|
3970
|
+
EXAMPLES::
|
|
3971
|
+
|
|
3972
|
+
sage: Burge = GrowthDiagram.rules.Burge()
|
|
3973
|
+
sage: Burge.backward_rule([1,1,1],[2,1,1,1],[2,1,1])
|
|
3974
|
+
([1, 1], 0)
|
|
3975
|
+
|
|
3976
|
+
TESTS::
|
|
3977
|
+
|
|
3978
|
+
sage: w = [4,1,8,3,6,5,2,7,9]; G = Burge(w)
|
|
3979
|
+
sage: GrowthDiagram(Burge, labels=G._out_labels).to_word() == w # indirect doctest
|
|
3980
|
+
True
|
|
3981
|
+
"""
|
|
3982
|
+
t = [0]*len(z) # z must be the longest partition
|
|
3983
|
+
mu = [0]*(len(z)-len(x)) + x[::-1]
|
|
3984
|
+
nu = [0]*(len(z)-len(y)) + y[::-1]
|
|
3985
|
+
la = z[::-1]
|
|
3986
|
+
carry = 0
|
|
3987
|
+
for i, (mu_i, la_i, nu_i) in enumerate(zip(mu, la, nu)):
|
|
3988
|
+
s = min(int(mu_i == nu_i == la_i), carry)
|
|
3989
|
+
t[i] = min(mu_i, nu_i) - s
|
|
3990
|
+
carry += -s + la_i - max(mu_i, nu_i)
|
|
3991
|
+
t.reverse()
|
|
3992
|
+
return (_make_partition(t), carry)
|
|
3993
|
+
|
|
3994
|
+
|
|
3995
|
+
class RuleDomino(Rule):
|
|
3996
|
+
r"""
|
|
3997
|
+
A rule modelling domino insertion.
|
|
3998
|
+
|
|
3999
|
+
EXAMPLES::
|
|
4000
|
+
|
|
4001
|
+
sage: Domino = GrowthDiagram.rules.Domino()
|
|
4002
|
+
sage: GrowthDiagram(Domino, [[1,0,0],[0,0,1],[0,-1,0]])
|
|
4003
|
+
1 0 0
|
|
4004
|
+
0 0 1
|
|
4005
|
+
0 -1 0
|
|
4006
|
+
|
|
4007
|
+
The vertices of the dual graded graph are integer partitions
|
|
4008
|
+
whose Ferrers diagram can be tiled with dominoes::
|
|
4009
|
+
|
|
4010
|
+
sage: Domino.vertices(2)
|
|
4011
|
+
[[4], [3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]]
|
|
4012
|
+
|
|
4013
|
+
Instead of passing the rule to :class:`GrowthDiagram`, we can
|
|
4014
|
+
also call the rule to create growth diagrams. For example, let
|
|
4015
|
+
us check Figure 3 in [Lam2004]_::
|
|
4016
|
+
|
|
4017
|
+
sage: G = Domino([[0,0,0,-1],[0,0,1,0],[-1,0,0,0],[0,1,0,0]]); G
|
|
4018
|
+
0 0 0 -1
|
|
4019
|
+
0 0 1 0
|
|
4020
|
+
-1 0 0 0
|
|
4021
|
+
0 1 0 0
|
|
4022
|
+
|
|
4023
|
+
sage: ascii_art([G.P_symbol(), G.Q_symbol()])
|
|
4024
|
+
[ 1 2 4 1 2 2 ]
|
|
4025
|
+
[ 1 2 4 1 3 3 ]
|
|
4026
|
+
[ 3 3 , 4 4 ]
|
|
4027
|
+
|
|
4028
|
+
The spin of a domino tableau is half the number of vertical dominoes::
|
|
4029
|
+
|
|
4030
|
+
sage: def spin(T):
|
|
4031
|
+
....: return sum(2*len(set(row)) - len(row) for row in T)/4
|
|
4032
|
+
|
|
4033
|
+
According to [Lam2004]_, the number of negative entries in the
|
|
4034
|
+
signed permutation equals the sum of the spins of the two
|
|
4035
|
+
associated tableaux::
|
|
4036
|
+
|
|
4037
|
+
sage: pi = [3,-1,2,4,-5]
|
|
4038
|
+
sage: G = Domino(pi)
|
|
4039
|
+
sage: list(G.filling().values()).count(-1) == spin(G.P_symbol()) + spin(G.Q_symbol())
|
|
4040
|
+
True
|
|
4041
|
+
|
|
4042
|
+
Negating all signs transposes all the partitions::
|
|
4043
|
+
|
|
4044
|
+
sage: G.P_symbol() == Domino([-e for e in pi]).P_symbol().conjugate()
|
|
4045
|
+
True
|
|
4046
|
+
|
|
4047
|
+
TESTS:
|
|
4048
|
+
|
|
4049
|
+
Check duality::
|
|
4050
|
+
|
|
4051
|
+
sage: Domino = GrowthDiagram.rules.Domino()
|
|
4052
|
+
sage: Domino._check_duality(3)
|
|
4053
|
+
|
|
4054
|
+
sage: G = Domino([[0,1,0],[0,0,-1],[1,0,0]]); G
|
|
4055
|
+
0 1 0
|
|
4056
|
+
0 0 -1
|
|
4057
|
+
1 0 0
|
|
4058
|
+
|
|
4059
|
+
sage: ascii_art([G.P_symbol(), G.Q_symbol()])
|
|
4060
|
+
[ 1 1 1 1 ]
|
|
4061
|
+
[ 2 3 2 2 ]
|
|
4062
|
+
[ 2 3, 3 3 ]
|
|
4063
|
+
|
|
4064
|
+
sage: l = {pi: Domino(pi) for pi in SignedPermutations(4)}
|
|
4065
|
+
sage: S = Set([(G.P_symbol(), G.Q_symbol()) for G in l.values()])
|
|
4066
|
+
sage: S.cardinality()
|
|
4067
|
+
384
|
|
4068
|
+
|
|
4069
|
+
Check the color-to-spin property for all permutations of size 4::
|
|
4070
|
+
|
|
4071
|
+
sage: all(list(G.filling().values()).count(-1) == spin(G.P_symbol()) + spin(G.Q_symbol())
|
|
4072
|
+
....: for G in l.values())
|
|
4073
|
+
True
|
|
4074
|
+
|
|
4075
|
+
Negating all signs transposes all the partitions::
|
|
4076
|
+
|
|
4077
|
+
sage: W = SignedPermutations(4)
|
|
4078
|
+
sage: all(l[pi].P_symbol() == l[W([-e for e in pi])].P_symbol().conjugate()
|
|
4079
|
+
....: for pi in l)
|
|
4080
|
+
True
|
|
4081
|
+
|
|
4082
|
+
Check part of Theorem 4.2.3 in [Lee1996]_::
|
|
4083
|
+
|
|
4084
|
+
sage: def to_permutation(pi):
|
|
4085
|
+
....: pi1 = list(pi)
|
|
4086
|
+
....: n = len(pi1)
|
|
4087
|
+
....: pi2 = [-e for e in pi][::-1] + pi1
|
|
4088
|
+
....: return Permutation([e+n+1 if e<0 else e+n for e in pi2])
|
|
4089
|
+
sage: RuleRSK = GrowthDiagram.rules.RSK()
|
|
4090
|
+
sage: def good(pi):
|
|
4091
|
+
....: return Domino(pi).P_chain()[-1] == RuleRSK(to_permutation(pi)).P_chain()[-1]
|
|
4092
|
+
sage: all(good(pi) for pi in SignedPermutations(4))
|
|
4093
|
+
True
|
|
4094
|
+
|
|
4095
|
+
sage: G = Domino(labels=[[1],[2,1]])
|
|
4096
|
+
Traceback (most recent call last):
|
|
4097
|
+
...
|
|
4098
|
+
ValueError: [1] has smaller rank than [2, 1] but is not covered by it in Q
|
|
4099
|
+
|
|
4100
|
+
sage: G = Domino(labels=[[2,1],[1]])
|
|
4101
|
+
Traceback (most recent call last):
|
|
4102
|
+
...
|
|
4103
|
+
ValueError: [1] has smaller rank than [2, 1] but is not covered by it in P
|
|
4104
|
+
"""
|
|
4105
|
+
r = 2
|
|
4106
|
+
zero = _make_partition([])
|
|
4107
|
+
|
|
4108
|
+
def normalize_vertex(self, v):
|
|
4109
|
+
"""
|
|
4110
|
+
Return ``v`` as a partition.
|
|
4111
|
+
|
|
4112
|
+
EXAMPLES::
|
|
4113
|
+
|
|
4114
|
+
sage: Domino = GrowthDiagram.rules.Domino()
|
|
4115
|
+
sage: Domino.normalize_vertex([3,1]).parent()
|
|
4116
|
+
Partitions
|
|
4117
|
+
"""
|
|
4118
|
+
return _make_partition(v)
|
|
4119
|
+
|
|
4120
|
+
def vertices(self, n):
|
|
4121
|
+
r"""
|
|
4122
|
+
Return the vertices of the dual graded graph on level ``n``.
|
|
4123
|
+
|
|
4124
|
+
EXAMPLES::
|
|
4125
|
+
|
|
4126
|
+
sage: Domino = GrowthDiagram.rules.Domino()
|
|
4127
|
+
sage: Domino.vertices(2)
|
|
4128
|
+
[[4], [3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]]
|
|
4129
|
+
"""
|
|
4130
|
+
return [la for la in Partitions(2*n) if len(la.core(2)) == 0]
|
|
4131
|
+
|
|
4132
|
+
def rank(self, v):
|
|
4133
|
+
r"""
|
|
4134
|
+
Return the rank of ``v``.
|
|
4135
|
+
|
|
4136
|
+
The rank of a vertex is half the size of the partition,
|
|
4137
|
+
which equals the number of dominoes in any filling.
|
|
4138
|
+
|
|
4139
|
+
EXAMPLES::
|
|
4140
|
+
|
|
4141
|
+
sage: Domino = GrowthDiagram.rules.Domino()
|
|
4142
|
+
sage: Domino.rank(Domino.vertices(3)[0])
|
|
4143
|
+
3
|
|
4144
|
+
"""
|
|
4145
|
+
return v.size() // 2
|
|
4146
|
+
|
|
4147
|
+
def is_P_edge(self, v, w):
|
|
4148
|
+
r"""
|
|
4149
|
+
Return whether ``(v, w)`` is a `P`-edge of ``self``.
|
|
4150
|
+
|
|
4151
|
+
``(v, w)`` is an edge if ``v`` is obtained from ``w`` by deleting
|
|
4152
|
+
a domino.
|
|
4153
|
+
|
|
4154
|
+
EXAMPLES::
|
|
4155
|
+
|
|
4156
|
+
sage: Domino = GrowthDiagram.rules.Domino()
|
|
4157
|
+
sage: v = Domino.vertices(2)[1]; ascii_art(v)
|
|
4158
|
+
***
|
|
4159
|
+
*
|
|
4160
|
+
sage: ascii_art([w for w in Domino.vertices(3) if Domino.is_P_edge(v, w)])
|
|
4161
|
+
[ *** ]
|
|
4162
|
+
[ * ]
|
|
4163
|
+
[ ***** *** * ]
|
|
4164
|
+
[ * , ***, * ]
|
|
4165
|
+
sage: [w for w in Domino.vertices(4) if Domino.is_P_edge(v, w)]
|
|
4166
|
+
[]
|
|
4167
|
+
"""
|
|
4168
|
+
try:
|
|
4169
|
+
(row_1, col_1), (row_2, col_2) = SkewPartition([w, v]).cells()
|
|
4170
|
+
except ValueError:
|
|
4171
|
+
return False
|
|
4172
|
+
return row_1 == row_2 or col_1 == col_2
|
|
4173
|
+
|
|
4174
|
+
is_Q_edge = is_P_edge
|
|
4175
|
+
|
|
4176
|
+
def P_symbol(self, P_chain):
|
|
4177
|
+
r"""
|
|
4178
|
+
Return the labels along the vertical boundary of a rectangular
|
|
4179
|
+
growth diagram as a (skew) domino tableau.
|
|
4180
|
+
|
|
4181
|
+
EXAMPLES::
|
|
4182
|
+
|
|
4183
|
+
sage: Domino = GrowthDiagram.rules.Domino()
|
|
4184
|
+
sage: G = Domino([[0,1,0],[0,0,-1],[1,0,0]])
|
|
4185
|
+
sage: G.P_symbol().pp()
|
|
4186
|
+
1 1
|
|
4187
|
+
2 3
|
|
4188
|
+
2 3
|
|
4189
|
+
"""
|
|
4190
|
+
return SkewTableau(chain=P_chain)
|
|
4191
|
+
|
|
4192
|
+
Q_symbol = P_symbol
|
|
4193
|
+
|
|
4194
|
+
def forward_rule(self, y, t, x, content):
|
|
4195
|
+
r"""
|
|
4196
|
+
Return the output shape given three shapes and the content.
|
|
4197
|
+
|
|
4198
|
+
See [Lam2004]_ Section 3.1.
|
|
4199
|
+
|
|
4200
|
+
INPUT:
|
|
4201
|
+
|
|
4202
|
+
- ``y``, ``t``, ``x`` -- three partitions from a cell in a
|
|
4203
|
+
growth diagram, labelled as::
|
|
4204
|
+
|
|
4205
|
+
t x
|
|
4206
|
+
y
|
|
4207
|
+
|
|
4208
|
+
- ``content`` -- `-1`, `0` or `1`; the content of the cell
|
|
4209
|
+
|
|
4210
|
+
OUTPUT: the fourth partition according to domino insertion
|
|
4211
|
+
|
|
4212
|
+
EXAMPLES::
|
|
4213
|
+
|
|
4214
|
+
sage: Domino = GrowthDiagram.rules.Domino()
|
|
4215
|
+
|
|
4216
|
+
Rule 1::
|
|
4217
|
+
|
|
4218
|
+
sage: Domino.forward_rule([], [], [], 1)
|
|
4219
|
+
[2]
|
|
4220
|
+
|
|
4221
|
+
sage: Domino.forward_rule([1,1], [1,1], [1,1], 1)
|
|
4222
|
+
[3, 1]
|
|
4223
|
+
|
|
4224
|
+
Rule 2::
|
|
4225
|
+
|
|
4226
|
+
sage: Domino.forward_rule([1,1], [1,1], [1,1], -1)
|
|
4227
|
+
[1, 1, 1, 1]
|
|
4228
|
+
|
|
4229
|
+
Rule 3::
|
|
4230
|
+
|
|
4231
|
+
sage: Domino.forward_rule([1,1], [1,1], [2,2], 0)
|
|
4232
|
+
[2, 2]
|
|
4233
|
+
|
|
4234
|
+
Rule 4::
|
|
4235
|
+
|
|
4236
|
+
sage: Domino.forward_rule([2,2,2], [2,2], [3,3], 0)
|
|
4237
|
+
[3, 3, 2]
|
|
4238
|
+
|
|
4239
|
+
sage: Domino.forward_rule([2], [], [1,1], 0)
|
|
4240
|
+
[2, 2]
|
|
4241
|
+
|
|
4242
|
+
sage: Domino.forward_rule([1,1], [], [2], 0)
|
|
4243
|
+
[2, 2]
|
|
4244
|
+
|
|
4245
|
+
sage: Domino.forward_rule([2], [], [2], 0)
|
|
4246
|
+
[2, 2]
|
|
4247
|
+
|
|
4248
|
+
sage: Domino.forward_rule([4], [2], [4], 0)
|
|
4249
|
+
[4, 2]
|
|
4250
|
+
|
|
4251
|
+
sage: Domino.forward_rule([1,1,1,1], [1,1], [1,1,1,1], 0)
|
|
4252
|
+
[2, 2, 1, 1]
|
|
4253
|
+
|
|
4254
|
+
sage: Domino.forward_rule([2,1,1], [2], [4], 0)
|
|
4255
|
+
[4, 1, 1]
|
|
4256
|
+
"""
|
|
4257
|
+
def union(la, mu):
|
|
4258
|
+
r"""
|
|
4259
|
+
Return the union of the two partitions.
|
|
4260
|
+
"""
|
|
4261
|
+
return [max(p,q) for (p,q) in zip_longest(la, mu, fillvalue=0)]
|
|
4262
|
+
|
|
4263
|
+
if content not in [0,1,-1]:
|
|
4264
|
+
raise ValueError("domino: the content of the filling must be in {-1,0,1}")
|
|
4265
|
+
|
|
4266
|
+
if content == 1:
|
|
4267
|
+
if not (x == t == y):
|
|
4268
|
+
raise ValueError("all shapes must be equal")
|
|
4269
|
+
if t == []:
|
|
4270
|
+
z = [2]
|
|
4271
|
+
else:
|
|
4272
|
+
z = [t[0] + 2] + t[1:]
|
|
4273
|
+
|
|
4274
|
+
elif content == -1:
|
|
4275
|
+
if not (x == t == y):
|
|
4276
|
+
raise ValueError("all shapes must be equal")
|
|
4277
|
+
z = t + [1,1]
|
|
4278
|
+
|
|
4279
|
+
elif content == 0 and (t == x or t == y):
|
|
4280
|
+
z = union(x, y)
|
|
4281
|
+
|
|
4282
|
+
else:
|
|
4283
|
+
# content == 0 and t differs from x and y by
|
|
4284
|
+
# domino's gamma1 and gamma3
|
|
4285
|
+
|
|
4286
|
+
# the following is certainly very slow
|
|
4287
|
+
gamma3 = set(SkewPartition([y, t]).cells())
|
|
4288
|
+
gamma1 = set(SkewPartition([x, t]).cells())
|
|
4289
|
+
diff = gamma1.intersection(gamma3)
|
|
4290
|
+
cell1, cell2 = gamma3
|
|
4291
|
+
if len(diff) == 0:
|
|
4292
|
+
z = union(x, y)
|
|
4293
|
+
|
|
4294
|
+
elif len(diff) == 1:
|
|
4295
|
+
z = copy(x)
|
|
4296
|
+
# diff is a single cell
|
|
4297
|
+
(k,l) = diff.pop()
|
|
4298
|
+
# add (k+1, l+1) to x
|
|
4299
|
+
# either (k, l+1) or (k+1, l) must also be added
|
|
4300
|
+
if z[k] <= l + 1:
|
|
4301
|
+
z[k] += 1
|
|
4302
|
+
z[k+1] += 1
|
|
4303
|
+
else:
|
|
4304
|
+
if len(z) <= k + 1:
|
|
4305
|
+
z += [2]
|
|
4306
|
+
else:
|
|
4307
|
+
z[k+1] += 2
|
|
4308
|
+
|
|
4309
|
+
# diff has size 2, that is x == y
|
|
4310
|
+
elif cell1[0] == cell2[0]:
|
|
4311
|
+
z = copy(x)
|
|
4312
|
+
# a horizontal domino - add 2 to row below of gamma
|
|
4313
|
+
if len(z) <= cell1[0] + 1:
|
|
4314
|
+
z += [2]
|
|
4315
|
+
else:
|
|
4316
|
+
z[cell1[0]+1] += 2
|
|
4317
|
+
|
|
4318
|
+
else:
|
|
4319
|
+
z = copy(x)
|
|
4320
|
+
# a vertical domino - add 2 to column right of gamma
|
|
4321
|
+
# find first row shorter than cell1[1]+1
|
|
4322
|
+
for r, p in enumerate(z):
|
|
4323
|
+
if p <= cell1[1] + 1:
|
|
4324
|
+
z[r] += 1
|
|
4325
|
+
z[r+1] += 1
|
|
4326
|
+
break
|
|
4327
|
+
else:
|
|
4328
|
+
raise NotImplementedError("domino: cannot call forward rule with shapes %s and content %s"
|
|
4329
|
+
% ((y, t, x), content))
|
|
4330
|
+
|
|
4331
|
+
return z
|
|
4332
|
+
|
|
4333
|
+
#####################################################################
|
|
4334
|
+
## Set the rules available from GrowthDiagram.rules.<tab>
|
|
4335
|
+
#####################################################################
|
|
4336
|
+
|
|
4337
|
+
|
|
4338
|
+
class Rules:
|
|
4339
|
+
"""
|
|
4340
|
+
Catalog of rules for growth diagrams.
|
|
4341
|
+
"""
|
|
4342
|
+
ShiftedShapes = RuleShiftedShapes
|
|
4343
|
+
LLMS = RuleLLMS
|
|
4344
|
+
BinaryWord = RuleBinaryWord
|
|
4345
|
+
Sylvester = RuleSylvester
|
|
4346
|
+
YoungFibonacci = RuleYoungFibonacci
|
|
4347
|
+
RSK = RuleRSK
|
|
4348
|
+
Burge = RuleBurge
|
|
4349
|
+
Domino = RuleDomino
|
|
4350
|
+
|
|
4351
|
+
|
|
4352
|
+
GrowthDiagram.rules = Rules
|