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
|
@@ -0,0 +1,3318 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-combinat
|
|
2
|
+
r"""
|
|
3
|
+
Set partitions
|
|
4
|
+
|
|
5
|
+
This module defines a class for immutable partitioning of a set. For
|
|
6
|
+
mutable version see :func:`DisjointSet`.
|
|
7
|
+
|
|
8
|
+
AUTHORS:
|
|
9
|
+
|
|
10
|
+
- Mike Hansen
|
|
11
|
+
- MuPAD-Combinat developers (for algorithms and design inspiration).
|
|
12
|
+
- Travis Scrimshaw (2013-02-28): Removed ``CombinatorialClass`` and added
|
|
13
|
+
entry point through :class:`SetPartition`.
|
|
14
|
+
- Martin Rubey (2017-10-10): Cleanup, add crossings and nestings, add
|
|
15
|
+
random generation.
|
|
16
|
+
"""
|
|
17
|
+
# ****************************************************************************
|
|
18
|
+
# Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>,
|
|
19
|
+
#
|
|
20
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
21
|
+
#
|
|
22
|
+
# This code is distributed in the hope that it will be useful,
|
|
23
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
24
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
25
|
+
# General Public License for more details.
|
|
26
|
+
#
|
|
27
|
+
# The full text of the GPL is available at:
|
|
28
|
+
#
|
|
29
|
+
# https://www.gnu.org/licenses/
|
|
30
|
+
# ****************************************************************************
|
|
31
|
+
import itertools
|
|
32
|
+
from itertools import repeat
|
|
33
|
+
from sage.sets.set import Set, Set_generic
|
|
34
|
+
|
|
35
|
+
from sage.structure.parent import Parent
|
|
36
|
+
from sage.structure.unique_representation import UniqueRepresentation
|
|
37
|
+
from sage.structure.list_clone import ClonableArray
|
|
38
|
+
from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets
|
|
39
|
+
from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
|
|
40
|
+
from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass
|
|
41
|
+
from sage.misc.lazy_import import lazy_import
|
|
42
|
+
from sage.rings.infinity import infinity
|
|
43
|
+
from sage.rings.integer import Integer
|
|
44
|
+
from sage.combinat.combinatorial_map import combinatorial_map
|
|
45
|
+
from sage.combinat.set_partition_iterator import (set_partition_iterator,
|
|
46
|
+
set_partition_iterator_blocks)
|
|
47
|
+
from sage.combinat.partition import Partition, Partitions
|
|
48
|
+
from sage.combinat.combinat import bell_number, stirling_number2 as stirling2
|
|
49
|
+
from sage.combinat.permutation import Permutation
|
|
50
|
+
from sage.arith.misc import factorial
|
|
51
|
+
from sage.misc.prandom import random, randint, sample
|
|
52
|
+
from sage.sets.disjoint_set import DisjointSet
|
|
53
|
+
|
|
54
|
+
lazy_import('sage.combinat.posets.hasse_diagram', 'HasseDiagram')
|
|
55
|
+
lazy_import('sage.probability.probability_distribution', 'GeneralDiscreteDistribution')
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class AbstractSetPartition(ClonableArray,
|
|
59
|
+
metaclass=InheritComparisonClasscallMetaclass):
|
|
60
|
+
r"""
|
|
61
|
+
Methods of set partitions which are independent of the base set
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
def _repr_(self):
|
|
65
|
+
"""
|
|
66
|
+
Return a string representation of ``self``.
|
|
67
|
+
|
|
68
|
+
EXAMPLES::
|
|
69
|
+
|
|
70
|
+
sage: S = SetPartitions(4)
|
|
71
|
+
sage: S([[1,3],[2,4]])
|
|
72
|
+
{{1, 3}, {2, 4}}
|
|
73
|
+
"""
|
|
74
|
+
try:
|
|
75
|
+
s = [sorted(x) for x in self]
|
|
76
|
+
except TypeError:
|
|
77
|
+
s = [sorted(x, key=str) for x in self]
|
|
78
|
+
return '{' + ', '.join('{' + repr(x)[1:-1] + '}' for x in s) + '}'
|
|
79
|
+
|
|
80
|
+
def __hash__(self):
|
|
81
|
+
"""
|
|
82
|
+
Return the hash of ``self``.
|
|
83
|
+
|
|
84
|
+
The parent is not included as part of the hash.
|
|
85
|
+
|
|
86
|
+
EXAMPLES::
|
|
87
|
+
|
|
88
|
+
sage: P = SetPartitions(4)
|
|
89
|
+
sage: A = SetPartition([[1], [2,3], [4]])
|
|
90
|
+
sage: B = P([[1], [2,3], [4]])
|
|
91
|
+
sage: hash(A) == hash(B)
|
|
92
|
+
True
|
|
93
|
+
"""
|
|
94
|
+
return sum(hash(x) for x in self)
|
|
95
|
+
|
|
96
|
+
def __eq__(self, y):
|
|
97
|
+
"""
|
|
98
|
+
Check equality of ``self`` and ``y``.
|
|
99
|
+
|
|
100
|
+
The parent is not included as part of the equality check.
|
|
101
|
+
|
|
102
|
+
EXAMPLES::
|
|
103
|
+
|
|
104
|
+
sage: P = SetPartitions(4)
|
|
105
|
+
sage: A = SetPartition([[1], [2,3], [4]])
|
|
106
|
+
sage: B = P([[1], [2,3], [4]])
|
|
107
|
+
sage: A == B
|
|
108
|
+
True
|
|
109
|
+
sage: C = P([[2, 3], [1], [4]])
|
|
110
|
+
sage: A == C
|
|
111
|
+
True
|
|
112
|
+
sage: D = P([[1], [2, 4], [3]])
|
|
113
|
+
sage: A == D
|
|
114
|
+
False
|
|
115
|
+
|
|
116
|
+
Note that this may give incorrect answers if the base set is not totally ordered::
|
|
117
|
+
|
|
118
|
+
sage: a,b = frozenset([0,1]), frozenset([2,3])
|
|
119
|
+
sage: p1 = SetPartition([[a], [b]])
|
|
120
|
+
sage: p2 = SetPartition([[b], [a]])
|
|
121
|
+
sage: p1 == p2
|
|
122
|
+
False
|
|
123
|
+
"""
|
|
124
|
+
if not isinstance(y, AbstractSetPartition):
|
|
125
|
+
return False
|
|
126
|
+
return list(self) == list(y)
|
|
127
|
+
|
|
128
|
+
def __ne__(self, y):
|
|
129
|
+
"""
|
|
130
|
+
Check lack of equality of ``self`` and ``y``.
|
|
131
|
+
|
|
132
|
+
The parent is not included as part of the equality check.
|
|
133
|
+
|
|
134
|
+
EXAMPLES::
|
|
135
|
+
|
|
136
|
+
sage: P = SetPartitions(4)
|
|
137
|
+
sage: A = SetPartition([[1], [2,3], [4]])
|
|
138
|
+
sage: B = P([[1], [2,3], [4]])
|
|
139
|
+
sage: A != B
|
|
140
|
+
False
|
|
141
|
+
sage: C = P([[2, 3], [1], [4]])
|
|
142
|
+
sage: A != C
|
|
143
|
+
False
|
|
144
|
+
sage: D = P([[1], [2, 4], [3]])
|
|
145
|
+
sage: A != D
|
|
146
|
+
True
|
|
147
|
+
|
|
148
|
+
Note that this may give incorrect answers if the base set is not totally ordered::
|
|
149
|
+
|
|
150
|
+
sage: a,b = frozenset([0,1]), frozenset([2,3])
|
|
151
|
+
sage: p1 = SetPartition([[a], [b]])
|
|
152
|
+
sage: p2 = SetPartition([[b], [a]])
|
|
153
|
+
sage: p1 != p2
|
|
154
|
+
True
|
|
155
|
+
"""
|
|
156
|
+
return not (self == y)
|
|
157
|
+
|
|
158
|
+
def __lt__(self, y):
|
|
159
|
+
"""
|
|
160
|
+
Check that ``self`` is less than ``y``.
|
|
161
|
+
|
|
162
|
+
The ordering used is lexicographic, where:
|
|
163
|
+
|
|
164
|
+
- a set partition is considered as the list of its parts
|
|
165
|
+
sorted by increasing smallest element;
|
|
166
|
+
|
|
167
|
+
- each part is regarded as a list of its elements, sorted
|
|
168
|
+
in increasing order;
|
|
169
|
+
|
|
170
|
+
- the parts themselves are compared lexicographically.
|
|
171
|
+
|
|
172
|
+
EXAMPLES::
|
|
173
|
+
|
|
174
|
+
sage: P = SetPartitions(4)
|
|
175
|
+
sage: A = P([[1], [2,3], [4]])
|
|
176
|
+
sage: B = SetPartition([[1,2,3], [4]])
|
|
177
|
+
sage: A < B
|
|
178
|
+
True
|
|
179
|
+
sage: C = P([[1,2,4], [3]])
|
|
180
|
+
sage: B < C
|
|
181
|
+
True
|
|
182
|
+
sage: B < B
|
|
183
|
+
False
|
|
184
|
+
sage: D = P([[1,4], [2], [3]])
|
|
185
|
+
sage: E = P([[1,4], [2,3]])
|
|
186
|
+
sage: D < E
|
|
187
|
+
True
|
|
188
|
+
sage: F = P([[1,2,4], [3]])
|
|
189
|
+
sage: E < C
|
|
190
|
+
False
|
|
191
|
+
sage: A < E
|
|
192
|
+
True
|
|
193
|
+
sage: A < C
|
|
194
|
+
True
|
|
195
|
+
"""
|
|
196
|
+
if not isinstance(y, AbstractSetPartition):
|
|
197
|
+
return False
|
|
198
|
+
return [sorted(i) for i in self] < [sorted(i) for i in y]
|
|
199
|
+
|
|
200
|
+
def __gt__(self, y):
|
|
201
|
+
"""
|
|
202
|
+
Check that ``self`` is greater than ``y``.
|
|
203
|
+
|
|
204
|
+
The ordering used is lexicographic, where:
|
|
205
|
+
|
|
206
|
+
- a set partition is considered as the list of its parts
|
|
207
|
+
sorted by increasing smallest element;
|
|
208
|
+
|
|
209
|
+
- each part is regarded as a list of its elements, sorted
|
|
210
|
+
in increasing order;
|
|
211
|
+
|
|
212
|
+
- the parts themselves are compared lexicographically.
|
|
213
|
+
|
|
214
|
+
EXAMPLES::
|
|
215
|
+
|
|
216
|
+
sage: P = SetPartitions(4)
|
|
217
|
+
sage: A = P([[1], [2,3], [4]])
|
|
218
|
+
sage: B = SetPartition([[1,2,3], [4]])
|
|
219
|
+
sage: B > A
|
|
220
|
+
True
|
|
221
|
+
sage: A > B
|
|
222
|
+
False
|
|
223
|
+
"""
|
|
224
|
+
if not isinstance(y, AbstractSetPartition):
|
|
225
|
+
return False
|
|
226
|
+
return [sorted(i) for i in self] > [sorted(i) for i in y]
|
|
227
|
+
|
|
228
|
+
def __le__(self, y):
|
|
229
|
+
"""
|
|
230
|
+
Check that ``self`` is less than or equals ``y``.
|
|
231
|
+
|
|
232
|
+
The ordering used is lexicographic, where:
|
|
233
|
+
|
|
234
|
+
- a set partition is considered as the list of its parts
|
|
235
|
+
sorted by increasing smallest element;
|
|
236
|
+
|
|
237
|
+
- each part is regarded as a list of its elements, sorted
|
|
238
|
+
in increasing order;
|
|
239
|
+
|
|
240
|
+
- the parts themselves are compared lexicographically.
|
|
241
|
+
|
|
242
|
+
EXAMPLES::
|
|
243
|
+
|
|
244
|
+
sage: P = SetPartitions(4)
|
|
245
|
+
sage: A = P([[1], [2,3], [4]])
|
|
246
|
+
sage: B = SetPartition([[1,2,3], [4]])
|
|
247
|
+
sage: A <= B
|
|
248
|
+
True
|
|
249
|
+
sage: A <= A
|
|
250
|
+
True
|
|
251
|
+
"""
|
|
252
|
+
return self == y or self < y
|
|
253
|
+
|
|
254
|
+
def __ge__(self, y):
|
|
255
|
+
"""
|
|
256
|
+
Check that ``self`` is greater than or equals ``y``.
|
|
257
|
+
|
|
258
|
+
The ordering used is lexicographic, where:
|
|
259
|
+
|
|
260
|
+
- a set partition is considered as the list of its parts
|
|
261
|
+
sorted by increasing smallest element;
|
|
262
|
+
|
|
263
|
+
- each part is regarded as a list of its elements, sorted
|
|
264
|
+
in increasing order;
|
|
265
|
+
|
|
266
|
+
- the parts themselves are compared lexicographically.
|
|
267
|
+
|
|
268
|
+
EXAMPLES::
|
|
269
|
+
|
|
270
|
+
sage: P = SetPartitions(4)
|
|
271
|
+
sage: A = P([[1], [2,3], [4]])
|
|
272
|
+
sage: B = SetPartition([[1,2,3], [4]])
|
|
273
|
+
sage: B >= A
|
|
274
|
+
True
|
|
275
|
+
sage: B >= B
|
|
276
|
+
True
|
|
277
|
+
"""
|
|
278
|
+
return self == y or self > y
|
|
279
|
+
|
|
280
|
+
def __mul__(self, other):
|
|
281
|
+
r"""
|
|
282
|
+
The product of the set partitions ``self`` and ``other``.
|
|
283
|
+
|
|
284
|
+
The product of two set partitions `B` and `C` is defined as the
|
|
285
|
+
set partition whose parts are the nonempty intersections between
|
|
286
|
+
each part of `B` and each part of `C`. This product is also
|
|
287
|
+
the infimum of `B` and `C` in the classical set partition
|
|
288
|
+
lattice (that is, the coarsest set partition which is finer than
|
|
289
|
+
each of `B` and `C`). Consequently, ``inf`` acts as an alias for
|
|
290
|
+
this method.
|
|
291
|
+
|
|
292
|
+
.. SEEALSO::
|
|
293
|
+
|
|
294
|
+
:meth:`sup`
|
|
295
|
+
|
|
296
|
+
EXAMPLES::
|
|
297
|
+
|
|
298
|
+
sage: x = SetPartition([ [1,2], [3,5,4] ])
|
|
299
|
+
sage: y = SetPartition(( (3,1,2), (5,4) ))
|
|
300
|
+
sage: x * y
|
|
301
|
+
{{1, 2}, {3}, {4, 5}}
|
|
302
|
+
|
|
303
|
+
sage: S = SetPartitions(4)
|
|
304
|
+
sage: sp1 = S([[2,3,4], [1]])
|
|
305
|
+
sage: sp2 = S([[1,3], [2,4]])
|
|
306
|
+
sage: s = S([[2,4], [3], [1]])
|
|
307
|
+
sage: sp1.inf(sp2) == s
|
|
308
|
+
True
|
|
309
|
+
|
|
310
|
+
TESTS:
|
|
311
|
+
|
|
312
|
+
Here is a different implementation of the ``__mul__`` method
|
|
313
|
+
(one that was formerly used for the ``inf`` method, before it
|
|
314
|
+
was realized that the methods do the same thing)::
|
|
315
|
+
|
|
316
|
+
sage: def mul2(s, t):
|
|
317
|
+
....: temp = [ss.intersection(ts) for ss in s for ts in t]
|
|
318
|
+
....: temp = filter(bool, temp)
|
|
319
|
+
....: return s.__class__(s.parent(), temp)
|
|
320
|
+
|
|
321
|
+
Let us check that this gives the same as ``__mul__`` on set
|
|
322
|
+
partitions of `\{1, 2, 3, 4\}`::
|
|
323
|
+
|
|
324
|
+
sage: all( all( mul2(s, t) == s * t for s in SetPartitions(4) )
|
|
325
|
+
....: for t in SetPartitions(4) )
|
|
326
|
+
True
|
|
327
|
+
"""
|
|
328
|
+
new_composition = []
|
|
329
|
+
for B in self:
|
|
330
|
+
for C in other:
|
|
331
|
+
BintC = B.intersection(C)
|
|
332
|
+
if BintC:
|
|
333
|
+
new_composition.append(BintC)
|
|
334
|
+
return SetPartition(new_composition)
|
|
335
|
+
|
|
336
|
+
inf = __mul__
|
|
337
|
+
|
|
338
|
+
def sup(self, t):
|
|
339
|
+
"""
|
|
340
|
+
Return the supremum of ``self`` and ``t`` in the classical set
|
|
341
|
+
partition lattice.
|
|
342
|
+
|
|
343
|
+
The supremum of two set partitions `B` and `C` is obtained as the
|
|
344
|
+
transitive closure of the relation which relates `i` to `j` if
|
|
345
|
+
and only if `i` and `j` are in the same part in at least
|
|
346
|
+
one of the set partitions `B` and `C`.
|
|
347
|
+
|
|
348
|
+
.. SEEALSO::
|
|
349
|
+
|
|
350
|
+
:meth:`__mul__`
|
|
351
|
+
|
|
352
|
+
EXAMPLES::
|
|
353
|
+
|
|
354
|
+
sage: S = SetPartitions(4)
|
|
355
|
+
sage: sp1 = S([[2,3,4], [1]])
|
|
356
|
+
sage: sp2 = S([[1,3], [2,4]])
|
|
357
|
+
sage: s = S([[1,2,3,4]])
|
|
358
|
+
sage: sp1.sup(sp2) == s
|
|
359
|
+
True
|
|
360
|
+
"""
|
|
361
|
+
res = list(self)
|
|
362
|
+
for p in t:
|
|
363
|
+
# find blocks in res which intersect p
|
|
364
|
+
inters = [(i, q) for i, q in enumerate(res)
|
|
365
|
+
if any(a in q for a in p)]
|
|
366
|
+
# remove these blocks from res
|
|
367
|
+
for i, _ in reversed(inters):
|
|
368
|
+
del res[i]
|
|
369
|
+
# add the union
|
|
370
|
+
res.append([e for _, q in inters for e in q])
|
|
371
|
+
return self.parent()(res)
|
|
372
|
+
|
|
373
|
+
def standard_form(self):
|
|
374
|
+
r"""
|
|
375
|
+
Return ``self`` as a list of lists.
|
|
376
|
+
|
|
377
|
+
When the ground set is totally ordered, the elements of each
|
|
378
|
+
block are listed in increasing order.
|
|
379
|
+
|
|
380
|
+
This is not related to standard set partitions (which simply
|
|
381
|
+
means set partitions of `[n] = \{ 1, 2, \ldots , n \}` for some
|
|
382
|
+
integer `n`) or standardization (:meth:`standardization`).
|
|
383
|
+
|
|
384
|
+
EXAMPLES::
|
|
385
|
+
|
|
386
|
+
sage: [x.standard_form() for x in SetPartitions(4, [2,2])] # needs sage.graphs sage.rings.finite_rings
|
|
387
|
+
[[[1, 2], [3, 4]], [[1, 4], [2, 3]], [[1, 3], [2, 4]]]
|
|
388
|
+
|
|
389
|
+
TESTS::
|
|
390
|
+
|
|
391
|
+
sage: SetPartition([(1, 9, 8), (2, 3, 4, 5, 6, 7)]).standard_form()
|
|
392
|
+
[[1, 8, 9], [2, 3, 4, 5, 6, 7]]
|
|
393
|
+
"""
|
|
394
|
+
return [sorted(i) for i in self]
|
|
395
|
+
|
|
396
|
+
def base_set(self):
|
|
397
|
+
"""
|
|
398
|
+
Return the base set of ``self``, which is the union of all parts
|
|
399
|
+
of ``self``.
|
|
400
|
+
|
|
401
|
+
EXAMPLES::
|
|
402
|
+
|
|
403
|
+
sage: SetPartition([[1], [2,3], [4]]).base_set()
|
|
404
|
+
{1, 2, 3, 4}
|
|
405
|
+
sage: SetPartition([[1,2,3,4]]).base_set()
|
|
406
|
+
{1, 2, 3, 4}
|
|
407
|
+
sage: SetPartition([]).base_set()
|
|
408
|
+
{}
|
|
409
|
+
"""
|
|
410
|
+
return Set(e for p in self for e in p)
|
|
411
|
+
|
|
412
|
+
def base_set_cardinality(self):
|
|
413
|
+
"""
|
|
414
|
+
Return the cardinality of the base set of ``self``, which is the sum
|
|
415
|
+
of the sizes of the parts of ``self``.
|
|
416
|
+
|
|
417
|
+
This is also known as the *size* (sometimes the *weight*) of
|
|
418
|
+
a set partition.
|
|
419
|
+
|
|
420
|
+
EXAMPLES::
|
|
421
|
+
|
|
422
|
+
sage: SetPartition([[1], [2,3], [4]]).base_set_cardinality()
|
|
423
|
+
4
|
|
424
|
+
sage: SetPartition([[1,2,3,4]]).base_set_cardinality()
|
|
425
|
+
4
|
|
426
|
+
"""
|
|
427
|
+
return sum(len(x) for x in self)
|
|
428
|
+
|
|
429
|
+
def coarsenings(self):
|
|
430
|
+
"""
|
|
431
|
+
Return a list of coarsenings of ``self``.
|
|
432
|
+
|
|
433
|
+
.. SEEALSO::
|
|
434
|
+
|
|
435
|
+
:meth:`refinements`
|
|
436
|
+
|
|
437
|
+
EXAMPLES::
|
|
438
|
+
|
|
439
|
+
sage: SetPartition([[1,3],[2,4]]).coarsenings()
|
|
440
|
+
[{{1, 2, 3, 4}}, {{1, 3}, {2, 4}}]
|
|
441
|
+
sage: SetPartition([[1],[2,4],[3]]).coarsenings()
|
|
442
|
+
[{{1, 2, 3, 4}},
|
|
443
|
+
{{1, 2, 4}, {3}},
|
|
444
|
+
{{1, 3}, {2, 4}},
|
|
445
|
+
{{1}, {2, 3, 4}},
|
|
446
|
+
{{1}, {2, 4}, {3}}]
|
|
447
|
+
sage: SetPartition([]).coarsenings()
|
|
448
|
+
[{}]
|
|
449
|
+
"""
|
|
450
|
+
SP = SetPartitions(len(self))
|
|
451
|
+
|
|
452
|
+
def union(s):
|
|
453
|
+
# Return the partition obtained by combining, for every
|
|
454
|
+
# part of s, those parts of self which are indexed by
|
|
455
|
+
# the elements of this part of s into a single part.
|
|
456
|
+
ret = []
|
|
457
|
+
for part in s:
|
|
458
|
+
cur = []
|
|
459
|
+
for i in part:
|
|
460
|
+
cur.extend(self[i - 1]) # -1 for indexing
|
|
461
|
+
ret.append(cur)
|
|
462
|
+
return ret
|
|
463
|
+
return [self.parent()(union(s)) for s in SP]
|
|
464
|
+
|
|
465
|
+
def max_block_size(self):
|
|
466
|
+
r"""
|
|
467
|
+
The maximum block size of the diagram.
|
|
468
|
+
|
|
469
|
+
EXAMPLES::
|
|
470
|
+
|
|
471
|
+
sage: # needs sage.modules
|
|
472
|
+
sage: from sage.combinat.diagram_algebras import PartitionDiagram, PartitionDiagrams
|
|
473
|
+
sage: pd = PartitionDiagram([[1,-3,-5],[2,4],[3,-1,-2],[5],[-4]])
|
|
474
|
+
sage: pd.max_block_size()
|
|
475
|
+
3
|
|
476
|
+
sage: sorted(d.max_block_size() for d in PartitionDiagrams(2))
|
|
477
|
+
[1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4]
|
|
478
|
+
sage: sorted(sp.max_block_size() for sp in SetPartitions(3))
|
|
479
|
+
[1, 2, 2, 2, 3]
|
|
480
|
+
"""
|
|
481
|
+
return max(len(block) for block in self)
|
|
482
|
+
|
|
483
|
+
def conjugate(self):
|
|
484
|
+
r"""
|
|
485
|
+
An involution exchanging singletons and circular adjacencies.
|
|
486
|
+
|
|
487
|
+
This method implements the definition of the conjugate of
|
|
488
|
+
a set partition defined in [Cal2005]_.
|
|
489
|
+
|
|
490
|
+
INPUT:
|
|
491
|
+
|
|
492
|
+
- ``self`` -- set partition of an ordered set
|
|
493
|
+
|
|
494
|
+
OUTPUT: a set partition
|
|
495
|
+
|
|
496
|
+
EXAMPLES::
|
|
497
|
+
|
|
498
|
+
sage: SetPartition([[1,6,7],[2,8],[3,4,5]]).conjugate()
|
|
499
|
+
{{1, 4, 7}, {2, 8}, {3}, {5}, {6}}
|
|
500
|
+
sage: all(sp.conjugate().conjugate()==sp for sp in SetPartitions([1,3,5,7]))
|
|
501
|
+
True
|
|
502
|
+
sage: SetPartition([]).conjugate()
|
|
503
|
+
{}
|
|
504
|
+
"""
|
|
505
|
+
def next_one(a, support):
|
|
506
|
+
return support[(support.index(a) + 1) % len(support)]
|
|
507
|
+
|
|
508
|
+
def addback(S, terminals, rsupport):
|
|
509
|
+
out = list(S)
|
|
510
|
+
for a in terminals * 2:
|
|
511
|
+
if a not in out and next_one(a, rsupport) in out:
|
|
512
|
+
out.append(a)
|
|
513
|
+
return out
|
|
514
|
+
|
|
515
|
+
def pre_conjugate(sp):
|
|
516
|
+
if len(sp) <= 1:
|
|
517
|
+
return SetPartition([[a] for S in sp for a in S])
|
|
518
|
+
if sp.max_block_size() == 1:
|
|
519
|
+
return SetPartition([sp.base_set()])
|
|
520
|
+
support = sorted(a for S in sp for a in S)
|
|
521
|
+
initials = [a for S in sp for a in S if next_one(a, support) in S]
|
|
522
|
+
singletons = [a for S in sp for a in S if len(S) == 1]
|
|
523
|
+
if not initials and not singletons:
|
|
524
|
+
return sp
|
|
525
|
+
rho = pre_conjugate(
|
|
526
|
+
SetPartition([[a for a in S if a not in initials]
|
|
527
|
+
for S in sp if len(S) > 1 and any(a not in initials for a in S)]))
|
|
528
|
+
# add back initials as singletons and singletons as terminals
|
|
529
|
+
return SetPartition([addback(S, singletons, support[::-1])
|
|
530
|
+
for S in rho] + [[a] for a in initials])
|
|
531
|
+
support = sorted(a for S in self for a in S)
|
|
532
|
+
return SetPartition([[support[-support.index(a) - 1] for a in S]
|
|
533
|
+
for S in pre_conjugate(self)])
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
class SetPartition(AbstractSetPartition,
|
|
537
|
+
metaclass=InheritComparisonClasscallMetaclass):
|
|
538
|
+
r"""
|
|
539
|
+
A partition of a set.
|
|
540
|
+
|
|
541
|
+
A set partition `p` of a set `S` is a partition of `S` into subsets
|
|
542
|
+
called parts and represented as a set of sets. By extension, a set
|
|
543
|
+
partition of a nonnegative integer `n` is the set partition of the
|
|
544
|
+
integers from 1 to `n`. The number of set partitions of `n` is called
|
|
545
|
+
the `n`-th Bell number.
|
|
546
|
+
|
|
547
|
+
There is a natural integer partition associated with a set partition,
|
|
548
|
+
namely the nonincreasing sequence of sizes of all its parts.
|
|
549
|
+
|
|
550
|
+
There is a classical lattice associated with all set partitions of
|
|
551
|
+
`n`. The infimum of two set partitions is the set partition obtained
|
|
552
|
+
by intersecting all the parts of both set partitions. The supremum
|
|
553
|
+
is obtained by transitive closure of the relation `i` related to `j`
|
|
554
|
+
if and only if they are in the same part in at least one of the set
|
|
555
|
+
partitions.
|
|
556
|
+
|
|
557
|
+
We will use terminology from partitions, in particular the *length* of
|
|
558
|
+
a set partition `A = \{A_1, \ldots, A_k\}` is the number of parts of `A`
|
|
559
|
+
and is denoted by `|A| := k`. The *size* of `A` is the cardinality of `S`.
|
|
560
|
+
We will also sometimes use the notation `[n] := \{1, 2, \ldots, n\}`.
|
|
561
|
+
|
|
562
|
+
EXAMPLES:
|
|
563
|
+
|
|
564
|
+
There are 5 set partitions of the set `\{1,2,3\}`::
|
|
565
|
+
|
|
566
|
+
sage: SetPartitions(3).cardinality() # needs sage.libs.flint
|
|
567
|
+
5
|
|
568
|
+
|
|
569
|
+
Here is the list of them::
|
|
570
|
+
|
|
571
|
+
sage: SetPartitions(3).list() # needs sage.graphs
|
|
572
|
+
[{{1, 2, 3}}, {{1, 2}, {3}}, {{1, 3}, {2}}, {{1}, {2, 3}}, {{1}, {2}, {3}}]
|
|
573
|
+
|
|
574
|
+
There are 6 set partitions of `\{1,2,3,4\}` whose underlying partition is
|
|
575
|
+
`[2, 1, 1]`::
|
|
576
|
+
|
|
577
|
+
sage: SetPartitions(4, [2,1,1]).list() # needs sage.graphs sage.rings.finite_rings
|
|
578
|
+
[{{1}, {2, 4}, {3}},
|
|
579
|
+
{{1}, {2}, {3, 4}},
|
|
580
|
+
{{1, 4}, {2}, {3}},
|
|
581
|
+
{{1, 3}, {2}, {4}},
|
|
582
|
+
{{1, 2}, {3}, {4}},
|
|
583
|
+
{{1}, {2, 3}, {4}}]
|
|
584
|
+
|
|
585
|
+
Since :issue:`14140`, we can create a set partition directly by
|
|
586
|
+
:class:`SetPartition`, which creates the base set by taking the
|
|
587
|
+
union of the parts passed in::
|
|
588
|
+
|
|
589
|
+
sage: s = SetPartition([[1,3],[2,4]]); s
|
|
590
|
+
{{1, 3}, {2, 4}}
|
|
591
|
+
sage: s.parent()
|
|
592
|
+
Set partitions
|
|
593
|
+
"""
|
|
594
|
+
@staticmethod
|
|
595
|
+
def __classcall_private__(cls, parts, check=True):
|
|
596
|
+
"""
|
|
597
|
+
Create a set partition from ``parts`` with the appropriate parent.
|
|
598
|
+
|
|
599
|
+
EXAMPLES::
|
|
600
|
+
|
|
601
|
+
sage: s = SetPartition([[1,3],[2,4]]); s
|
|
602
|
+
{{1, 3}, {2, 4}}
|
|
603
|
+
sage: s.parent()
|
|
604
|
+
Set partitions
|
|
605
|
+
"""
|
|
606
|
+
P = SetPartitions()
|
|
607
|
+
return P.element_class(P, parts, check=check)
|
|
608
|
+
|
|
609
|
+
def __init__(self, parent, s, check=True):
|
|
610
|
+
"""
|
|
611
|
+
Initialize ``self``.
|
|
612
|
+
|
|
613
|
+
Internally, a set partition is stored as iterable of blocks,
|
|
614
|
+
sorted by minimal element.
|
|
615
|
+
|
|
616
|
+
EXAMPLES::
|
|
617
|
+
|
|
618
|
+
sage: S = SetPartitions(4)
|
|
619
|
+
sage: s = S([[1,3],[2,4]])
|
|
620
|
+
sage: TestSuite(s).run()
|
|
621
|
+
sage: SetPartition([])
|
|
622
|
+
{}
|
|
623
|
+
"""
|
|
624
|
+
self._latex_options = {}
|
|
625
|
+
try:
|
|
626
|
+
s = sorted(map(frozenset, s), key=min)
|
|
627
|
+
except TypeError:
|
|
628
|
+
s = sorted(map(frozenset, s), key=lambda b: min(str(b)))
|
|
629
|
+
ClonableArray.__init__(self, parent, s, check=check)
|
|
630
|
+
|
|
631
|
+
def check(self):
|
|
632
|
+
"""
|
|
633
|
+
Check that we are a valid set partition.
|
|
634
|
+
|
|
635
|
+
EXAMPLES::
|
|
636
|
+
|
|
637
|
+
sage: S = SetPartitions(4)
|
|
638
|
+
sage: s = S([[1, 3], [2, 4]])
|
|
639
|
+
sage: s.check()
|
|
640
|
+
|
|
641
|
+
TESTS::
|
|
642
|
+
|
|
643
|
+
sage: s = S([[1, 2, 3]], check=False)
|
|
644
|
+
sage: s.check()
|
|
645
|
+
Traceback (most recent call last):
|
|
646
|
+
...
|
|
647
|
+
ValueError: {{1, 2, 3}} is not an element of Set partitions of {1, 2, 3, 4}
|
|
648
|
+
|
|
649
|
+
sage: s = S([1, 2, 3])
|
|
650
|
+
Traceback (most recent call last):
|
|
651
|
+
...
|
|
652
|
+
TypeError: 'sage.rings.integer.Integer' object is not iterable
|
|
653
|
+
"""
|
|
654
|
+
if self not in self.parent():
|
|
655
|
+
raise ValueError(f"{self} is not an element of {self.parent()}")
|
|
656
|
+
|
|
657
|
+
def set_latex_options(self, **kwargs):
|
|
658
|
+
r"""
|
|
659
|
+
Set the latex options for use in the ``_latex_`` function.
|
|
660
|
+
|
|
661
|
+
- ``tikz_scale`` -- (default: 1) scale for use with tikz package
|
|
662
|
+
|
|
663
|
+
- ``plot`` -- (default: ``None``) ``None`` returns the set notation,
|
|
664
|
+
``linear`` returns a linear plot, ``cyclic`` returns a cyclic
|
|
665
|
+
plot
|
|
666
|
+
|
|
667
|
+
- ``color`` -- (default: ``'black'``) the arc colors
|
|
668
|
+
|
|
669
|
+
- ``fill`` -- boolean (default: ``False``); if ``True`` then fills
|
|
670
|
+
``color``, else you can pass in a color to alter the fill color -
|
|
671
|
+
*only works with cyclic plot*
|
|
672
|
+
|
|
673
|
+
- ``show_labels`` -- boolean (default: ``True``); if ``True`` shows
|
|
674
|
+
labels (*only works with plots*)
|
|
675
|
+
|
|
676
|
+
- ``radius`` -- (default: ``'1cm'``) radius of circle for cyclic
|
|
677
|
+
plot - *only works with cyclic plot*
|
|
678
|
+
|
|
679
|
+
- ``angle`` -- (default: 0) angle for linear plot
|
|
680
|
+
|
|
681
|
+
EXAMPLES::
|
|
682
|
+
|
|
683
|
+
sage: SP = SetPartition([[1,6], [3,5,4]])
|
|
684
|
+
sage: SP.set_latex_options(tikz_scale=2,plot='linear',fill=True,color='blue',angle=45)
|
|
685
|
+
sage: SP.set_latex_options(plot='cyclic')
|
|
686
|
+
sage: SP.latex_options()
|
|
687
|
+
{'angle': 45,
|
|
688
|
+
'color': 'blue',
|
|
689
|
+
'fill': True,
|
|
690
|
+
'plot': 'cyclic',
|
|
691
|
+
'radius': '1cm',
|
|
692
|
+
'show_labels': True,
|
|
693
|
+
'tikz_scale': 2}
|
|
694
|
+
"""
|
|
695
|
+
valid_args = ['tikz_scale', 'plot', 'color', 'fill', 'show_labels',
|
|
696
|
+
'radius', 'angle']
|
|
697
|
+
|
|
698
|
+
for key in kwargs:
|
|
699
|
+
if key not in valid_args:
|
|
700
|
+
raise ValueError(f"unknown keyword argument: {key}")
|
|
701
|
+
if key == 'plot':
|
|
702
|
+
if not (kwargs['plot'] == 'cyclic'
|
|
703
|
+
or kwargs['plot'] == 'linear'
|
|
704
|
+
or kwargs['plot'] is None):
|
|
705
|
+
raise ValueError("plot must be None, 'cyclic', or 'linear'")
|
|
706
|
+
|
|
707
|
+
self._latex_options.update(kwargs)
|
|
708
|
+
|
|
709
|
+
def latex_options(self):
|
|
710
|
+
r"""
|
|
711
|
+
Return the latex options for use in the ``_latex_`` function as a
|
|
712
|
+
dictionary. The default values are set using the global options.
|
|
713
|
+
|
|
714
|
+
Options can be found in :meth:`set_latex_options`
|
|
715
|
+
|
|
716
|
+
EXAMPLES::
|
|
717
|
+
|
|
718
|
+
sage: SP = SetPartition([[1,6], [3,5,4]]); SP.latex_options()
|
|
719
|
+
{'angle': 0,
|
|
720
|
+
'color': 'black',
|
|
721
|
+
'fill': False,
|
|
722
|
+
'plot': None,
|
|
723
|
+
'radius': '1cm',
|
|
724
|
+
'show_labels': True,
|
|
725
|
+
'tikz_scale': 1}
|
|
726
|
+
"""
|
|
727
|
+
opts = self._latex_options.copy()
|
|
728
|
+
if "tikz_scale" not in opts:
|
|
729
|
+
opts["tikz_scale"] = 1
|
|
730
|
+
if "plot" not in opts:
|
|
731
|
+
opts["plot"] = None
|
|
732
|
+
if "color" not in opts:
|
|
733
|
+
opts['color'] = 'black'
|
|
734
|
+
if "fill" not in opts:
|
|
735
|
+
opts["fill"] = False
|
|
736
|
+
if "show_labels" not in opts:
|
|
737
|
+
opts['show_labels'] = True
|
|
738
|
+
if "radius" not in opts:
|
|
739
|
+
opts['radius'] = "1cm"
|
|
740
|
+
if "angle" not in opts:
|
|
741
|
+
opts['angle'] = 0
|
|
742
|
+
return opts
|
|
743
|
+
|
|
744
|
+
def _latex_(self):
|
|
745
|
+
r"""
|
|
746
|
+
Return a `\LaTeX` string representation of ``self``.
|
|
747
|
+
|
|
748
|
+
EXAMPLES::
|
|
749
|
+
|
|
750
|
+
sage: x = SetPartition([[1,2], [3,5,4]])
|
|
751
|
+
sage: latex(x)
|
|
752
|
+
\{\{1, 2\}, \{3, 4, 5\}\}
|
|
753
|
+
|
|
754
|
+
sage: x.set_latex_options(plot='linear', angle=25, color='red')
|
|
755
|
+
sage: latex(x)
|
|
756
|
+
\begin{tikzpicture}[scale=1]
|
|
757
|
+
\node[below=.05cm] at (0,0) {$1$};
|
|
758
|
+
\node[draw,circle, inner sep=0pt, minimum width=4pt, fill=black] (0) at (0,0) {};
|
|
759
|
+
\node[below=.05cm] at (1,0) {$2$};
|
|
760
|
+
\node[draw,circle, inner sep=0pt, minimum width=4pt, fill=black] (1) at (1,0) {};
|
|
761
|
+
\node[below=.05cm] at (2,0) {$3$};
|
|
762
|
+
\node[draw,circle, inner sep=0pt, minimum width=4pt, fill=black] (2) at (2,0) {};
|
|
763
|
+
\node[below=.05cm] at (3,0) {$4$};
|
|
764
|
+
\node[draw,circle, inner sep=0pt, minimum width=4pt, fill=black] (3) at (3,0) {};
|
|
765
|
+
\node[below=.05cm] at (4,0) {$5$};
|
|
766
|
+
\node[draw,circle, inner sep=0pt, minimum width=4pt, fill=black] (4) at (4,0) {};
|
|
767
|
+
\draw[color=red] (1) to [out=115,in=65] (0);
|
|
768
|
+
\draw[color=red] (3) to [out=115,in=65] (2);
|
|
769
|
+
\draw[color=red] (4) to [out=115,in=65] (3);
|
|
770
|
+
\end{tikzpicture}
|
|
771
|
+
|
|
772
|
+
sage: p = SetPartition([['a','c'],['b','d'],['e']])
|
|
773
|
+
sage: p.set_latex_options(plot='cyclic', color='blue', fill=True, tikz_scale=2)
|
|
774
|
+
sage: latex(p)
|
|
775
|
+
\begin{tikzpicture}[scale=2]
|
|
776
|
+
\draw (0,0) circle [radius=1cm];
|
|
777
|
+
\node[label=90:a] (0) at (90:1cm) {};
|
|
778
|
+
\node[label=18:b] (1) at (18:1cm) {};
|
|
779
|
+
\node[label=-54:c] (2) at (-54:1cm) {};
|
|
780
|
+
\node[label=-126:d] (3) at (-126:1cm) {};
|
|
781
|
+
\node[label=-198:e] (4) at (-198:1cm) {};
|
|
782
|
+
\draw[-,thick,color=blue,fill=blue,fill opacity=0.1] ...
|
|
783
|
+
\draw[-,thick,color=blue,fill=blue,fill opacity=0.1] ...
|
|
784
|
+
\draw[-,thick,color=blue,fill=blue,fill opacity=0.1] ...
|
|
785
|
+
\fill[color=black] (0) circle (1.5pt);
|
|
786
|
+
\fill[color=black] (1) circle (1.5pt);
|
|
787
|
+
\fill[color=black] (2) circle (1.5pt);
|
|
788
|
+
\fill[color=black] (3) circle (1.5pt);
|
|
789
|
+
\fill[color=black] (4) circle (1.5pt);
|
|
790
|
+
\end{tikzpicture}
|
|
791
|
+
"""
|
|
792
|
+
latex_options = self.latex_options()
|
|
793
|
+
if latex_options["plot"] is None:
|
|
794
|
+
return repr(self).replace("{", r"\{").replace("}", r"\}")
|
|
795
|
+
|
|
796
|
+
from sage.misc.latex import latex
|
|
797
|
+
latex.add_package_to_preamble_if_available("tikz")
|
|
798
|
+
res = "\\begin{{tikzpicture}}[scale={}]\n".format(latex_options['tikz_scale'])
|
|
799
|
+
|
|
800
|
+
cardinality = self.base_set_cardinality()
|
|
801
|
+
from sage.rings.integer_ring import ZZ
|
|
802
|
+
if all(x in ZZ for x in self.base_set()):
|
|
803
|
+
sort_key = ZZ
|
|
804
|
+
else:
|
|
805
|
+
sort_key = str
|
|
806
|
+
base_set = sorted(self.base_set(), key=sort_key)
|
|
807
|
+
color = latex_options['color']
|
|
808
|
+
|
|
809
|
+
# If we want cyclic plots
|
|
810
|
+
if latex_options['plot'] == 'cyclic':
|
|
811
|
+
degrees = 360 // cardinality
|
|
812
|
+
radius = latex_options['radius']
|
|
813
|
+
|
|
814
|
+
res += "\\draw (0,0) circle [radius={}];\n".format(radius)
|
|
815
|
+
|
|
816
|
+
# Add nodes
|
|
817
|
+
for k, i in enumerate(base_set):
|
|
818
|
+
location = (cardinality - k) * degrees - 270
|
|
819
|
+
if latex_options['show_labels']:
|
|
820
|
+
res += "\\node[label={}:{}]".format(location, i)
|
|
821
|
+
else:
|
|
822
|
+
res += "\\node"
|
|
823
|
+
res += " ({}) at ({}:{}) {{}};\n".format(k, location, radius)
|
|
824
|
+
|
|
825
|
+
# Setup partitions
|
|
826
|
+
for partition in sorted(self, key=str):
|
|
827
|
+
res += "\\draw[-,thick,color=" + color
|
|
828
|
+
if latex_options['fill'] is not False:
|
|
829
|
+
if isinstance(latex_options['fill'], str):
|
|
830
|
+
res += ",fill=" + latex_options['fill']
|
|
831
|
+
else:
|
|
832
|
+
res += ",fill={},fill opacity=0.1".format(color)
|
|
833
|
+
res += "] "
|
|
834
|
+
res += " -- ".join("({}.center)".format(base_set.index(j))
|
|
835
|
+
for j in sorted(partition, key=sort_key))
|
|
836
|
+
res += " -- cycle;\n"
|
|
837
|
+
|
|
838
|
+
# Draw the circles on top
|
|
839
|
+
for k in range(len(base_set)):
|
|
840
|
+
res += "\\fill[color=black] ({}) circle (1.5pt);\n".format(k)
|
|
841
|
+
|
|
842
|
+
# If we want line plots
|
|
843
|
+
elif latex_options['plot'] == 'linear':
|
|
844
|
+
angle = latex_options['angle']
|
|
845
|
+
# setup line
|
|
846
|
+
for k, i in enumerate(base_set):
|
|
847
|
+
if latex_options['show_labels']:
|
|
848
|
+
res += "\\node[below=.05cm] at ({},0) {{${}$}};\n".format(k, i)
|
|
849
|
+
res += "\\node[draw,circle, inner sep=0pt, minimum width=4pt, fill=black] "
|
|
850
|
+
res += "({k}) at ({k},0) {{}};\n".format(k=k)
|
|
851
|
+
|
|
852
|
+
# setup arcs
|
|
853
|
+
for partition in sorted(self, key=str):
|
|
854
|
+
p = sorted(partition, key=sort_key)
|
|
855
|
+
if len(p) <= 1:
|
|
856
|
+
continue
|
|
857
|
+
for k in range(1, len(p)):
|
|
858
|
+
res += "\\draw[color={}] ({})".format(color, base_set.index(p[k]))
|
|
859
|
+
res += " to [out={},in={}] ".format(90 + angle, 90 - angle)
|
|
860
|
+
res += "({});\n".format(base_set.index(p[k - 1]))
|
|
861
|
+
else:
|
|
862
|
+
raise ValueError("plot must be None, 'cyclic', or 'linear'")
|
|
863
|
+
|
|
864
|
+
res += "\\end{tikzpicture}"
|
|
865
|
+
return res
|
|
866
|
+
|
|
867
|
+
cardinality = ClonableArray.__len__
|
|
868
|
+
|
|
869
|
+
size = AbstractSetPartition.base_set_cardinality
|
|
870
|
+
|
|
871
|
+
def pipe(self, other):
|
|
872
|
+
r"""
|
|
873
|
+
Return the pipe of the set partitions ``self`` and ``other``.
|
|
874
|
+
|
|
875
|
+
The pipe of two set partitions is defined as follows:
|
|
876
|
+
|
|
877
|
+
For any integer `k` and any subset `I` of `\ZZ`, let `I + k`
|
|
878
|
+
denote the subset of `\ZZ` obtained by adding `k` to every
|
|
879
|
+
element of `k`.
|
|
880
|
+
|
|
881
|
+
If `B` and `C` are set partitions of `[n]` and `[m]`,
|
|
882
|
+
respectively, then the pipe of `B` and `C` is defined as the
|
|
883
|
+
set partition
|
|
884
|
+
|
|
885
|
+
.. MATH::
|
|
886
|
+
|
|
887
|
+
\{ B_1, B_2, \ldots, B_b,
|
|
888
|
+
C_1 + n, C_2 + n, \ldots, C_c + n \}
|
|
889
|
+
|
|
890
|
+
of `[n+m]`, where `B = \{ B_1, B_2, \ldots, B_b \}` and
|
|
891
|
+
`C = \{ C_1, C_2, \ldots, C_c \}`. This pipe is denoted by
|
|
892
|
+
`B | C`.
|
|
893
|
+
|
|
894
|
+
EXAMPLES::
|
|
895
|
+
|
|
896
|
+
sage: SetPartition([[1,3],[2,4]]).pipe(SetPartition([[1,3],[2]]))
|
|
897
|
+
{{1, 3}, {2, 4}, {5, 7}, {6}}
|
|
898
|
+
sage: SetPartition([]).pipe(SetPartition([[1,2],[3,5],[4]]))
|
|
899
|
+
{{1, 2}, {3, 5}, {4}}
|
|
900
|
+
sage: SetPartition([[1,2],[3,5],[4]]).pipe(SetPartition([]))
|
|
901
|
+
{{1, 2}, {3, 5}, {4}}
|
|
902
|
+
sage: SetPartition([[1,2],[3]]).pipe(SetPartition([[1]]))
|
|
903
|
+
{{1, 2}, {3}, {4}}
|
|
904
|
+
"""
|
|
905
|
+
# Note: GIGO if self and other are not standard.
|
|
906
|
+
parts = list(self)
|
|
907
|
+
n = self.base_set_cardinality()
|
|
908
|
+
for newpart in other:
|
|
909
|
+
raised_newpart = Set(i + n for i in newpart)
|
|
910
|
+
parts.append(raised_newpart)
|
|
911
|
+
return SetPartition(parts)
|
|
912
|
+
|
|
913
|
+
@combinatorial_map(name='shape')
|
|
914
|
+
def shape(self):
|
|
915
|
+
r"""
|
|
916
|
+
Return the integer partition whose parts are the sizes of the sets
|
|
917
|
+
in ``self``.
|
|
918
|
+
|
|
919
|
+
EXAMPLES::
|
|
920
|
+
|
|
921
|
+
sage: S = SetPartitions(5)
|
|
922
|
+
sage: x = S([[1,2], [3,5,4]])
|
|
923
|
+
sage: x.shape()
|
|
924
|
+
[3, 2]
|
|
925
|
+
sage: y = S([[2], [3,1], [5,4]])
|
|
926
|
+
sage: y.shape()
|
|
927
|
+
[2, 2, 1]
|
|
928
|
+
"""
|
|
929
|
+
return Partition(sorted(map(len, self), reverse=True))
|
|
930
|
+
|
|
931
|
+
# we define aliases for shape()
|
|
932
|
+
shape_partition = shape
|
|
933
|
+
to_partition = shape
|
|
934
|
+
|
|
935
|
+
@combinatorial_map(name='to permutation')
|
|
936
|
+
def to_permutation(self):
|
|
937
|
+
r"""
|
|
938
|
+
Convert a set partition of `\{1,...,n\}` to a permutation by considering
|
|
939
|
+
the blocks of the partition as cycles.
|
|
940
|
+
|
|
941
|
+
The cycles are such that the number of excedences is maximised, that is,
|
|
942
|
+
each cycle is of the form `(a_1,a_2, ...,a_k)` with `a_1<a_2<...<a_k`.
|
|
943
|
+
|
|
944
|
+
EXAMPLES::
|
|
945
|
+
|
|
946
|
+
sage: s = SetPartition([[1,3],[2,4]])
|
|
947
|
+
sage: s.to_permutation()
|
|
948
|
+
[3, 4, 1, 2]
|
|
949
|
+
"""
|
|
950
|
+
return Permutation(tuple(map(tuple, self.standard_form())))
|
|
951
|
+
|
|
952
|
+
def to_restricted_growth_word(self, bijection='blocks'):
|
|
953
|
+
r"""
|
|
954
|
+
Convert a set partition of `\{1,...,n\}` to a word of length `n`
|
|
955
|
+
with letters in the nonnegative integers such that each
|
|
956
|
+
letter is at most 1 larger than all the letters before.
|
|
957
|
+
|
|
958
|
+
INPUT:
|
|
959
|
+
|
|
960
|
+
- ``bijection`` -- (default: ``blocks``) defines the map from
|
|
961
|
+
set partitions to restricted growth functions. These are
|
|
962
|
+
currently:
|
|
963
|
+
|
|
964
|
+
- ``blocks``: :meth:`to_restricted_growth_word_blocks`.
|
|
965
|
+
|
|
966
|
+
- ``intertwining``: :meth:`to_restricted_growth_word_intertwining`.
|
|
967
|
+
|
|
968
|
+
OUTPUT: a restricted growth word
|
|
969
|
+
|
|
970
|
+
.. SEEALSO::
|
|
971
|
+
|
|
972
|
+
:meth:`SetPartitions.from_restricted_growth_word`
|
|
973
|
+
|
|
974
|
+
EXAMPLES::
|
|
975
|
+
|
|
976
|
+
sage: P = SetPartition([[1,4],[2,8],[3,5,6,9],[7]])
|
|
977
|
+
sage: P.to_restricted_growth_word()
|
|
978
|
+
[0, 1, 2, 0, 2, 2, 3, 1, 2]
|
|
979
|
+
|
|
980
|
+
sage: P.to_restricted_growth_word("intertwining")
|
|
981
|
+
[0, 1, 2, 2, 1, 0, 3, 3, 2]
|
|
982
|
+
|
|
983
|
+
sage: P = SetPartition([[1,2,4,7],[3,9],[5,6,10,11,13],[8],[12]])
|
|
984
|
+
sage: P.to_restricted_growth_word()
|
|
985
|
+
[0, 0, 1, 0, 2, 2, 0, 3, 1, 2, 2, 4, 2]
|
|
986
|
+
|
|
987
|
+
sage: P.to_restricted_growth_word("intertwining")
|
|
988
|
+
[0, 0, 1, 1, 2, 0, 1, 3, 3, 3, 0, 4, 1]
|
|
989
|
+
|
|
990
|
+
TESTS::
|
|
991
|
+
|
|
992
|
+
sage: P = SetPartition([])
|
|
993
|
+
sage: P.to_restricted_growth_word()
|
|
994
|
+
[]
|
|
995
|
+
sage: P.to_restricted_growth_word("intertwining")
|
|
996
|
+
[]
|
|
997
|
+
sage: S = SetPartitions(5, 2)
|
|
998
|
+
sage: all(S.from_restricted_growth_word(P.to_restricted_growth_word()) == P for P in S)
|
|
999
|
+
True
|
|
1000
|
+
|
|
1001
|
+
sage: S = SetPartitions(5, 2)
|
|
1002
|
+
sage: all(S.from_restricted_growth_word(P.to_restricted_growth_word("intertwining"), "intertwining") == P for P in S)
|
|
1003
|
+
True
|
|
1004
|
+
"""
|
|
1005
|
+
if bijection == "blocks":
|
|
1006
|
+
return self.to_restricted_growth_word_blocks()
|
|
1007
|
+
if bijection == "intertwining":
|
|
1008
|
+
return self.to_restricted_growth_word_intertwining()
|
|
1009
|
+
raise ValueError("the given bijection is not valid")
|
|
1010
|
+
|
|
1011
|
+
def to_restricted_growth_word_blocks(self):
|
|
1012
|
+
r"""
|
|
1013
|
+
Convert a set partition of `\{1,...,n\}` to a word of length `n`
|
|
1014
|
+
with letters in the nonnegative integers such that each
|
|
1015
|
+
letter is at most 1 larger than all the letters before.
|
|
1016
|
+
|
|
1017
|
+
The word is obtained by sorting the blocks by their minimal
|
|
1018
|
+
element and setting the letters at the positions of the
|
|
1019
|
+
elements in the `i`-th block to `i`.
|
|
1020
|
+
|
|
1021
|
+
OUTPUT: a restricted growth word
|
|
1022
|
+
|
|
1023
|
+
.. SEEALSO::
|
|
1024
|
+
|
|
1025
|
+
:meth:`to_restricted_growth_word`
|
|
1026
|
+
:meth:`SetPartitions.from_restricted_growth_word`
|
|
1027
|
+
|
|
1028
|
+
EXAMPLES::
|
|
1029
|
+
|
|
1030
|
+
sage: P = SetPartition([[1,4],[2,8],[3,5,6,9],[7]])
|
|
1031
|
+
sage: P.to_restricted_growth_word_blocks()
|
|
1032
|
+
[0, 1, 2, 0, 2, 2, 3, 1, 2]
|
|
1033
|
+
"""
|
|
1034
|
+
w = [0] * self.size()
|
|
1035
|
+
# we can assume that the blocks are sorted by minimal element
|
|
1036
|
+
for i, B in enumerate(self):
|
|
1037
|
+
for j in B:
|
|
1038
|
+
w[j - 1] = i
|
|
1039
|
+
return w
|
|
1040
|
+
|
|
1041
|
+
def to_restricted_growth_word_intertwining(self):
|
|
1042
|
+
r"""
|
|
1043
|
+
Convert a set partition of `\{1,...,n\}` to a word of length `n`
|
|
1044
|
+
with letters in the nonnegative integers such that each
|
|
1045
|
+
letter is at most 1 larger than all the letters before.
|
|
1046
|
+
|
|
1047
|
+
The `i`-th letter of the word is the numbers of crossings of
|
|
1048
|
+
the arc (or half-arc) in the extended arc diagram ending at
|
|
1049
|
+
`i`, with arcs (or half-arcs) beginning at a smaller element
|
|
1050
|
+
and ending at a larger element.
|
|
1051
|
+
|
|
1052
|
+
OUTPUT: a restricted growth word
|
|
1053
|
+
|
|
1054
|
+
.. SEEALSO::
|
|
1055
|
+
|
|
1056
|
+
:meth:`to_restricted_growth_word`
|
|
1057
|
+
:meth:`SetPartitions.from_restricted_growth_word`
|
|
1058
|
+
|
|
1059
|
+
EXAMPLES::
|
|
1060
|
+
|
|
1061
|
+
sage: P = SetPartition([[1,4],[2,8],[3,5,6,9],[7]])
|
|
1062
|
+
sage: P.to_restricted_growth_word_intertwining()
|
|
1063
|
+
[0, 1, 2, 2, 1, 0, 3, 3, 2]
|
|
1064
|
+
"""
|
|
1065
|
+
A = sorted(self.arcs())
|
|
1066
|
+
O = (min(B) for B in self) # openers
|
|
1067
|
+
C = [max(B) for B in self] # closers
|
|
1068
|
+
I = [0] * self.size()
|
|
1069
|
+
for i in O:
|
|
1070
|
+
I[i - 1] = sum(1 for k, l in A if k < i < l) + sum(1 for k in C if k < i)
|
|
1071
|
+
for i, j in A:
|
|
1072
|
+
I[j - 1] = sum(1 for k, l in A if i < k < j < l) + sum(1 for k in C if i < k < j)
|
|
1073
|
+
return I
|
|
1074
|
+
|
|
1075
|
+
def openers(self):
|
|
1076
|
+
"""
|
|
1077
|
+
Return the minimal elements of the blocks.
|
|
1078
|
+
|
|
1079
|
+
EXAMPLES::
|
|
1080
|
+
|
|
1081
|
+
sage: P = SetPartition([[1,2,4,7],[3,9],[5,6,10,11,13],[8],[12]])
|
|
1082
|
+
sage: P.openers()
|
|
1083
|
+
[1, 3, 5, 8, 12]
|
|
1084
|
+
"""
|
|
1085
|
+
return sorted([min(B) for B in self])
|
|
1086
|
+
|
|
1087
|
+
def closers(self):
|
|
1088
|
+
"""
|
|
1089
|
+
Return the maximal elements of the blocks.
|
|
1090
|
+
|
|
1091
|
+
EXAMPLES::
|
|
1092
|
+
|
|
1093
|
+
sage: P = SetPartition([[1,2,4,7],[3,9],[5,6,10,11,13],[8],[12]])
|
|
1094
|
+
sage: P.closers()
|
|
1095
|
+
[7, 8, 9, 12, 13]
|
|
1096
|
+
"""
|
|
1097
|
+
return sorted([max(B) for B in self])
|
|
1098
|
+
|
|
1099
|
+
def to_rook_placement(self, bijection='arcs'):
|
|
1100
|
+
r"""
|
|
1101
|
+
Return a set of pairs defining a placement of non-attacking rooks
|
|
1102
|
+
on a triangular board.
|
|
1103
|
+
|
|
1104
|
+
The cells of the board corresponding to a set partition of
|
|
1105
|
+
`\{1,...,n\}` are the pairs `(i,j)` with `0 < i < j < n+1`.
|
|
1106
|
+
|
|
1107
|
+
INPUT:
|
|
1108
|
+
|
|
1109
|
+
- ``bijection`` -- (default: ``arcs``) defines the bijection
|
|
1110
|
+
from set partitions to rook placements. These are
|
|
1111
|
+
currently:
|
|
1112
|
+
|
|
1113
|
+
- ``arcs``: :meth:`arcs`
|
|
1114
|
+
- ``gamma``: :meth:`to_rook_placement_gamma`
|
|
1115
|
+
- ``rho``: :meth:`to_rook_placement_rho`
|
|
1116
|
+
- ``psi``: :meth:`to_rook_placement_psi`
|
|
1117
|
+
|
|
1118
|
+
.. SEEALSO::
|
|
1119
|
+
|
|
1120
|
+
:meth:`SetPartitions.from_rook_placement`
|
|
1121
|
+
|
|
1122
|
+
EXAMPLES::
|
|
1123
|
+
|
|
1124
|
+
sage: P = SetPartition([[1,2,4,7],[3,9],[5,6,10,11,13],[8],[12]])
|
|
1125
|
+
sage: P.to_rook_placement()
|
|
1126
|
+
[(1, 2), (2, 4), (4, 7), (3, 9), (5, 6), (6, 10), (10, 11), (11, 13)]
|
|
1127
|
+
sage: P.to_rook_placement("gamma")
|
|
1128
|
+
[(1, 4), (3, 5), (4, 6), (5, 8), (7, 11), (8, 9), (10, 12), (12, 13)]
|
|
1129
|
+
sage: P.to_rook_placement("rho")
|
|
1130
|
+
[(1, 2), (2, 6), (3, 4), (4, 10), (5, 9), (6, 7), (10, 11), (11, 13)]
|
|
1131
|
+
sage: P.to_rook_placement("psi")
|
|
1132
|
+
[(1, 2), (2, 6), (3, 4), (5, 9), (6, 7), (7, 10), (9, 11), (11, 13)]
|
|
1133
|
+
"""
|
|
1134
|
+
if bijection == "arcs":
|
|
1135
|
+
return self.arcs()
|
|
1136
|
+
if bijection == "gamma":
|
|
1137
|
+
return self.to_rook_placement_gamma()
|
|
1138
|
+
if bijection == "rho":
|
|
1139
|
+
return self.to_rook_placement_rho()
|
|
1140
|
+
if bijection == "psi":
|
|
1141
|
+
return self.to_rook_placement_psi()
|
|
1142
|
+
raise ValueError("the given map is not valid")
|
|
1143
|
+
|
|
1144
|
+
def to_rook_placement_gamma(self):
|
|
1145
|
+
"""
|
|
1146
|
+
Return the rook diagram obtained by placing rooks according to
|
|
1147
|
+
Wachs and White's bijection gamma.
|
|
1148
|
+
|
|
1149
|
+
Note that our index convention differs from the convention in
|
|
1150
|
+
[WW1991]_: regarding the rook board as a lower-right
|
|
1151
|
+
triangular grid, we refer with `(i,j)` to the cell in the
|
|
1152
|
+
`i`-th column from the right and the `j`-th row from the top.
|
|
1153
|
+
|
|
1154
|
+
The algorithm proceeds as follows: non-attacking rooks are
|
|
1155
|
+
placed beginning at the left column. If `n+1-i` is an
|
|
1156
|
+
opener, column `i` remains empty. Otherwise, we place a rook
|
|
1157
|
+
into column `i`, such that the number of cells below the
|
|
1158
|
+
rook, which are not yet attacked by another rook, equals the
|
|
1159
|
+
index of the block to which `n+1-i` belongs.
|
|
1160
|
+
|
|
1161
|
+
OUTPUT: list of coordinates
|
|
1162
|
+
|
|
1163
|
+
.. SEEALSO::
|
|
1164
|
+
|
|
1165
|
+
- :meth:`to_rook_placement`
|
|
1166
|
+
- :meth:`SetPartitions.from_rook_placement`
|
|
1167
|
+
- :meth:`SetPartitions.from_rook_placement_gamma`
|
|
1168
|
+
|
|
1169
|
+
EXAMPLES::
|
|
1170
|
+
|
|
1171
|
+
sage: P = SetPartition([[1,4],[2,8],[3,5,6,9],[7]])
|
|
1172
|
+
sage: P.to_rook_placement_gamma()
|
|
1173
|
+
[(1, 3), (2, 7), (4, 5), (5, 6), (6, 9)]
|
|
1174
|
+
|
|
1175
|
+
Figure 5 in [WW1991]_::
|
|
1176
|
+
|
|
1177
|
+
sage: P = SetPartition([[1,2,4,7],[3,9],[5,6,10,11,13],[8],[12]])
|
|
1178
|
+
sage: r = P.to_rook_placement_gamma(); r
|
|
1179
|
+
[(1, 4), (3, 5), (4, 6), (5, 8), (7, 11), (8, 9), (10, 12), (12, 13)]
|
|
1180
|
+
|
|
1181
|
+
TESTS::
|
|
1182
|
+
|
|
1183
|
+
sage: P = SetPartition([])
|
|
1184
|
+
sage: P.to_rook_placement_gamma()
|
|
1185
|
+
[]
|
|
1186
|
+
sage: S = SetPartitions(5, 2)
|
|
1187
|
+
sage: all(S.from_rook_placement(P.to_rook_placement("gamma"), "gamma") == P for P in S)
|
|
1188
|
+
True
|
|
1189
|
+
"""
|
|
1190
|
+
n = self.size()
|
|
1191
|
+
if n == 0:
|
|
1192
|
+
return []
|
|
1193
|
+
w = self.to_restricted_growth_word_blocks()
|
|
1194
|
+
# the set of openers - leftmost occurrences of a letter in w
|
|
1195
|
+
EC = sorted([w.index(i) for i in range(max(w) + 1)])
|
|
1196
|
+
rooks = [] # pairs (row i, column j)
|
|
1197
|
+
R = [] # attacked rows
|
|
1198
|
+
for c in range(n): # columns from left to right
|
|
1199
|
+
if c not in EC:
|
|
1200
|
+
r = 0
|
|
1201
|
+
w_c = w[c]
|
|
1202
|
+
while w_c > 0 or r in R:
|
|
1203
|
+
if r not in R:
|
|
1204
|
+
w_c -= 1
|
|
1205
|
+
r += 1
|
|
1206
|
+
rooks.append((n - c, n - r))
|
|
1207
|
+
R.append(r)
|
|
1208
|
+
return sorted(rooks)
|
|
1209
|
+
|
|
1210
|
+
def to_rook_placement_rho(self):
|
|
1211
|
+
"""
|
|
1212
|
+
Return the rook diagram obtained by placing rooks according to
|
|
1213
|
+
Wachs and White's bijection rho.
|
|
1214
|
+
|
|
1215
|
+
Note that our index convention differs from the convention in
|
|
1216
|
+
[WW1991]_: regarding the rook board as a lower-right
|
|
1217
|
+
triangular grid, we refer with `(i,j)` to the cell in the
|
|
1218
|
+
`i`-th column from the right and the `j`-th row from the top.
|
|
1219
|
+
|
|
1220
|
+
The algorithm proceeds as follows: non-attacking rooks are
|
|
1221
|
+
placed beginning at the top row. The columns corresponding
|
|
1222
|
+
to the closers of the set partition remain empty. Let `rs_j`
|
|
1223
|
+
be the number of closers which are larger than `j` and
|
|
1224
|
+
whose block is before the block of `j`.
|
|
1225
|
+
|
|
1226
|
+
We then place a rook into row `j`, such that the number of
|
|
1227
|
+
cells to the left of the rook, which are not yet attacked by
|
|
1228
|
+
another rook and are not in a column corresponding to a
|
|
1229
|
+
closer, equals `rs_j`, unless there are not enough cells in
|
|
1230
|
+
this row available, in which case the row remains empty.
|
|
1231
|
+
|
|
1232
|
+
One can show that the precisely those rows which correspond
|
|
1233
|
+
to openers of the set partition remain empty.
|
|
1234
|
+
|
|
1235
|
+
OUTPUT: list of coordinates
|
|
1236
|
+
|
|
1237
|
+
.. SEEALSO::
|
|
1238
|
+
|
|
1239
|
+
- :meth:`to_rook_placement`
|
|
1240
|
+
- :meth:`SetPartitions.from_rook_placement`
|
|
1241
|
+
- :meth:`SetPartitions.from_rook_placement_rho`
|
|
1242
|
+
|
|
1243
|
+
EXAMPLES::
|
|
1244
|
+
|
|
1245
|
+
sage: P = SetPartition([[1,4],[2,8],[3,5,6,9],[7]])
|
|
1246
|
+
sage: P.to_rook_placement_rho()
|
|
1247
|
+
[(1, 5), (2, 6), (3, 4), (5, 9), (6, 8)]
|
|
1248
|
+
|
|
1249
|
+
Figure 6 in [WW1991]_::
|
|
1250
|
+
|
|
1251
|
+
sage: P = SetPartition([[1,2,4,7],[3,9],[5,6,10,11,13],[8],[12]])
|
|
1252
|
+
sage: r = P.to_rook_placement_rho(); r
|
|
1253
|
+
[(1, 2), (2, 6), (3, 4), (4, 10), (5, 9), (6, 7), (10, 11), (11, 13)]
|
|
1254
|
+
|
|
1255
|
+
sage: sorted(P.closers() + [i for i, _ in r]) == list(range(1,14))
|
|
1256
|
+
True
|
|
1257
|
+
sage: sorted(P.openers() + [j for _, j in r]) == list(range(1,14))
|
|
1258
|
+
True
|
|
1259
|
+
|
|
1260
|
+
TESTS::
|
|
1261
|
+
|
|
1262
|
+
sage: P = SetPartition([])
|
|
1263
|
+
sage: P.to_rook_placement_rho()
|
|
1264
|
+
[]
|
|
1265
|
+
sage: S = SetPartitions(5, 2)
|
|
1266
|
+
sage: all(S.from_rook_placement(P.to_rook_placement("rho"), "rho") == P for P in S)
|
|
1267
|
+
True
|
|
1268
|
+
"""
|
|
1269
|
+
n = self.size()
|
|
1270
|
+
if n == 0:
|
|
1271
|
+
return []
|
|
1272
|
+
w = self.to_restricted_growth_word_blocks()
|
|
1273
|
+
w_rev = w[::-1]
|
|
1274
|
+
# the set of closers - rightmost occurrences of a letter in w
|
|
1275
|
+
R = sorted([n - w_rev.index(i) - 1 for i in range(max(w) + 1)])
|
|
1276
|
+
# the number of closers which are larger than i and whose
|
|
1277
|
+
# block is before the block of i
|
|
1278
|
+
rs = [sum(1 for j in R if j > i and w[j] < w[i]) for i in range(n)]
|
|
1279
|
+
EC = [n - j for j in R] # empty columns
|
|
1280
|
+
rooks = [] # pairs (row i, column j)
|
|
1281
|
+
for i in range(1, n):
|
|
1282
|
+
U = [j for j in range(n + 1 - i, n + 1) if j not in EC]
|
|
1283
|
+
if rs[i] < len(U):
|
|
1284
|
+
j = U[rs[i]]
|
|
1285
|
+
rooks.append((n + 1 - j, i + 1))
|
|
1286
|
+
EC.append(j)
|
|
1287
|
+
return sorted(rooks)
|
|
1288
|
+
|
|
1289
|
+
def to_rook_placement_psi(self):
|
|
1290
|
+
r"""
|
|
1291
|
+
Return the rook diagram obtained by placing rooks according to
|
|
1292
|
+
Yip's bijection psi.
|
|
1293
|
+
|
|
1294
|
+
OUTPUT: list of coordinates
|
|
1295
|
+
|
|
1296
|
+
.. SEEALSO::
|
|
1297
|
+
|
|
1298
|
+
- :meth:`to_rook_placement`
|
|
1299
|
+
- :meth:`SetPartitions.from_rook_placement`
|
|
1300
|
+
- :meth:`SetPartitions.from_rook_placement_psi`
|
|
1301
|
+
|
|
1302
|
+
EXAMPLES:
|
|
1303
|
+
|
|
1304
|
+
Example 36 (arXiv version: Example 4.5) in [Yip2018]_::
|
|
1305
|
+
|
|
1306
|
+
sage: P = SetPartition([[1, 5], [2], [3, 8, 9], [4], [6, 7]])
|
|
1307
|
+
sage: P.to_rook_placement_psi()
|
|
1308
|
+
[(1, 7), (3, 8), (4, 5), (7, 9)]
|
|
1309
|
+
|
|
1310
|
+
Note that the columns corresponding to the minimal elements
|
|
1311
|
+
of the blocks remain empty.
|
|
1312
|
+
|
|
1313
|
+
TESTS::
|
|
1314
|
+
|
|
1315
|
+
sage: P = SetPartition([])
|
|
1316
|
+
sage: P.to_rook_placement_psi()
|
|
1317
|
+
[]
|
|
1318
|
+
sage: S = SetPartitions(5,2)
|
|
1319
|
+
sage: all(S.from_rook_placement(P.to_rook_placement("psi"), "psi") == P for P in S)
|
|
1320
|
+
True
|
|
1321
|
+
"""
|
|
1322
|
+
# Yip draws the diagram as an upper triangular matrix, thus
|
|
1323
|
+
# we refer to the cell in row i and column j with (i, j)
|
|
1324
|
+
n = self.size()
|
|
1325
|
+
degrees = []
|
|
1326
|
+
P = [sorted(e) for e in self]
|
|
1327
|
+
for j in range(n, 0, -1):
|
|
1328
|
+
# find the block number into which c was placed by first
|
|
1329
|
+
# removing j and then sorting the blocks
|
|
1330
|
+
B = next(B for B in P if B[-1] == j)
|
|
1331
|
+
if len(B) == 1:
|
|
1332
|
+
P.remove(B)
|
|
1333
|
+
else:
|
|
1334
|
+
del B[-1]
|
|
1335
|
+
P = sorted(P, key=lambda B: (-len(B), min(B)))
|
|
1336
|
+
b = P.index(B)
|
|
1337
|
+
i = j - b - 1
|
|
1338
|
+
degrees.append((j, i))
|
|
1339
|
+
# reconstruct rooks from degree sequence
|
|
1340
|
+
rooks = []
|
|
1341
|
+
attacked_rows = []
|
|
1342
|
+
for j, d in reversed(degrees):
|
|
1343
|
+
i = 1
|
|
1344
|
+
while d > i + sum(1 for r in attacked_rows if r > i):
|
|
1345
|
+
i += 1
|
|
1346
|
+
attacked_rows.append(i)
|
|
1347
|
+
rooks.append((i, j))
|
|
1348
|
+
return sorted(rooks)
|
|
1349
|
+
|
|
1350
|
+
def apply_permutation(self, p):
|
|
1351
|
+
r"""
|
|
1352
|
+
Apply ``p`` to the underlying set of ``self``.
|
|
1353
|
+
|
|
1354
|
+
INPUT:
|
|
1355
|
+
|
|
1356
|
+
- ``p`` -- a permutation
|
|
1357
|
+
|
|
1358
|
+
EXAMPLES::
|
|
1359
|
+
|
|
1360
|
+
sage: x = SetPartition([[1,2], [3,5,4]])
|
|
1361
|
+
sage: p = Permutation([2,1,4,5,3])
|
|
1362
|
+
sage: x.apply_permutation(p)
|
|
1363
|
+
{{1, 2}, {3, 4, 5}}
|
|
1364
|
+
sage: q = Permutation([3,2,1,5,4])
|
|
1365
|
+
sage: x.apply_permutation(q)
|
|
1366
|
+
{{1, 4, 5}, {2, 3}}
|
|
1367
|
+
|
|
1368
|
+
sage: m = PerfectMatching([(1,4),(2,6),(3,5)])
|
|
1369
|
+
sage: m.apply_permutation(Permutation([4,1,5,6,3,2]))
|
|
1370
|
+
[(1, 2), (3, 5), (4, 6)]
|
|
1371
|
+
"""
|
|
1372
|
+
return self.__class__(self.parent(), [Set(map(p, B)) for B in self])
|
|
1373
|
+
|
|
1374
|
+
def crossings_iterator(self):
|
|
1375
|
+
r"""
|
|
1376
|
+
Return the crossing arcs of a set partition on a totally ordered set.
|
|
1377
|
+
|
|
1378
|
+
OUTPUT:
|
|
1379
|
+
|
|
1380
|
+
We place the elements of the ground set in order on a
|
|
1381
|
+
line and draw the set partition by linking consecutive
|
|
1382
|
+
elements of each block in the upper half-plane. This
|
|
1383
|
+
function returns an iterator over the pairs of crossing
|
|
1384
|
+
lines (as a line correspond to a pair, the iterator
|
|
1385
|
+
produces pairs of pairs).
|
|
1386
|
+
|
|
1387
|
+
EXAMPLES::
|
|
1388
|
+
|
|
1389
|
+
sage: p = SetPartition([[1,4],[2,5,7],[3,6]])
|
|
1390
|
+
sage: next(p.crossings_iterator())
|
|
1391
|
+
((1, 4), (2, 5))
|
|
1392
|
+
|
|
1393
|
+
TESTS::
|
|
1394
|
+
|
|
1395
|
+
sage: p = SetPartition([]); p.crossings()
|
|
1396
|
+
[]
|
|
1397
|
+
"""
|
|
1398
|
+
# each arc is sorted, but the set of arcs might not be
|
|
1399
|
+
arcs = sorted(self.arcs(), key=min)
|
|
1400
|
+
while arcs:
|
|
1401
|
+
i1, j1 = arcs.pop(0)
|
|
1402
|
+
for i2, j2 in arcs:
|
|
1403
|
+
# we know that i1 < i2 and i1 < j1 and i2 < j2
|
|
1404
|
+
if i2 < j1 < j2:
|
|
1405
|
+
yield ((i1, j1), (i2, j2))
|
|
1406
|
+
|
|
1407
|
+
def crossings(self):
|
|
1408
|
+
r"""
|
|
1409
|
+
Return the crossing arcs of a set partition on a totally ordered set.
|
|
1410
|
+
|
|
1411
|
+
OUTPUT:
|
|
1412
|
+
|
|
1413
|
+
We place the elements of the ground set in order on a
|
|
1414
|
+
line and draw the set partition by linking consecutive
|
|
1415
|
+
elements of each block in the upper half-plane. This
|
|
1416
|
+
function returns a list of the pairs of crossing lines
|
|
1417
|
+
(as a line correspond to a pair, it returns a list of
|
|
1418
|
+
pairs of pairs).
|
|
1419
|
+
|
|
1420
|
+
EXAMPLES::
|
|
1421
|
+
|
|
1422
|
+
sage: p = SetPartition([[1,4],[2,5,7],[3,6]])
|
|
1423
|
+
sage: p.crossings()
|
|
1424
|
+
[((1, 4), (2, 5)), ((1, 4), (3, 6)), ((2, 5), (3, 6)), ((3, 6), (5, 7))]
|
|
1425
|
+
|
|
1426
|
+
TESTS::
|
|
1427
|
+
|
|
1428
|
+
sage: p = SetPartition([]); p.crossings()
|
|
1429
|
+
[]
|
|
1430
|
+
"""
|
|
1431
|
+
return list(self.crossings_iterator())
|
|
1432
|
+
|
|
1433
|
+
def number_of_crossings(self):
|
|
1434
|
+
r"""
|
|
1435
|
+
Return the number of crossings.
|
|
1436
|
+
|
|
1437
|
+
OUTPUT:
|
|
1438
|
+
|
|
1439
|
+
We place the elements of the ground set in order on a
|
|
1440
|
+
line and draw the set partition by linking consecutive
|
|
1441
|
+
elements of each block in the upper half-plane. This
|
|
1442
|
+
function returns the number the pairs of crossing lines.
|
|
1443
|
+
|
|
1444
|
+
EXAMPLES::
|
|
1445
|
+
|
|
1446
|
+
sage: p = SetPartition([[1,4],[2,5,7],[3,6]])
|
|
1447
|
+
sage: p.number_of_crossings()
|
|
1448
|
+
4
|
|
1449
|
+
|
|
1450
|
+
sage: n = PerfectMatching([3,8,1,7,6,5,4,2]); n
|
|
1451
|
+
[(1, 3), (2, 8), (4, 7), (5, 6)]
|
|
1452
|
+
sage: n.number_of_crossings()
|
|
1453
|
+
1
|
|
1454
|
+
"""
|
|
1455
|
+
return Integer(len(list(self.crossings_iterator())))
|
|
1456
|
+
|
|
1457
|
+
def is_noncrossing(self) -> bool:
|
|
1458
|
+
r"""
|
|
1459
|
+
Check if ``self`` is noncrossing.
|
|
1460
|
+
|
|
1461
|
+
OUTPUT:
|
|
1462
|
+
|
|
1463
|
+
We place the elements of the ground set in order on a
|
|
1464
|
+
line and draw the set partition by linking consecutive
|
|
1465
|
+
elements of each block in the upper half-plane. This
|
|
1466
|
+
function returns ``True`` if the picture obtained this
|
|
1467
|
+
way has no crossings.
|
|
1468
|
+
|
|
1469
|
+
EXAMPLES::
|
|
1470
|
+
|
|
1471
|
+
sage: p = SetPartition([[1,4],[2,5,7],[3,6]])
|
|
1472
|
+
sage: p.is_noncrossing()
|
|
1473
|
+
False
|
|
1474
|
+
|
|
1475
|
+
sage: n = PerfectMatching([3,8,1,7,6,5,4,2]); n
|
|
1476
|
+
[(1, 3), (2, 8), (4, 7), (5, 6)]
|
|
1477
|
+
sage: n.is_noncrossing()
|
|
1478
|
+
False
|
|
1479
|
+
sage: PerfectMatching([(1, 4), (2, 3), (5, 6)]).is_noncrossing()
|
|
1480
|
+
True
|
|
1481
|
+
"""
|
|
1482
|
+
it = self.crossings_iterator()
|
|
1483
|
+
try:
|
|
1484
|
+
next(it)
|
|
1485
|
+
except StopIteration:
|
|
1486
|
+
return True
|
|
1487
|
+
return False
|
|
1488
|
+
|
|
1489
|
+
def nestings_iterator(self):
|
|
1490
|
+
r"""
|
|
1491
|
+
Iterate over the nestings of ``self``.
|
|
1492
|
+
|
|
1493
|
+
OUTPUT:
|
|
1494
|
+
|
|
1495
|
+
We place the elements of the ground set in order on a
|
|
1496
|
+
line and draw the set partition by linking consecutive
|
|
1497
|
+
elements of each block in the upper half-plane. This
|
|
1498
|
+
function returns an iterator over the pairs of nesting
|
|
1499
|
+
lines (as a line correspond to a pair, the iterator
|
|
1500
|
+
produces pairs of pairs).
|
|
1501
|
+
|
|
1502
|
+
EXAMPLES::
|
|
1503
|
+
|
|
1504
|
+
sage: n = PerfectMatching([(1, 6), (2, 7), (3, 5), (4, 8)])
|
|
1505
|
+
sage: it = n.nestings_iterator()
|
|
1506
|
+
sage: next(it)
|
|
1507
|
+
((1, 6), (3, 5))
|
|
1508
|
+
sage: next(it)
|
|
1509
|
+
((2, 7), (3, 5))
|
|
1510
|
+
sage: next(it)
|
|
1511
|
+
Traceback (most recent call last):
|
|
1512
|
+
...
|
|
1513
|
+
StopIteration
|
|
1514
|
+
"""
|
|
1515
|
+
# each arc is sorted, but the set of arcs might not be
|
|
1516
|
+
arcs = sorted(self.arcs(), key=min)
|
|
1517
|
+
while arcs:
|
|
1518
|
+
i1, j1 = arcs.pop(0)
|
|
1519
|
+
for i2, j2 in arcs:
|
|
1520
|
+
# we know that i1 < i2 and i1 < j1 and i2 < j2
|
|
1521
|
+
if i2 < j2 < j1:
|
|
1522
|
+
yield ((i1, j1), (i2, j2))
|
|
1523
|
+
|
|
1524
|
+
def nestings(self):
|
|
1525
|
+
r"""
|
|
1526
|
+
Return the nestings of ``self``.
|
|
1527
|
+
|
|
1528
|
+
OUTPUT:
|
|
1529
|
+
|
|
1530
|
+
We place the elements of the ground set in order on a
|
|
1531
|
+
line and draw the set partition by linking consecutive
|
|
1532
|
+
elements of each block in the upper half-plane. This
|
|
1533
|
+
function returns the list of the pairs of nesting lines
|
|
1534
|
+
(as a line correspond to a pair, it returns a list of
|
|
1535
|
+
pairs of pairs).
|
|
1536
|
+
|
|
1537
|
+
EXAMPLES::
|
|
1538
|
+
|
|
1539
|
+
sage: m = PerfectMatching([(1, 6), (2, 7), (3, 5), (4, 8)])
|
|
1540
|
+
sage: m.nestings()
|
|
1541
|
+
[((1, 6), (3, 5)), ((2, 7), (3, 5))]
|
|
1542
|
+
|
|
1543
|
+
sage: n = PerfectMatching([3,8,1,7,6,5,4,2]); n
|
|
1544
|
+
[(1, 3), (2, 8), (4, 7), (5, 6)]
|
|
1545
|
+
sage: n.nestings()
|
|
1546
|
+
[((2, 8), (4, 7)), ((2, 8), (5, 6)), ((4, 7), (5, 6))]
|
|
1547
|
+
|
|
1548
|
+
TESTS::
|
|
1549
|
+
|
|
1550
|
+
sage: m = PerfectMatching([]); m.nestings()
|
|
1551
|
+
[]
|
|
1552
|
+
"""
|
|
1553
|
+
return list(self.nestings_iterator())
|
|
1554
|
+
|
|
1555
|
+
def number_of_nestings(self):
|
|
1556
|
+
r"""
|
|
1557
|
+
Return the number of nestings of ``self``.
|
|
1558
|
+
|
|
1559
|
+
OUTPUT:
|
|
1560
|
+
|
|
1561
|
+
We place the elements of the ground set in order on a
|
|
1562
|
+
line and draw the set partition by linking consecutive
|
|
1563
|
+
elements of each block in the upper half-plane. This
|
|
1564
|
+
function returns the number the pairs of nesting lines.
|
|
1565
|
+
|
|
1566
|
+
EXAMPLES::
|
|
1567
|
+
|
|
1568
|
+
sage: n = PerfectMatching([3,8,1,7,6,5,4,2]); n
|
|
1569
|
+
[(1, 3), (2, 8), (4, 7), (5, 6)]
|
|
1570
|
+
sage: n.number_of_nestings()
|
|
1571
|
+
3
|
|
1572
|
+
"""
|
|
1573
|
+
c = Integer(0)
|
|
1574
|
+
one = Integer(1)
|
|
1575
|
+
for _ in self.nestings_iterator():
|
|
1576
|
+
c += one
|
|
1577
|
+
return c
|
|
1578
|
+
|
|
1579
|
+
def is_nonnesting(self) -> bool:
|
|
1580
|
+
r"""
|
|
1581
|
+
Return if ``self`` is nonnesting or not.
|
|
1582
|
+
|
|
1583
|
+
OUTPUT:
|
|
1584
|
+
|
|
1585
|
+
We place the elements of the ground set in order on a
|
|
1586
|
+
line and draw the set partition by linking consecutive
|
|
1587
|
+
elements of each block in the upper half-plane. This
|
|
1588
|
+
function returns ``True`` if the picture obtained this
|
|
1589
|
+
way has no nestings.
|
|
1590
|
+
|
|
1591
|
+
EXAMPLES::
|
|
1592
|
+
|
|
1593
|
+
sage: n = PerfectMatching([3,8,1,7,6,5,4,2]); n
|
|
1594
|
+
[(1, 3), (2, 8), (4, 7), (5, 6)]
|
|
1595
|
+
sage: n.is_nonnesting()
|
|
1596
|
+
False
|
|
1597
|
+
sage: PerfectMatching([(1, 3), (2, 5), (4, 6)]).is_nonnesting()
|
|
1598
|
+
True
|
|
1599
|
+
"""
|
|
1600
|
+
it = self.nestings_iterator()
|
|
1601
|
+
try:
|
|
1602
|
+
next(it)
|
|
1603
|
+
except StopIteration:
|
|
1604
|
+
return True
|
|
1605
|
+
return False
|
|
1606
|
+
|
|
1607
|
+
def is_atomic(self) -> bool:
|
|
1608
|
+
r"""
|
|
1609
|
+
Return if ``self`` is an atomic set partition.
|
|
1610
|
+
|
|
1611
|
+
A (standard) set partition `A` can be split if there exist `j < i`
|
|
1612
|
+
such that `\max(A_j) < \min(A_i)` where `A` is ordered by minimal
|
|
1613
|
+
elements. This means we can write `A = B | C` for some nonempty set
|
|
1614
|
+
partitions `B` and `C`. We call a set partition *atomic* if it
|
|
1615
|
+
cannot be split and is nonempty. Here, the pipe symbol
|
|
1616
|
+
`|` is as defined in method :meth:`pipe`.
|
|
1617
|
+
|
|
1618
|
+
EXAMPLES::
|
|
1619
|
+
|
|
1620
|
+
sage: SetPartition([[1,3], [2]]).is_atomic()
|
|
1621
|
+
True
|
|
1622
|
+
sage: SetPartition([[1,3], [2], [4]]).is_atomic()
|
|
1623
|
+
False
|
|
1624
|
+
sage: SetPartition([[1], [2,4], [3]]).is_atomic()
|
|
1625
|
+
False
|
|
1626
|
+
sage: SetPartition([[1,2,3,4]]).is_atomic()
|
|
1627
|
+
True
|
|
1628
|
+
sage: SetPartition([[1, 4], [2], [3]]).is_atomic()
|
|
1629
|
+
True
|
|
1630
|
+
sage: SetPartition([]).is_atomic()
|
|
1631
|
+
False
|
|
1632
|
+
"""
|
|
1633
|
+
if len(self) == 0:
|
|
1634
|
+
return False
|
|
1635
|
+
maximum_so_far = max(self[0])
|
|
1636
|
+
for S in self[1:]:
|
|
1637
|
+
if maximum_so_far < min(S):
|
|
1638
|
+
return False
|
|
1639
|
+
maximum_so_far = max(maximum_so_far, *S)
|
|
1640
|
+
return True
|
|
1641
|
+
|
|
1642
|
+
def standardization(self):
|
|
1643
|
+
r"""
|
|
1644
|
+
Return the standardization of ``self``.
|
|
1645
|
+
|
|
1646
|
+
Given a set partition `A = \{A_1, \ldots, A_n\}` of an ordered
|
|
1647
|
+
set `S`, the standardization of `A` is the set partition of
|
|
1648
|
+
`\{1, 2, \ldots, |S|\}` obtained by replacing the elements of
|
|
1649
|
+
the parts of `A` by the integers `1, 2, \ldots, |S|` in such
|
|
1650
|
+
a way that their relative order is preserved (i. e., the
|
|
1651
|
+
smallest element in the whole set partition is replaced by
|
|
1652
|
+
`1`, the next-smallest by `2`, and so on).
|
|
1653
|
+
|
|
1654
|
+
EXAMPLES::
|
|
1655
|
+
|
|
1656
|
+
sage: SetPartition([[4], [1, 3]]).standardization()
|
|
1657
|
+
{{1, 2}, {3}}
|
|
1658
|
+
sage: SetPartition([[4], [6, 3]]).standardization()
|
|
1659
|
+
{{1, 3}, {2}}
|
|
1660
|
+
sage: SetPartition([]).standardization()
|
|
1661
|
+
{}
|
|
1662
|
+
sage: SetPartition([('c','b'),('d','f'),('e','a')]).standardization()
|
|
1663
|
+
{{1, 5}, {2, 3}, {4, 6}}
|
|
1664
|
+
"""
|
|
1665
|
+
r = {e: i for i, e in enumerate(sorted(self.base_set()), 1)}
|
|
1666
|
+
return SetPartitions(len(r))([[r[e] for e in b] for b in self])
|
|
1667
|
+
|
|
1668
|
+
def restriction(self, I):
|
|
1669
|
+
"""
|
|
1670
|
+
Return the restriction of ``self`` to a subset ``I``
|
|
1671
|
+
(which is given as a set or list or any other iterable).
|
|
1672
|
+
|
|
1673
|
+
EXAMPLES::
|
|
1674
|
+
|
|
1675
|
+
sage: A = SetPartition([[1], [2,3]])
|
|
1676
|
+
sage: A.restriction([1,2])
|
|
1677
|
+
{{1}, {2}}
|
|
1678
|
+
sage: A.restriction([2,3])
|
|
1679
|
+
{{2, 3}}
|
|
1680
|
+
sage: A.restriction([])
|
|
1681
|
+
{}
|
|
1682
|
+
sage: A.restriction([4])
|
|
1683
|
+
{}
|
|
1684
|
+
"""
|
|
1685
|
+
ret = []
|
|
1686
|
+
for part in self:
|
|
1687
|
+
newpart = [i for i in part if i in I]
|
|
1688
|
+
if len(newpart) != 0:
|
|
1689
|
+
ret.append(newpart)
|
|
1690
|
+
return SetPartition(ret)
|
|
1691
|
+
|
|
1692
|
+
def ordered_set_partition_action(self, s):
|
|
1693
|
+
r"""
|
|
1694
|
+
Return the action of an ordered set partition ``s`` on ``self``.
|
|
1695
|
+
|
|
1696
|
+
Let `A = \{A_1, A_2, \ldots, A_k\}` be a set partition of some
|
|
1697
|
+
set `S` and `s` be an ordered set partition (i.e., set composition)
|
|
1698
|
+
of a subset of `[k]`. Let `A^{\downarrow}` denote the standardization
|
|
1699
|
+
of `A`, and `A_{\{ i_1, i_2, \ldots, i_m \}}` denote the sub-partition
|
|
1700
|
+
`\{A_{i_1}, A_{i_2}, \ldots, A_{i_m}\}` for any subset
|
|
1701
|
+
`\{i_1, \ldots, i_m\}` of `\{1, \ldots, k\}`. We define the set
|
|
1702
|
+
partition `s(A)` by
|
|
1703
|
+
|
|
1704
|
+
.. MATH::
|
|
1705
|
+
|
|
1706
|
+
s(A) = A_{s_1}^{\downarrow} | A_{s_2}^{\downarrow} | \cdots
|
|
1707
|
+
| A_{s_q}^{\downarrow}.
|
|
1708
|
+
|
|
1709
|
+
where `s = (s_1, s_2, \ldots, s_q)`. Here, the pipe symbol
|
|
1710
|
+
`|` is as defined in method :meth:`pipe`.
|
|
1711
|
+
|
|
1712
|
+
This is `s[A]` in section 2.3 in [LM2011]_.
|
|
1713
|
+
|
|
1714
|
+
INPUT:
|
|
1715
|
+
|
|
1716
|
+
- ``s`` -- an ordered set partition with base set a subset
|
|
1717
|
+
of `\{1, \ldots, k\}`
|
|
1718
|
+
|
|
1719
|
+
EXAMPLES::
|
|
1720
|
+
|
|
1721
|
+
sage: A = SetPartition([[1], [2,4], [3]])
|
|
1722
|
+
sage: s = OrderedSetPartition([[1,3], [2]])
|
|
1723
|
+
sage: A.ordered_set_partition_action(s)
|
|
1724
|
+
{{1}, {2}, {3, 4}}
|
|
1725
|
+
sage: s = OrderedSetPartition([[2,3], [1]])
|
|
1726
|
+
sage: A.ordered_set_partition_action(s)
|
|
1727
|
+
{{1, 3}, {2}, {4}}
|
|
1728
|
+
|
|
1729
|
+
We create Figure 1 in [LM2011]_ (we note that there is a typo in the
|
|
1730
|
+
lower-left corner of the table in the published version of the
|
|
1731
|
+
paper, whereas the arXiv version gives the correct partition)::
|
|
1732
|
+
|
|
1733
|
+
sage: A = SetPartition([[1,3], [2,9], [4,5,8], [7]])
|
|
1734
|
+
sage: B = SetPartition([[1,3], [2,8], [4,5,6], [7]])
|
|
1735
|
+
sage: C = SetPartition([[1,5], [2,8], [3,4,6], [7]])
|
|
1736
|
+
sage: s = OrderedSetPartition([[1,3], [2]])
|
|
1737
|
+
sage: t = OrderedSetPartition([[2], [3,4]])
|
|
1738
|
+
sage: u = OrderedSetPartition([[1], [2,3,4]])
|
|
1739
|
+
sage: A.ordered_set_partition_action(s)
|
|
1740
|
+
{{1, 2}, {3, 4, 5}, {6, 7}}
|
|
1741
|
+
sage: A.ordered_set_partition_action(t)
|
|
1742
|
+
{{1, 2}, {3, 4, 6}, {5}}
|
|
1743
|
+
sage: A.ordered_set_partition_action(u)
|
|
1744
|
+
{{1, 2}, {3, 8}, {4, 5, 7}, {6}}
|
|
1745
|
+
sage: B.ordered_set_partition_action(s)
|
|
1746
|
+
{{1, 2}, {3, 4, 5}, {6, 7}}
|
|
1747
|
+
sage: B.ordered_set_partition_action(t)
|
|
1748
|
+
{{1, 2}, {3, 4, 5}, {6}}
|
|
1749
|
+
sage: B.ordered_set_partition_action(u)
|
|
1750
|
+
{{1, 2}, {3, 8}, {4, 5, 6}, {7}}
|
|
1751
|
+
sage: C.ordered_set_partition_action(s)
|
|
1752
|
+
{{1, 4}, {2, 3, 5}, {6, 7}}
|
|
1753
|
+
sage: C.ordered_set_partition_action(t)
|
|
1754
|
+
{{1, 2}, {3, 4, 5}, {6}}
|
|
1755
|
+
sage: C.ordered_set_partition_action(u)
|
|
1756
|
+
{{1, 2}, {3, 8}, {4, 5, 6}, {7}}
|
|
1757
|
+
|
|
1758
|
+
REFERENCES:
|
|
1759
|
+
|
|
1760
|
+
- [LM2011]_
|
|
1761
|
+
"""
|
|
1762
|
+
cur = 1
|
|
1763
|
+
ret = []
|
|
1764
|
+
for part in s:
|
|
1765
|
+
sub_parts = [list(self[i - 1]) for i in part] # -1 for indexing
|
|
1766
|
+
# Standardizing sub_parts (the cur variable not being reset
|
|
1767
|
+
# to 1 gives us the offset we want):
|
|
1768
|
+
mins = [min(i) for i in sub_parts]
|
|
1769
|
+
over_max = max(map(max, sub_parts)) + 1
|
|
1770
|
+
temp = [[] for _ in repeat(None, len(part))]
|
|
1771
|
+
while min(mins) != over_max:
|
|
1772
|
+
m = min(mins)
|
|
1773
|
+
i = mins.index(m)
|
|
1774
|
+
temp[i].append(cur)
|
|
1775
|
+
cur += 1
|
|
1776
|
+
sub_parts[i].pop(sub_parts[i].index(m))
|
|
1777
|
+
if len(sub_parts[i]) != 0:
|
|
1778
|
+
mins[i] = min(sub_parts[i])
|
|
1779
|
+
else:
|
|
1780
|
+
mins[i] = over_max
|
|
1781
|
+
ret += temp
|
|
1782
|
+
return SetPartition(ret)
|
|
1783
|
+
|
|
1784
|
+
def refinements(self):
|
|
1785
|
+
"""
|
|
1786
|
+
Return a list of refinements of ``self``.
|
|
1787
|
+
|
|
1788
|
+
.. SEEALSO::
|
|
1789
|
+
|
|
1790
|
+
:meth:`coarsenings`
|
|
1791
|
+
|
|
1792
|
+
EXAMPLES::
|
|
1793
|
+
|
|
1794
|
+
sage: SetPartition([[1,3],[2,4]]).refinements() # needs sage.graphs sage.libs.flint
|
|
1795
|
+
[{{1, 3}, {2, 4}},
|
|
1796
|
+
{{1, 3}, {2}, {4}},
|
|
1797
|
+
{{1}, {2, 4}, {3}},
|
|
1798
|
+
{{1}, {2}, {3}, {4}}]
|
|
1799
|
+
sage: SetPartition([[1],[2,4],[3]]).refinements() # needs sage.graphs sage.libs.flint
|
|
1800
|
+
[{{1}, {2, 4}, {3}}, {{1}, {2}, {3}, {4}}]
|
|
1801
|
+
sage: SetPartition([]).refinements() # needs sage.graphs sage.libs.flint
|
|
1802
|
+
[{}]
|
|
1803
|
+
"""
|
|
1804
|
+
L = [SetPartitions(part) for part in self]
|
|
1805
|
+
return [SetPartition(sum(map(list, x), [])) for x in itertools.product(*L)]
|
|
1806
|
+
|
|
1807
|
+
def strict_coarsenings(self):
|
|
1808
|
+
r"""
|
|
1809
|
+
Return all strict coarsenings of ``self``.
|
|
1810
|
+
|
|
1811
|
+
Strict coarsening is the binary relation on set partitions
|
|
1812
|
+
defined as the transitive-and-reflexive closure of the
|
|
1813
|
+
relation `\prec` defined as follows: For two set partitions
|
|
1814
|
+
`A` and `B`, we have `A \prec B` if there exist parts
|
|
1815
|
+
`A_i, A_j` of `A` such that `\max(A_i) < \min(A_j)` and
|
|
1816
|
+
`B = A \setminus \{A_i, A_j\} \cup \{ A_i \cup A_j \}`.
|
|
1817
|
+
|
|
1818
|
+
EXAMPLES::
|
|
1819
|
+
|
|
1820
|
+
sage: A = SetPartition([[1],[2,3],[4]])
|
|
1821
|
+
sage: A.strict_coarsenings()
|
|
1822
|
+
[{{1}, {2, 3}, {4}}, {{1, 2, 3}, {4}}, {{1, 4}, {2, 3}},
|
|
1823
|
+
{{1}, {2, 3, 4}}, {{1, 2, 3, 4}}]
|
|
1824
|
+
sage: SetPartition([[1],[2,4],[3]]).strict_coarsenings()
|
|
1825
|
+
[{{1}, {2, 4}, {3}}, {{1, 2, 4}, {3}}, {{1, 3}, {2, 4}}]
|
|
1826
|
+
sage: SetPartition([]).strict_coarsenings()
|
|
1827
|
+
[{}]
|
|
1828
|
+
"""
|
|
1829
|
+
# This is more or less generic code for computing a
|
|
1830
|
+
# transitive-and-reflexive closure by depth-first search.
|
|
1831
|
+
todo = [self]
|
|
1832
|
+
visited = set([self])
|
|
1833
|
+
ret = [self]
|
|
1834
|
+
while todo:
|
|
1835
|
+
A = todo.pop()
|
|
1836
|
+
for i, part in enumerate(A):
|
|
1837
|
+
for j, other in enumerate(A[i + 1:]):
|
|
1838
|
+
if max(part) < min(other):
|
|
1839
|
+
next_pi = A[:i]
|
|
1840
|
+
next_pi.append(part.union(other))
|
|
1841
|
+
next_pi += A[i + 1:i + 1 + j] + A[i + j + 2:]
|
|
1842
|
+
next_pi = SetPartition(next_pi)
|
|
1843
|
+
if next_pi not in visited:
|
|
1844
|
+
todo.append(next_pi)
|
|
1845
|
+
visited.add(next_pi)
|
|
1846
|
+
ret.append(next_pi)
|
|
1847
|
+
return ret
|
|
1848
|
+
|
|
1849
|
+
def arcs(self):
|
|
1850
|
+
r"""
|
|
1851
|
+
Return ``self`` as a list of arcs.
|
|
1852
|
+
|
|
1853
|
+
Assuming that the blocks are sorted, the arcs are the pairs
|
|
1854
|
+
of consecutive elements in the blocks.
|
|
1855
|
+
|
|
1856
|
+
EXAMPLES::
|
|
1857
|
+
|
|
1858
|
+
sage: A = SetPartition([[1],[2,3],[4]])
|
|
1859
|
+
sage: A.arcs()
|
|
1860
|
+
[(2, 3)]
|
|
1861
|
+
sage: B = SetPartition([[1,3,6,7],[2,5],[4]])
|
|
1862
|
+
sage: B.arcs()
|
|
1863
|
+
[(1, 3), (3, 6), (6, 7), (2, 5)]
|
|
1864
|
+
"""
|
|
1865
|
+
arcs = []
|
|
1866
|
+
for p in self:
|
|
1867
|
+
p = sorted(p)
|
|
1868
|
+
arcs.extend((p[i], p[i + 1]) for i in range(len(p) - 1))
|
|
1869
|
+
return arcs
|
|
1870
|
+
|
|
1871
|
+
def plot(self, angle=None, color='black', base_set_dict=None):
|
|
1872
|
+
r"""
|
|
1873
|
+
Return a plot of ``self``.
|
|
1874
|
+
|
|
1875
|
+
INPUT:
|
|
1876
|
+
|
|
1877
|
+
- ``angle`` -- (default: `\pi/4`) the angle at which the arcs take off
|
|
1878
|
+
(if angle is negative, the arcs are drawn below the horizontal line)
|
|
1879
|
+
|
|
1880
|
+
- ``color`` -- (default: ``'black'``) color of the arcs
|
|
1881
|
+
|
|
1882
|
+
- ``base_set_dict`` -- (optional) dictionary with keys elements
|
|
1883
|
+
of :meth:`base_set()` and values as integer or float
|
|
1884
|
+
|
|
1885
|
+
EXAMPLES::
|
|
1886
|
+
|
|
1887
|
+
sage: p = SetPartition([[1,10,11],[2,3,7],[4,5,6],[8,9]])
|
|
1888
|
+
sage: p.plot() # needs sage.plot sage.symbolic
|
|
1889
|
+
Graphics object consisting of 29 graphics primitives
|
|
1890
|
+
|
|
1891
|
+
.. PLOT::
|
|
1892
|
+
|
|
1893
|
+
p = SetPartition([[1,10,11],[2,3,7],[4,5,6],[8,9]])
|
|
1894
|
+
sphinx_plot(p.plot())
|
|
1895
|
+
|
|
1896
|
+
::
|
|
1897
|
+
|
|
1898
|
+
sage: p = SetPartition([[1,3,4],[2,5]])
|
|
1899
|
+
sage: print(p.plot().description()) # needs sage.plot sage.symbolic
|
|
1900
|
+
Point set defined by 1 point(s): [(0.0, 0.0)]
|
|
1901
|
+
Point set defined by 1 point(s): [(1.0, 0.0)]
|
|
1902
|
+
Point set defined by 1 point(s): [(2.0, 0.0)]
|
|
1903
|
+
Point set defined by 1 point(s): [(3.0, 0.0)]
|
|
1904
|
+
Point set defined by 1 point(s): [(4.0, 0.0)]
|
|
1905
|
+
Text '1' at the point (0.0,-0.1)
|
|
1906
|
+
Text '2' at the point (1.0,-0.1)
|
|
1907
|
+
Text '3' at the point (2.0,-0.1)
|
|
1908
|
+
Text '4' at the point (3.0,-0.1)
|
|
1909
|
+
Text '5' at the point (4.0,-0.1)
|
|
1910
|
+
Arc with center (1.0,-1.0) radii (1.41421356237...,1.41421356237...)
|
|
1911
|
+
angle 0.0 inside the sector (0.785398163397...,2.35619449019...)
|
|
1912
|
+
Arc with center (2.5,-0.5) radii (0.70710678118...,0.70710678118...)
|
|
1913
|
+
angle 0.0 inside the sector (0.785398163397...,2.35619449019...)
|
|
1914
|
+
Arc with center (2.5,-1.5) radii (2.1213203435...,2.1213203435...)
|
|
1915
|
+
angle 0.0 inside the sector (0.785398163397...,2.35619449019...)
|
|
1916
|
+
sage: p = SetPartition([['a','c'],['b','d'],['e']])
|
|
1917
|
+
sage: print(p.plot().description()) # needs sage.plot sage.symbolic
|
|
1918
|
+
Point set defined by 1 point(s): [(0.0, 0.0)]
|
|
1919
|
+
Point set defined by 1 point(s): [(1.0, 0.0)]
|
|
1920
|
+
Point set defined by 1 point(s): [(2.0, 0.0)]
|
|
1921
|
+
Point set defined by 1 point(s): [(3.0, 0.0)]
|
|
1922
|
+
Point set defined by 1 point(s): [(4.0, 0.0)]
|
|
1923
|
+
Text 'a' at the point (0.0,-0.1)
|
|
1924
|
+
Text 'b' at the point (1.0,-0.1)
|
|
1925
|
+
Text 'c' at the point (2.0,-0.1)
|
|
1926
|
+
Text 'd' at the point (3.0,-0.1)
|
|
1927
|
+
Text 'e' at the point (4.0,-0.1)
|
|
1928
|
+
Arc with center (1.0,-1.0) radii (1.41421356237...,1.41421356237...)
|
|
1929
|
+
angle 0.0 inside the sector (0.785398163397...,2.35619449019...)
|
|
1930
|
+
Arc with center (2.0,-1.0) radii (1.41421356237...,1.41421356237...)
|
|
1931
|
+
angle 0.0 inside the sector (0.785398163397...,2.35619449019...)
|
|
1932
|
+
sage: p = SetPartition([['a','c'],['b','d'],['e']])
|
|
1933
|
+
sage: print(p.plot(base_set_dict={'a':0,'b':1,'c':2, # needs sage.plot sage.symbolic
|
|
1934
|
+
....: 'd':-2.3,'e':5.4}).description())
|
|
1935
|
+
Point set defined by 1 point(s): [(-2.3, 0.0)]
|
|
1936
|
+
Point set defined by 1 point(s): [(0.0, 0.0)]
|
|
1937
|
+
Point set defined by 1 point(s): [(1.0, 0.0)]
|
|
1938
|
+
Point set defined by 1 point(s): [(2.0, 0.0)]
|
|
1939
|
+
Point set defined by 1 point(s): [(5.4, 0.0)]
|
|
1940
|
+
Text 'a' at the point (0.0,-0.1)
|
|
1941
|
+
Text 'b' at the point (1.0,-0.1)
|
|
1942
|
+
Text 'c' at the point (2.0,-0.1)
|
|
1943
|
+
Text 'd' at the point (-2.3,-0.1)
|
|
1944
|
+
Text 'e' at the point (5.4,-0.1)
|
|
1945
|
+
Arc with center (-0.6...,-1.65) radii (2.3334523779...,2.3334523779...)
|
|
1946
|
+
angle 0.0 inside the sector (0.785398163397...,2.35619449019...)
|
|
1947
|
+
Arc with center (1.0,-1.0) radii (1.4142135623...,1.4142135623...)
|
|
1948
|
+
angle 0.0 inside the sector (0.785398163397...,2.35619449019...)
|
|
1949
|
+
"""
|
|
1950
|
+
from sage.plot.graphics import Graphics
|
|
1951
|
+
from sage.plot.point import point
|
|
1952
|
+
from sage.plot.text import text
|
|
1953
|
+
from sage.plot.arc import arc
|
|
1954
|
+
from sage.symbolic.constants import pi
|
|
1955
|
+
from sage.functions.trig import tan, sin
|
|
1956
|
+
from sage.functions.generalized import sgn
|
|
1957
|
+
|
|
1958
|
+
diag = Graphics()
|
|
1959
|
+
sorted_vertices_list = list(self.base_set())
|
|
1960
|
+
sorted_vertices_list.sort()
|
|
1961
|
+
|
|
1962
|
+
if angle is None:
|
|
1963
|
+
angle = pi / 4
|
|
1964
|
+
|
|
1965
|
+
if base_set_dict is not None:
|
|
1966
|
+
vertices_dict = base_set_dict
|
|
1967
|
+
else:
|
|
1968
|
+
vertices_dict = {val: pos for pos, val in enumerate(sorted_vertices_list)}
|
|
1969
|
+
|
|
1970
|
+
for elt in vertices_dict:
|
|
1971
|
+
pos = vertices_dict[elt]
|
|
1972
|
+
diag += point((pos, 0), size=30, color=color)
|
|
1973
|
+
diag += text(elt, (pos, -sgn(angle) * 0.1), color=color)
|
|
1974
|
+
# TODO: change 0.1 to something proportional to the height of the picture
|
|
1975
|
+
|
|
1976
|
+
for k, j in self.arcs():
|
|
1977
|
+
pos_k, pos_j = float(vertices_dict[k]), float(vertices_dict[j])
|
|
1978
|
+
center = ((pos_k + pos_j) / 2,
|
|
1979
|
+
-abs(pos_j - pos_k) / (2 * tan(angle)))
|
|
1980
|
+
r1 = abs((pos_j - pos_k) / (2 * sin(angle)))
|
|
1981
|
+
sector = (sgn(angle) * (pi / 2 - angle),
|
|
1982
|
+
sgn(angle) * (pi / 2 + angle))
|
|
1983
|
+
diag += arc(center=center, r1=r1, sector=sector, color=color)
|
|
1984
|
+
|
|
1985
|
+
diag.axes(False)
|
|
1986
|
+
return diag
|
|
1987
|
+
|
|
1988
|
+
|
|
1989
|
+
class SetPartitions(UniqueRepresentation, Parent):
|
|
1990
|
+
r"""
|
|
1991
|
+
An (unordered) partition of a set `S` is a set of pairwise
|
|
1992
|
+
disjoint nonempty subsets with union `S`, and is represented
|
|
1993
|
+
by a sorted list of such subsets.
|
|
1994
|
+
|
|
1995
|
+
``SetPartitions(s)`` returns the class of all set partitions of the set
|
|
1996
|
+
``s``, which can be given as a set or a string; if a string, each
|
|
1997
|
+
character is considered an element.
|
|
1998
|
+
|
|
1999
|
+
``SetPartitions(n)``, where ``n`` is an integer, returns the class of
|
|
2000
|
+
all set partitions of the set `\{1, 2, \ldots, n\}`.
|
|
2001
|
+
|
|
2002
|
+
You may specify a second argument `k`. If `k` is an integer,
|
|
2003
|
+
:class:`SetPartitions` returns the class of set partitions into `k` parts;
|
|
2004
|
+
if it is an integer partition, :class:`SetPartitions` returns the class of
|
|
2005
|
+
set partitions whose block sizes correspond to that integer partition.
|
|
2006
|
+
|
|
2007
|
+
The Bell number `B_n`, named in honor of Eric Temple Bell,
|
|
2008
|
+
is the number of different partitions of a set with `n` elements.
|
|
2009
|
+
|
|
2010
|
+
EXAMPLES::
|
|
2011
|
+
|
|
2012
|
+
sage: S = [1,2,3,4]
|
|
2013
|
+
sage: SetPartitions(S, 2)
|
|
2014
|
+
Set partitions of {1, 2, 3, 4} with 2 parts
|
|
2015
|
+
sage: SetPartitions([1,2,3,4], [3,1]).list() # needs sage.graphs sage.rings.finite_rings
|
|
2016
|
+
[{{1}, {2, 3, 4}}, {{1, 2, 3}, {4}}, {{1, 2, 4}, {3}}, {{1, 3, 4}, {2}}]
|
|
2017
|
+
sage: SetPartitions(7, [3,3,1]).cardinality() # needs sage.libs.flint
|
|
2018
|
+
70
|
|
2019
|
+
|
|
2020
|
+
In strings, repeated letters are not considered distinct as of
|
|
2021
|
+
:issue:`14140`::
|
|
2022
|
+
|
|
2023
|
+
sage: SetPartitions('abcde').cardinality() # needs sage.libs.flint
|
|
2024
|
+
52
|
|
2025
|
+
sage: SetPartitions('aabcd').cardinality() # needs sage.libs.flint
|
|
2026
|
+
15
|
|
2027
|
+
|
|
2028
|
+
If the number of parts exceeds the length of the set,
|
|
2029
|
+
an empty iterator is returned (:issue:`37643`)::
|
|
2030
|
+
|
|
2031
|
+
sage: SetPartitions(range(3), 4).list()
|
|
2032
|
+
[]
|
|
2033
|
+
sage: SetPartitions('abcd', 6).list()
|
|
2034
|
+
[]
|
|
2035
|
+
|
|
2036
|
+
REFERENCES:
|
|
2037
|
+
|
|
2038
|
+
- :wikipedia:`Partition_of_a_set`
|
|
2039
|
+
"""
|
|
2040
|
+
@staticmethod
|
|
2041
|
+
def __classcall_private__(cls, s=None, part=None):
|
|
2042
|
+
"""
|
|
2043
|
+
Normalize input to ensure a unique representation.
|
|
2044
|
+
|
|
2045
|
+
EXAMPLES::
|
|
2046
|
+
|
|
2047
|
+
sage: S = SetPartitions(4)
|
|
2048
|
+
sage: T = SetPartitions([1,2,3,4])
|
|
2049
|
+
sage: S is T
|
|
2050
|
+
True
|
|
2051
|
+
"""
|
|
2052
|
+
if s is None:
|
|
2053
|
+
return SetPartitions_all()
|
|
2054
|
+
if isinstance(s, (int, Integer)):
|
|
2055
|
+
s = frozenset(range(1, s + 1))
|
|
2056
|
+
else:
|
|
2057
|
+
try:
|
|
2058
|
+
if s.cardinality() == infinity:
|
|
2059
|
+
raise ValueError("the set must be finite")
|
|
2060
|
+
except AttributeError:
|
|
2061
|
+
pass
|
|
2062
|
+
s = frozenset(s)
|
|
2063
|
+
|
|
2064
|
+
if part is None:
|
|
2065
|
+
return SetPartitions_set(s)
|
|
2066
|
+
else:
|
|
2067
|
+
if isinstance(part, (int, Integer)):
|
|
2068
|
+
return SetPartitions_setn(s, part)
|
|
2069
|
+
else:
|
|
2070
|
+
part = sorted(part, reverse=True)
|
|
2071
|
+
if part not in Partitions(len(s)):
|
|
2072
|
+
raise ValueError("part must be an integer partition of %s" % len(s))
|
|
2073
|
+
return SetPartitions_setparts(s, Partition(part))
|
|
2074
|
+
|
|
2075
|
+
def __contains__(self, x):
|
|
2076
|
+
"""
|
|
2077
|
+
TESTS::
|
|
2078
|
+
|
|
2079
|
+
sage: S = SetPartitions(4, [2,2])
|
|
2080
|
+
sage: SA = SetPartitions()
|
|
2081
|
+
sage: all(sp in SA for sp in S) # needs sage.graphs sage.modules sage.rings.finite_rings
|
|
2082
|
+
True
|
|
2083
|
+
sage: Set([Set([1,2]),Set([3,7])]) in SA # needs sage.graphs
|
|
2084
|
+
True
|
|
2085
|
+
sage: Set([Set([1,2]),Set([2,3])]) in SA # needs sage.graphs
|
|
2086
|
+
False
|
|
2087
|
+
sage: Set([]) in SA # needs sage.graphs
|
|
2088
|
+
True
|
|
2089
|
+
"""
|
|
2090
|
+
# x must be a set
|
|
2091
|
+
if not isinstance(x, (SetPartition, set, frozenset, Set_generic)):
|
|
2092
|
+
return False
|
|
2093
|
+
|
|
2094
|
+
# Check that all parts are disjoint
|
|
2095
|
+
base_set = set(e for p in x for e in p)
|
|
2096
|
+
if len(base_set) != sum(map(len, x)):
|
|
2097
|
+
return False
|
|
2098
|
+
|
|
2099
|
+
# Check to make sure each element of x is a set
|
|
2100
|
+
for s in x:
|
|
2101
|
+
if not isinstance(s, (set, frozenset, Set_generic)):
|
|
2102
|
+
return False
|
|
2103
|
+
|
|
2104
|
+
return True
|
|
2105
|
+
|
|
2106
|
+
def _element_constructor_(self, s, check=True):
|
|
2107
|
+
"""
|
|
2108
|
+
Construct an element of ``self`` from ``s``.
|
|
2109
|
+
|
|
2110
|
+
INPUT:
|
|
2111
|
+
|
|
2112
|
+
- ``s`` -- set of sets
|
|
2113
|
+
|
|
2114
|
+
EXAMPLES::
|
|
2115
|
+
|
|
2116
|
+
sage: S = SetPartitions(4)
|
|
2117
|
+
sage: elt = S([[1,3],[2,4]]); elt
|
|
2118
|
+
{{1, 3}, {2, 4}}
|
|
2119
|
+
sage: P = SetPartitions()
|
|
2120
|
+
sage: P(elt).parent() is P
|
|
2121
|
+
True
|
|
2122
|
+
sage: S = SetPartitions([])
|
|
2123
|
+
sage: S([])
|
|
2124
|
+
{}
|
|
2125
|
+
"""
|
|
2126
|
+
if isinstance(s, SetPartition):
|
|
2127
|
+
if isinstance(s.parent(), SetPartitions):
|
|
2128
|
+
return self.element_class(self, s, check=check)
|
|
2129
|
+
raise ValueError("cannot convert %s into an element of %s" % (s, self))
|
|
2130
|
+
return self.element_class(self, s, check=check)
|
|
2131
|
+
|
|
2132
|
+
Element = SetPartition
|
|
2133
|
+
|
|
2134
|
+
def from_restricted_growth_word(self, w, bijection='blocks'):
|
|
2135
|
+
r"""
|
|
2136
|
+
Convert a word of length `n` with letters in the nonnegative
|
|
2137
|
+
integers such that each letter is at most 1 larger than all
|
|
2138
|
+
the letters before to a set partition of `\{1,...,n\}`.
|
|
2139
|
+
|
|
2140
|
+
INPUT:
|
|
2141
|
+
|
|
2142
|
+
- ``w`` -- a restricted growth word
|
|
2143
|
+
|
|
2144
|
+
- ``bijection`` -- (default: ``blocks``) defines the map from
|
|
2145
|
+
restricted growth functions to set partitions. These are
|
|
2146
|
+
currently:
|
|
2147
|
+
|
|
2148
|
+
- ``blocks``: .
|
|
2149
|
+
|
|
2150
|
+
- ``intertwining``: :meth:`from_restricted_growth_word_intertwining`.
|
|
2151
|
+
|
|
2152
|
+
OUTPUT: a set partition
|
|
2153
|
+
|
|
2154
|
+
.. SEEALSO::
|
|
2155
|
+
|
|
2156
|
+
:meth:`SetPartition.to_restricted_growth_word`
|
|
2157
|
+
|
|
2158
|
+
EXAMPLES::
|
|
2159
|
+
|
|
2160
|
+
sage: SetPartitions().from_restricted_growth_word([0, 1, 2, 0, 2, 2, 3, 1, 2])
|
|
2161
|
+
{{1, 4}, {2, 8}, {3, 5, 6, 9}, {7}}
|
|
2162
|
+
|
|
2163
|
+
sage: SetPartitions().from_restricted_growth_word([0, 0, 1, 0, 2, 2, 0, 3, 1, 2, 2, 4, 2])
|
|
2164
|
+
{{1, 2, 4, 7}, {3, 9}, {5, 6, 10, 11, 13}, {8}, {12}}
|
|
2165
|
+
|
|
2166
|
+
sage: SetPartitions().from_restricted_growth_word([0, 0, 1, 0, 2, 2, 0, 3, 1, 2, 2, 4, 2], "intertwining")
|
|
2167
|
+
{{1, 2, 6, 7, 9}, {3, 4}, {5, 10, 13}, {8, 11}, {12}}
|
|
2168
|
+
"""
|
|
2169
|
+
if bijection == "blocks":
|
|
2170
|
+
return self.from_restricted_growth_word_blocks(w)
|
|
2171
|
+
if bijection == "intertwining":
|
|
2172
|
+
return self.from_restricted_growth_word_intertwining(w)
|
|
2173
|
+
raise ValueError("the given bijection is not valid")
|
|
2174
|
+
|
|
2175
|
+
def from_restricted_growth_word_blocks(self, w):
|
|
2176
|
+
r"""
|
|
2177
|
+
Convert a word of length `n` with letters in the nonnegative
|
|
2178
|
+
integers such that each letter is at most 1 larger than all
|
|
2179
|
+
the letters before to a set partition of `\{1,...,n\}`.
|
|
2180
|
+
|
|
2181
|
+
``w[i]`` is the index of the block containing ``i+1`` when
|
|
2182
|
+
sorting the blocks by their minimal element.
|
|
2183
|
+
|
|
2184
|
+
INPUT:
|
|
2185
|
+
|
|
2186
|
+
- ``w`` -- a restricted growth word
|
|
2187
|
+
|
|
2188
|
+
OUTPUT: a set partition
|
|
2189
|
+
|
|
2190
|
+
.. SEEALSO::
|
|
2191
|
+
|
|
2192
|
+
:meth:`from_restricted_growth_word`
|
|
2193
|
+
:meth:`SetPartition.to_restricted_growth_word`
|
|
2194
|
+
|
|
2195
|
+
EXAMPLES::
|
|
2196
|
+
|
|
2197
|
+
sage: SetPartitions().from_restricted_growth_word_blocks([0, 0, 1, 0, 2, 2, 0, 3, 1, 2, 2, 4, 2])
|
|
2198
|
+
{{1, 2, 4, 7}, {3, 9}, {5, 6, 10, 11, 13}, {8}, {12}}
|
|
2199
|
+
"""
|
|
2200
|
+
R = []
|
|
2201
|
+
for i, B in enumerate(w, 1):
|
|
2202
|
+
if len(R) <= B:
|
|
2203
|
+
R.append([i])
|
|
2204
|
+
else:
|
|
2205
|
+
R[B].append(i)
|
|
2206
|
+
return self.element_class(self, R)
|
|
2207
|
+
|
|
2208
|
+
def from_restricted_growth_word_intertwining(self, w):
|
|
2209
|
+
r"""
|
|
2210
|
+
Convert a word of length `n` with letters in the nonnegative
|
|
2211
|
+
integers such that each letter is at most 1 larger than all
|
|
2212
|
+
the letters before to a set partition of `\{1,...,n\}`.
|
|
2213
|
+
|
|
2214
|
+
The `i`-th letter of the word is the numbers of crossings of
|
|
2215
|
+
the arc (or half-arc) in the extended arc diagram ending at
|
|
2216
|
+
`i`, with arcs (or half-arcs) beginning at a smaller element
|
|
2217
|
+
and ending at a larger element.
|
|
2218
|
+
|
|
2219
|
+
INPUT:
|
|
2220
|
+
|
|
2221
|
+
- ``w`` -- a restricted growth word
|
|
2222
|
+
|
|
2223
|
+
OUTPUT: a set partition
|
|
2224
|
+
|
|
2225
|
+
.. SEEALSO::
|
|
2226
|
+
|
|
2227
|
+
:meth:`from_restricted_growth_word`
|
|
2228
|
+
:meth:`SetPartition.to_restricted_growth_word`
|
|
2229
|
+
|
|
2230
|
+
EXAMPLES::
|
|
2231
|
+
|
|
2232
|
+
sage: SetPartitions().from_restricted_growth_word_intertwining([0, 0, 1, 0, 2, 2, 0, 3, 1, 2, 2, 4, 2])
|
|
2233
|
+
{{1, 2, 6, 7, 9}, {3, 4}, {5, 10, 13}, {8, 11}, {12}}
|
|
2234
|
+
"""
|
|
2235
|
+
if len(w) == 0:
|
|
2236
|
+
return self.element_class(self, [])
|
|
2237
|
+
R = [[1]]
|
|
2238
|
+
C = [1] # closers, always reverse sorted
|
|
2239
|
+
m = 0 # max
|
|
2240
|
+
for i in range(1, len(w)):
|
|
2241
|
+
if w[i] == 1 + m: # i+1 is an opener
|
|
2242
|
+
m += 1
|
|
2243
|
+
R.append([i + 1])
|
|
2244
|
+
else:
|
|
2245
|
+
# add i+1 to the block, such that there are I[i] closers thereafter
|
|
2246
|
+
l = C[w[i]]
|
|
2247
|
+
B = next(B for B in R if l in B)
|
|
2248
|
+
B.append(i + 1)
|
|
2249
|
+
C.remove(l)
|
|
2250
|
+
C = [i + 1] + C
|
|
2251
|
+
return self.element_class(self, R)
|
|
2252
|
+
|
|
2253
|
+
def from_rook_placement(self, rooks, bijection='arcs', n=None):
|
|
2254
|
+
r"""
|
|
2255
|
+
Convert a rook placement of the triangular grid to a set
|
|
2256
|
+
partition of `\{1,...,n\}`.
|
|
2257
|
+
|
|
2258
|
+
If ``n`` is not given, it is first checked whether it can be
|
|
2259
|
+
determined from the parent, otherwise it is the maximal
|
|
2260
|
+
occurring integer in the set of rooks.
|
|
2261
|
+
|
|
2262
|
+
INPUT:
|
|
2263
|
+
|
|
2264
|
+
- ``rooks`` -- list of pairs `(i,j)` satisfying
|
|
2265
|
+
`0 < i < j < n+1`
|
|
2266
|
+
|
|
2267
|
+
- ``bijection`` -- (default: ``arcs``) defines the map from
|
|
2268
|
+
rook placements to set partitions. These are currently:
|
|
2269
|
+
|
|
2270
|
+
- ``arcs``: :meth:`from_arcs`.
|
|
2271
|
+
- ``gamma``: :meth:`from_rook_placement_gamma`.
|
|
2272
|
+
- ``rho``: :meth:`from_rook_placement_rho`.
|
|
2273
|
+
- ``psi``: :meth:`from_rook_placement_psi`.
|
|
2274
|
+
|
|
2275
|
+
- ``n`` -- (optional) the size of the ground set
|
|
2276
|
+
|
|
2277
|
+
.. SEEALSO::
|
|
2278
|
+
|
|
2279
|
+
:meth:`SetPartition.to_rook_placement`
|
|
2280
|
+
|
|
2281
|
+
EXAMPLES::
|
|
2282
|
+
|
|
2283
|
+
sage: SetPartitions(9).from_rook_placement([[1,4],[2,8],[3,5],[5,6],[6,9]])
|
|
2284
|
+
{{1, 4}, {2, 8}, {3, 5, 6, 9}, {7}}
|
|
2285
|
+
|
|
2286
|
+
sage: SetPartitions(13).from_rook_placement([[12,13],[10,12],[8,9],[7,11],[5,8],[4,6],[3,5],[1,4]], "gamma")
|
|
2287
|
+
{{1, 2, 4, 7}, {3, 9}, {5, 6, 10, 11, 13}, {8}, {12}}
|
|
2288
|
+
|
|
2289
|
+
TESTS::
|
|
2290
|
+
|
|
2291
|
+
sage: SetPartitions().from_rook_placement([])
|
|
2292
|
+
{}
|
|
2293
|
+
sage: SetPartitions().from_rook_placement([], "gamma")
|
|
2294
|
+
{}
|
|
2295
|
+
sage: SetPartitions().from_rook_placement([], "rho")
|
|
2296
|
+
{}
|
|
2297
|
+
sage: SetPartitions().from_rook_placement([], "psi")
|
|
2298
|
+
{}
|
|
2299
|
+
sage: SetPartitions().from_rook_placement([], n=2)
|
|
2300
|
+
{{1}, {2}}
|
|
2301
|
+
sage: SetPartitions().from_rook_placement([], "gamma", 2)
|
|
2302
|
+
{{1}, {2}}
|
|
2303
|
+
sage: SetPartitions().from_rook_placement([], "rho", 2)
|
|
2304
|
+
{{1}, {2}}
|
|
2305
|
+
sage: SetPartitions().from_rook_placement([], "psi", 2)
|
|
2306
|
+
{{1}, {2}}
|
|
2307
|
+
"""
|
|
2308
|
+
if n is None:
|
|
2309
|
+
try:
|
|
2310
|
+
n = self.base_set_cardinality()
|
|
2311
|
+
except AttributeError:
|
|
2312
|
+
n = max((max(r) for r in rooks), default=0)
|
|
2313
|
+
|
|
2314
|
+
if bijection == "arcs":
|
|
2315
|
+
return self.from_arcs(rooks, n)
|
|
2316
|
+
if bijection == "rho":
|
|
2317
|
+
return self.from_rook_placement_rho(rooks, n)
|
|
2318
|
+
if bijection == "gamma":
|
|
2319
|
+
return self.from_rook_placement_gamma(rooks, n)
|
|
2320
|
+
if bijection == "psi":
|
|
2321
|
+
return self.from_rook_placement_psi(rooks, n)
|
|
2322
|
+
raise ValueError("the given bijection is not valid")
|
|
2323
|
+
|
|
2324
|
+
def from_arcs(self, arcs, n):
|
|
2325
|
+
r"""
|
|
2326
|
+
Return the coarsest set partition of `\{1,...,n\}` such that any
|
|
2327
|
+
two elements connected by an arc are in the same block.
|
|
2328
|
+
|
|
2329
|
+
INPUT:
|
|
2330
|
+
|
|
2331
|
+
- ``n`` -- integer specifying the size of the set
|
|
2332
|
+
partition to be produced
|
|
2333
|
+
|
|
2334
|
+
- ``arcs`` -- list of pairs specifying which elements are
|
|
2335
|
+
in the same block
|
|
2336
|
+
|
|
2337
|
+
.. SEEALSO::
|
|
2338
|
+
|
|
2339
|
+
- :meth:`from_rook_placement`
|
|
2340
|
+
- :meth:`SetPartition.to_rook_placement`
|
|
2341
|
+
- :meth:`SetPartition.arcs`
|
|
2342
|
+
|
|
2343
|
+
EXAMPLES::
|
|
2344
|
+
|
|
2345
|
+
sage: SetPartitions().from_arcs([(2,3)], 5)
|
|
2346
|
+
{{1}, {2, 3}, {4}, {5}}
|
|
2347
|
+
"""
|
|
2348
|
+
P = DisjointSet(range(1, n + 1))
|
|
2349
|
+
for i, j in arcs:
|
|
2350
|
+
P.union(i, j)
|
|
2351
|
+
return self.element_class(self, P)
|
|
2352
|
+
|
|
2353
|
+
def from_rook_placement_gamma(self, rooks, n):
|
|
2354
|
+
r"""
|
|
2355
|
+
Return the set partition of `\{1,...,n\}` corresponding to the
|
|
2356
|
+
given rook placement by applying Wachs and White's bijection
|
|
2357
|
+
gamma.
|
|
2358
|
+
|
|
2359
|
+
Note that our index convention differs from the convention in
|
|
2360
|
+
[WW1991]_: regarding the rook board as a lower-right
|
|
2361
|
+
triangular grid, we refer with `(i,j)` to the cell in the
|
|
2362
|
+
`i`-th column from the right and the `j`-th row from the top.
|
|
2363
|
+
|
|
2364
|
+
INPUT:
|
|
2365
|
+
|
|
2366
|
+
- ``n`` -- integer specifying the size of the set
|
|
2367
|
+
partition to be produced
|
|
2368
|
+
|
|
2369
|
+
- ``rooks`` -- list of pairs `(i,j)` such that `0 < i < j < n+1`
|
|
2370
|
+
|
|
2371
|
+
OUTPUT: a set partition
|
|
2372
|
+
|
|
2373
|
+
.. SEEALSO::
|
|
2374
|
+
|
|
2375
|
+
- :meth:`from_rook_placement`
|
|
2376
|
+
- :meth:`SetPartition.to_rook_placement`
|
|
2377
|
+
- :meth:`SetPartition.to_rook_placement_gamma`
|
|
2378
|
+
|
|
2379
|
+
EXAMPLES:
|
|
2380
|
+
|
|
2381
|
+
Figure 5 in [WW1991]_ concerns the following rook placement::
|
|
2382
|
+
|
|
2383
|
+
sage: r = [(1, 4), (3, 5), (4, 6), (5, 8), (7, 11), (8, 9), (10, 12), (12, 13)]
|
|
2384
|
+
|
|
2385
|
+
Note that the rook `(1, 4)`, translated into Wachs and
|
|
2386
|
+
White's convention, is a rook in row 4 from the top and
|
|
2387
|
+
column 13 from the left. The corresponding set partition
|
|
2388
|
+
is::
|
|
2389
|
+
|
|
2390
|
+
sage: SetPartitions().from_rook_placement_gamma(r, 13)
|
|
2391
|
+
{{1, 2, 4, 7}, {3, 9}, {5, 6, 10, 11, 13}, {8}, {12}}
|
|
2392
|
+
"""
|
|
2393
|
+
if n == 0:
|
|
2394
|
+
return self.element_class(self, [])
|
|
2395
|
+
# the columns of the board, beginning with column n-1
|
|
2396
|
+
C = [set(range(n + 1 - j, n + 1)) for j in range(1, n)]
|
|
2397
|
+
# delete cells north and east of each rook
|
|
2398
|
+
for j, i in rooks:
|
|
2399
|
+
# north
|
|
2400
|
+
C[n - j - 1].difference_update(range(j + 1, i + 1))
|
|
2401
|
+
# east
|
|
2402
|
+
for l in range(n + 1 - j, n + 1):
|
|
2403
|
+
C[l - 2].discard(i)
|
|
2404
|
+
w = [0] + [len(c) for c in C]
|
|
2405
|
+
return self.from_restricted_growth_word_blocks(w)
|
|
2406
|
+
|
|
2407
|
+
def from_rook_placement_rho(self, rooks, n):
|
|
2408
|
+
r"""
|
|
2409
|
+
Return the set partition of `\{1,...,n\}` corresponding to the
|
|
2410
|
+
given rook placement by applying Wachs and White's bijection
|
|
2411
|
+
rho.
|
|
2412
|
+
|
|
2413
|
+
Note that our index convention differs from the convention in
|
|
2414
|
+
[WW1991]_: regarding the rook board as a lower-right
|
|
2415
|
+
triangular grid, we refer with `(i,j)` to the cell in the
|
|
2416
|
+
`i`-th column from the right and the `j`-th row from the top.
|
|
2417
|
+
|
|
2418
|
+
INPUT:
|
|
2419
|
+
|
|
2420
|
+
- ``n`` -- integer specifying the size of the set
|
|
2421
|
+
partition to be produced
|
|
2422
|
+
|
|
2423
|
+
- ``rooks`` -- list of pairs `(i,j)` such that `0 < i < j < n+1`
|
|
2424
|
+
|
|
2425
|
+
OUTPUT: a set partition
|
|
2426
|
+
|
|
2427
|
+
.. SEEALSO::
|
|
2428
|
+
|
|
2429
|
+
- :meth:`from_rook_placement`
|
|
2430
|
+
- :meth:`SetPartition.to_rook_placement`
|
|
2431
|
+
- :meth:`SetPartition.to_rook_placement_rho`
|
|
2432
|
+
|
|
2433
|
+
EXAMPLES:
|
|
2434
|
+
|
|
2435
|
+
Figure 5 in [WW1991]_ concerns the following rook placement::
|
|
2436
|
+
|
|
2437
|
+
sage: r = [(1, 2), (2, 6), (3, 4), (4, 10), (5, 9), (6, 7), (10, 11), (11, 13)]
|
|
2438
|
+
|
|
2439
|
+
Note that the rook `(1, 2)`, translated into Wachs and
|
|
2440
|
+
White's convention, is a rook in row 2 from the top and
|
|
2441
|
+
column 13 from the left. The corresponding set partition
|
|
2442
|
+
is::
|
|
2443
|
+
|
|
2444
|
+
sage: SetPartitions().from_rook_placement_rho(r, 13)
|
|
2445
|
+
{{1, 2, 4, 7}, {3, 9}, {5, 6, 10, 11, 13}, {8}, {12}}
|
|
2446
|
+
"""
|
|
2447
|
+
# the closers correspond to the empty columns
|
|
2448
|
+
cols = [j for j, _ in rooks]
|
|
2449
|
+
R = [j for j in range(1, n + 1) if j not in cols]
|
|
2450
|
+
# the columns of the board, beginning with column n-1
|
|
2451
|
+
C = [set(range(n + 1 - j, n + 1)) if n - j not in R else set()
|
|
2452
|
+
for j in range(1, n)]
|
|
2453
|
+
for j, i in rooks: # column j from right, row i from top
|
|
2454
|
+
# south
|
|
2455
|
+
C[n - j - 1].difference_update(range(i, n + 1))
|
|
2456
|
+
# east
|
|
2457
|
+
for l in range(n + 1 - j, n + 1):
|
|
2458
|
+
C[l - 2].discard(i)
|
|
2459
|
+
|
|
2460
|
+
C_flat = [i for c in C for i in c]
|
|
2461
|
+
# the number of closers which are larger than i and whose
|
|
2462
|
+
# block is before the block of i
|
|
2463
|
+
rs = [C_flat.count(i) for i in range(1, n + 1)]
|
|
2464
|
+
# create the blocks
|
|
2465
|
+
P = [[] for _ in R]
|
|
2466
|
+
for i in range(1, n + 1):
|
|
2467
|
+
k = rs[i - 1]
|
|
2468
|
+
# find k-th block which does not yet have a closer
|
|
2469
|
+
b = 0
|
|
2470
|
+
while k > 0 or (P[b] and P[b][-1] in R):
|
|
2471
|
+
if P[b][-1] not in R:
|
|
2472
|
+
k -= 1
|
|
2473
|
+
b += 1
|
|
2474
|
+
P[b].append(i)
|
|
2475
|
+
return self.element_class(self, P)
|
|
2476
|
+
|
|
2477
|
+
def from_rook_placement_psi(self, rooks, n):
|
|
2478
|
+
r"""
|
|
2479
|
+
Return the set partition of `\{1,...,n\}` corresponding to the
|
|
2480
|
+
given rook placement by applying Yip's bijection psi.
|
|
2481
|
+
|
|
2482
|
+
INPUT:
|
|
2483
|
+
|
|
2484
|
+
- ``n`` -- integer specifying the size of the set
|
|
2485
|
+
partition to be produced
|
|
2486
|
+
|
|
2487
|
+
- ``rooks`` -- list of pairs `(i,j)` such that `0 < i < j < n+1`
|
|
2488
|
+
|
|
2489
|
+
OUTPUT: a set partition
|
|
2490
|
+
|
|
2491
|
+
.. SEEALSO::
|
|
2492
|
+
|
|
2493
|
+
- :meth:`from_rook_placement`
|
|
2494
|
+
- :meth:`SetPartition.to_rook_placement`
|
|
2495
|
+
- :meth:`SetPartition.to_rook_placement_psi`
|
|
2496
|
+
|
|
2497
|
+
EXAMPLES:
|
|
2498
|
+
|
|
2499
|
+
Example 36 (arXiv version: Example 4.5) in [Yip2018]_
|
|
2500
|
+
concerns the following rook placement::
|
|
2501
|
+
|
|
2502
|
+
sage: r = [(4,5), (1,7), (3, 8), (7,9)]
|
|
2503
|
+
sage: SetPartitions().from_rook_placement_psi(r, 9)
|
|
2504
|
+
{{1, 5}, {2}, {3, 8, 9}, {4}, {6, 7}}
|
|
2505
|
+
"""
|
|
2506
|
+
# Yip draws the diagram as an upper triangular matrix, thus
|
|
2507
|
+
# we refer to the cell in row i and column j with (i, j)
|
|
2508
|
+
P = []
|
|
2509
|
+
rooks_by_column = {j: i for (i, j) in rooks}
|
|
2510
|
+
for c in range(1, n + 1):
|
|
2511
|
+
# determine the weight of column c
|
|
2512
|
+
try:
|
|
2513
|
+
r = rooks_by_column[c]
|
|
2514
|
+
n_rooks = 1
|
|
2515
|
+
ne = r - 1 + sum(1 for i, j in rooks if i > r and j < c)
|
|
2516
|
+
except KeyError:
|
|
2517
|
+
n_rooks = 0
|
|
2518
|
+
ne = sum(1 for i, j in rooks if j < c)
|
|
2519
|
+
|
|
2520
|
+
b = c - n_rooks - ne
|
|
2521
|
+
if len(P) == b - 1:
|
|
2522
|
+
P.append([c])
|
|
2523
|
+
else:
|
|
2524
|
+
P[b - 1].append(c)
|
|
2525
|
+
P = sorted(P, key=lambda B: (-len(B), min(B)))
|
|
2526
|
+
return self.element_class(self, P)
|
|
2527
|
+
|
|
2528
|
+
def is_less_than(self, s, t) -> bool:
|
|
2529
|
+
r"""
|
|
2530
|
+
Check if `s < t` in the refinement ordering on set partitions.
|
|
2531
|
+
|
|
2532
|
+
This means that `s` is a refinement of `t` and satisfies
|
|
2533
|
+
`s \neq t`.
|
|
2534
|
+
|
|
2535
|
+
A set partition `s` is said to be a refinement of a set
|
|
2536
|
+
partition `t` of the same set if and only if each part of
|
|
2537
|
+
`s` is a subset of a part of `t`.
|
|
2538
|
+
|
|
2539
|
+
EXAMPLES::
|
|
2540
|
+
|
|
2541
|
+
sage: S = SetPartitions(4)
|
|
2542
|
+
sage: s = S([[1,3],[2,4]])
|
|
2543
|
+
sage: t = S([[1],[2],[3],[4]])
|
|
2544
|
+
sage: S.is_less_than(t, s)
|
|
2545
|
+
True
|
|
2546
|
+
sage: S.is_less_than(s, t)
|
|
2547
|
+
False
|
|
2548
|
+
sage: S.is_less_than(s, s)
|
|
2549
|
+
False
|
|
2550
|
+
"""
|
|
2551
|
+
if hasattr(s.parent(), "_set"):
|
|
2552
|
+
S = s.parent()._set
|
|
2553
|
+
else:
|
|
2554
|
+
S = s.base_set()
|
|
2555
|
+
if hasattr(t.parent(), "_set"):
|
|
2556
|
+
T = t.parent()._set
|
|
2557
|
+
else:
|
|
2558
|
+
T = t.base_set()
|
|
2559
|
+
if S != T:
|
|
2560
|
+
raise ValueError("cannot compare partitions of different sets")
|
|
2561
|
+
|
|
2562
|
+
if s == t:
|
|
2563
|
+
return False
|
|
2564
|
+
|
|
2565
|
+
for p in s:
|
|
2566
|
+
x = next(iter(p))
|
|
2567
|
+
for t_ in t:
|
|
2568
|
+
if x in t_:
|
|
2569
|
+
break
|
|
2570
|
+
for p_ in p:
|
|
2571
|
+
if p_ not in t_:
|
|
2572
|
+
return False
|
|
2573
|
+
return True
|
|
2574
|
+
|
|
2575
|
+
lt = is_less_than
|
|
2576
|
+
|
|
2577
|
+
def is_strict_refinement(self, s, t) -> bool:
|
|
2578
|
+
r"""
|
|
2579
|
+
Return ``True`` if ``s`` is a strict refinement of ``t`` and
|
|
2580
|
+
satisfies `s \neq t`.
|
|
2581
|
+
|
|
2582
|
+
A set partition `s` is said to be a strict refinement of a set
|
|
2583
|
+
partition `t` of the same set if and only if one can obtain
|
|
2584
|
+
`t` from `s` by repeatedly combining pairs of parts whose
|
|
2585
|
+
convex hulls don't intersect (i. e., whenever we are combining
|
|
2586
|
+
two parts, the maximum of each of them should be smaller than
|
|
2587
|
+
the minimum of the other).
|
|
2588
|
+
|
|
2589
|
+
EXAMPLES::
|
|
2590
|
+
|
|
2591
|
+
sage: S = SetPartitions(4)
|
|
2592
|
+
sage: s = S([[1],[2],[3],[4]])
|
|
2593
|
+
sage: t = S([[1,3],[2,4]])
|
|
2594
|
+
sage: u = S([[1,2,3,4]])
|
|
2595
|
+
sage: S.is_strict_refinement(s, t)
|
|
2596
|
+
True
|
|
2597
|
+
sage: S.is_strict_refinement(t, u)
|
|
2598
|
+
False
|
|
2599
|
+
sage: A = SetPartition([[1,3],[2,4]])
|
|
2600
|
+
sage: B = SetPartition([[1,2,3,4]])
|
|
2601
|
+
sage: S.is_strict_refinement(s, A)
|
|
2602
|
+
True
|
|
2603
|
+
sage: S.is_strict_refinement(t, B)
|
|
2604
|
+
False
|
|
2605
|
+
"""
|
|
2606
|
+
if hasattr(s.parent(), "_set"):
|
|
2607
|
+
S = s.parent()._set
|
|
2608
|
+
else:
|
|
2609
|
+
S = frozenset(s.base_set())
|
|
2610
|
+
if hasattr(t.parent(), "_set"):
|
|
2611
|
+
T = t.parent()._set
|
|
2612
|
+
else:
|
|
2613
|
+
T = frozenset(t.base_set())
|
|
2614
|
+
if S != T:
|
|
2615
|
+
raise ValueError("cannot compare partitions of different sets")
|
|
2616
|
+
|
|
2617
|
+
if s == t:
|
|
2618
|
+
return False
|
|
2619
|
+
|
|
2620
|
+
for p in t:
|
|
2621
|
+
L = [x for x in s if x.issubset(p)]
|
|
2622
|
+
if sum(len(x) for x in L) != len(p) \
|
|
2623
|
+
or any(max(L[i]) > min(L[i + 1])
|
|
2624
|
+
for i in range(len(L) - 1)):
|
|
2625
|
+
return False
|
|
2626
|
+
return True
|
|
2627
|
+
|
|
2628
|
+
|
|
2629
|
+
class SetPartitions_all(SetPartitions):
|
|
2630
|
+
r"""
|
|
2631
|
+
All set partitions.
|
|
2632
|
+
"""
|
|
2633
|
+
|
|
2634
|
+
def __init__(self):
|
|
2635
|
+
"""
|
|
2636
|
+
Initialize ``self``.
|
|
2637
|
+
|
|
2638
|
+
EXAMPLES::
|
|
2639
|
+
|
|
2640
|
+
sage: S = SetPartitions()
|
|
2641
|
+
sage: TestSuite(S).run()
|
|
2642
|
+
"""
|
|
2643
|
+
SetPartitions.__init__(self, category=InfiniteEnumeratedSets())
|
|
2644
|
+
|
|
2645
|
+
def subset(self, size=None, **kwargs):
|
|
2646
|
+
"""
|
|
2647
|
+
Return the subset of set partitions of a given size and
|
|
2648
|
+
additional keyword arguments.
|
|
2649
|
+
|
|
2650
|
+
EXAMPLES::
|
|
2651
|
+
|
|
2652
|
+
sage: P = SetPartitions()
|
|
2653
|
+
sage: P.subset(4)
|
|
2654
|
+
Set partitions of {1, 2, 3, 4}
|
|
2655
|
+
"""
|
|
2656
|
+
if size is None:
|
|
2657
|
+
return self
|
|
2658
|
+
return SetPartitions(size, **kwargs)
|
|
2659
|
+
|
|
2660
|
+
def _repr_(self):
|
|
2661
|
+
"""
|
|
2662
|
+
Return a string representation of ``self``.
|
|
2663
|
+
|
|
2664
|
+
EXAMPLES::
|
|
2665
|
+
|
|
2666
|
+
sage: SetPartitions()
|
|
2667
|
+
Set partitions
|
|
2668
|
+
"""
|
|
2669
|
+
return "Set partitions"
|
|
2670
|
+
|
|
2671
|
+
def __iter__(self):
|
|
2672
|
+
"""
|
|
2673
|
+
Iterate over ``self``.
|
|
2674
|
+
|
|
2675
|
+
EXAMPLES::
|
|
2676
|
+
|
|
2677
|
+
sage: it = SetPartitions().__iter__()
|
|
2678
|
+
sage: [next(it) for x in range(10)]
|
|
2679
|
+
[{}, {{1}}, {{1, 2}}, {{1}, {2}}, {{1, 2, 3}}, {{1, 2}, {3}},
|
|
2680
|
+
{{1, 3}, {2}}, {{1}, {2, 3}}, {{1}, {2}, {3}}, {{1, 2, 3, 4}}]
|
|
2681
|
+
"""
|
|
2682
|
+
n = 0
|
|
2683
|
+
while True:
|
|
2684
|
+
for x in SetPartitions_set(frozenset(range(1, n + 1))):
|
|
2685
|
+
yield self.element_class(self, list(x))
|
|
2686
|
+
n += 1
|
|
2687
|
+
|
|
2688
|
+
|
|
2689
|
+
class SetPartitions_set(SetPartitions):
|
|
2690
|
+
"""
|
|
2691
|
+
Set partitions of a fixed set `S`.
|
|
2692
|
+
"""
|
|
2693
|
+
@staticmethod
|
|
2694
|
+
def __classcall_private__(cls, s):
|
|
2695
|
+
"""
|
|
2696
|
+
Normalize ``s`` to ensure a unique representation.
|
|
2697
|
+
|
|
2698
|
+
EXAMPLES::
|
|
2699
|
+
|
|
2700
|
+
sage: S1 = SetPartitions(set([2,1,4]))
|
|
2701
|
+
sage: S2 = SetPartitions([4,1,2])
|
|
2702
|
+
sage: S3 = SetPartitions((1,2,4))
|
|
2703
|
+
sage: S1 is S2, S1 is S3
|
|
2704
|
+
(True, True)
|
|
2705
|
+
"""
|
|
2706
|
+
return super().__classcall__(cls, frozenset(s))
|
|
2707
|
+
|
|
2708
|
+
def __init__(self, s):
|
|
2709
|
+
"""
|
|
2710
|
+
Initialize ``self``.
|
|
2711
|
+
|
|
2712
|
+
EXAMPLES::
|
|
2713
|
+
|
|
2714
|
+
sage: S = SetPartitions(3)
|
|
2715
|
+
sage: TestSuite(S).run()
|
|
2716
|
+
sage: SetPartitions(0).list()
|
|
2717
|
+
[{}]
|
|
2718
|
+
sage: SetPartitions([]).list()
|
|
2719
|
+
[{}]
|
|
2720
|
+
"""
|
|
2721
|
+
self._set = s
|
|
2722
|
+
SetPartitions.__init__(self, category=FiniteEnumeratedSets())
|
|
2723
|
+
|
|
2724
|
+
def _repr_(self):
|
|
2725
|
+
"""
|
|
2726
|
+
TESTS::
|
|
2727
|
+
|
|
2728
|
+
sage: SetPartitions([1,2,3])
|
|
2729
|
+
Set partitions of {1, 2, 3}
|
|
2730
|
+
"""
|
|
2731
|
+
return "Set partitions of %s" % (Set(self._set))
|
|
2732
|
+
|
|
2733
|
+
def __contains__(self, x):
|
|
2734
|
+
"""
|
|
2735
|
+
TESTS::
|
|
2736
|
+
|
|
2737
|
+
sage: S = SetPartitions(4, [2,2])
|
|
2738
|
+
sage: all(sp in S for sp in S) # needs sage.graphs sage.rings.finite_rings
|
|
2739
|
+
True
|
|
2740
|
+
sage: SetPartition([[1,3],[2,4]]) in SetPartitions(3) # needs sage.graphs
|
|
2741
|
+
False
|
|
2742
|
+
sage: SetPartition([[1,3],[2,4]]) in SetPartitions(4, [3,1]) # needs sage.graphs
|
|
2743
|
+
False
|
|
2744
|
+
sage: SetPartition([[2],[1,3,4]]) in SetPartitions(4, [3,1]) # needs sage.graphs
|
|
2745
|
+
True
|
|
2746
|
+
"""
|
|
2747
|
+
# Must pass the general check
|
|
2748
|
+
if not SetPartitions.__contains__(self, x):
|
|
2749
|
+
return False
|
|
2750
|
+
|
|
2751
|
+
# Make sure that the number of elements match up
|
|
2752
|
+
if sum(map(len, x)) != len(self._set):
|
|
2753
|
+
return False
|
|
2754
|
+
|
|
2755
|
+
# Make sure that the union of all the sets is the original set
|
|
2756
|
+
return Set(e for p in x for e in p) == Set(self._set)
|
|
2757
|
+
|
|
2758
|
+
def random_element(self):
|
|
2759
|
+
r"""
|
|
2760
|
+
Return a random set partition.
|
|
2761
|
+
|
|
2762
|
+
This is a very naive implementation of Knuths outline in F3B,
|
|
2763
|
+
7.2.1.5.
|
|
2764
|
+
|
|
2765
|
+
EXAMPLES::
|
|
2766
|
+
|
|
2767
|
+
sage: S = SetPartitions(10)
|
|
2768
|
+
sage: s = S.random_element() # needs sage.symbolic
|
|
2769
|
+
sage: s.parent() is S # needs sage.symbolic
|
|
2770
|
+
True
|
|
2771
|
+
sage: assert s in S, s # needs sage.symbolic
|
|
2772
|
+
|
|
2773
|
+
sage: S = SetPartitions(["a", "b", "c"])
|
|
2774
|
+
sage: s = S.random_element() # needs sage.symbolic
|
|
2775
|
+
sage: s.parent() is S # needs sage.symbolic
|
|
2776
|
+
True
|
|
2777
|
+
sage: assert s in S, s # needs sage.symbolic
|
|
2778
|
+
"""
|
|
2779
|
+
base_set = list(self.base_set())
|
|
2780
|
+
N = len(base_set)
|
|
2781
|
+
from sage.symbolic.constants import e
|
|
2782
|
+
c = float(e) * bell_number(N)
|
|
2783
|
+
# it would be much better to generate M in the way Knuth
|
|
2784
|
+
# recommends, the following is a waste
|
|
2785
|
+
G = GeneralDiscreteDistribution([float(m)**N / (c * factorial(m)) for m in range(4 * N)])
|
|
2786
|
+
M = G.get_random_element() - 1
|
|
2787
|
+
l = (randint(0, M) for i in range(N))
|
|
2788
|
+
p = {}
|
|
2789
|
+
for i, b in enumerate(l):
|
|
2790
|
+
if b in p:
|
|
2791
|
+
p[b].append(base_set[i])
|
|
2792
|
+
else:
|
|
2793
|
+
p[b] = [base_set[i]]
|
|
2794
|
+
|
|
2795
|
+
return self.element_class(self, p.values(), check=False)
|
|
2796
|
+
|
|
2797
|
+
def cardinality(self):
|
|
2798
|
+
"""
|
|
2799
|
+
Return the number of set partitions of the set `S`.
|
|
2800
|
+
|
|
2801
|
+
The cardinality is given by the `n`-th Bell number where `n` is the
|
|
2802
|
+
number of elements in the set `S`.
|
|
2803
|
+
|
|
2804
|
+
EXAMPLES::
|
|
2805
|
+
|
|
2806
|
+
sage: # needs sage.libs.flint
|
|
2807
|
+
sage: SetPartitions([1,2,3,4]).cardinality()
|
|
2808
|
+
15
|
|
2809
|
+
sage: SetPartitions(3).cardinality()
|
|
2810
|
+
5
|
|
2811
|
+
sage: SetPartitions(3,2).cardinality()
|
|
2812
|
+
3
|
|
2813
|
+
sage: SetPartitions([]).cardinality()
|
|
2814
|
+
1
|
|
2815
|
+
"""
|
|
2816
|
+
return bell_number(len(self._set))
|
|
2817
|
+
|
|
2818
|
+
def __iter__(self):
|
|
2819
|
+
"""
|
|
2820
|
+
Iterate over ``self``.
|
|
2821
|
+
|
|
2822
|
+
EXAMPLES::
|
|
2823
|
+
|
|
2824
|
+
sage: SetPartitions(3).list()
|
|
2825
|
+
[{{1, 2, 3}}, {{1, 2}, {3}}, {{1, 3}, {2}}, {{1}, {2, 3}}, {{1}, {2}, {3}}]
|
|
2826
|
+
|
|
2827
|
+
sage: SetPartitions(["a", "b"]).list()
|
|
2828
|
+
[{{'a', 'b'}}, {{'a'}, {'b'}}]
|
|
2829
|
+
"""
|
|
2830
|
+
try:
|
|
2831
|
+
s = sorted(self._set)
|
|
2832
|
+
except TypeError:
|
|
2833
|
+
s = sorted(self._set, key=str)
|
|
2834
|
+
for sp in set_partition_iterator(s):
|
|
2835
|
+
yield self.element_class(self, sp, check=False)
|
|
2836
|
+
|
|
2837
|
+
def base_set(self):
|
|
2838
|
+
"""
|
|
2839
|
+
Return the base set of ``self``.
|
|
2840
|
+
|
|
2841
|
+
EXAMPLES::
|
|
2842
|
+
|
|
2843
|
+
sage: SetPartitions(3).base_set()
|
|
2844
|
+
{1, 2, 3}
|
|
2845
|
+
|
|
2846
|
+
sage: sorted(SetPartitions(["a", "b", "c"]).base_set())
|
|
2847
|
+
['a', 'b', 'c']
|
|
2848
|
+
"""
|
|
2849
|
+
return Set(self._set)
|
|
2850
|
+
|
|
2851
|
+
def base_set_cardinality(self):
|
|
2852
|
+
"""
|
|
2853
|
+
Return the cardinality of the base set of ``self``.
|
|
2854
|
+
|
|
2855
|
+
EXAMPLES::
|
|
2856
|
+
|
|
2857
|
+
sage: SetPartitions(3).base_set_cardinality()
|
|
2858
|
+
3
|
|
2859
|
+
"""
|
|
2860
|
+
return len(self._set)
|
|
2861
|
+
|
|
2862
|
+
|
|
2863
|
+
class SetPartitions_setparts(SetPartitions_set):
|
|
2864
|
+
r"""
|
|
2865
|
+
Set partitions with fixed partition sizes corresponding to an
|
|
2866
|
+
integer partition `\lambda`.
|
|
2867
|
+
"""
|
|
2868
|
+
@staticmethod
|
|
2869
|
+
def __classcall_private__(cls, s, parts):
|
|
2870
|
+
"""
|
|
2871
|
+
Normalize input to ensure a unique representation.
|
|
2872
|
+
|
|
2873
|
+
EXAMPLES::
|
|
2874
|
+
|
|
2875
|
+
sage: S = SetPartitions(4, [2,2])
|
|
2876
|
+
sage: T = SetPartitions([1,2,3,4], Partition([2,2]))
|
|
2877
|
+
sage: S is T
|
|
2878
|
+
True
|
|
2879
|
+
|
|
2880
|
+
sage: S = SetPartitions(4, [3,1])
|
|
2881
|
+
sage: T = SetPartitions(4, (1,3))
|
|
2882
|
+
sage: S is T
|
|
2883
|
+
True
|
|
2884
|
+
"""
|
|
2885
|
+
if isinstance(s, (int, Integer)):
|
|
2886
|
+
s = list(range(1, s + 1))
|
|
2887
|
+
return super().__classcall__(cls, frozenset(s), Partition(parts))
|
|
2888
|
+
|
|
2889
|
+
def __init__(self, s, parts):
|
|
2890
|
+
"""
|
|
2891
|
+
Initialize the data structure.
|
|
2892
|
+
|
|
2893
|
+
We can assume here that ``parts`` is a :class:`Partition`.
|
|
2894
|
+
|
|
2895
|
+
TESTS::
|
|
2896
|
+
|
|
2897
|
+
sage: S = SetPartitions(4, [2,2])
|
|
2898
|
+
sage: TestSuite(S).run() # needs sage.graphs sage.libs.flint
|
|
2899
|
+
"""
|
|
2900
|
+
SetPartitions_set.__init__(self, s)
|
|
2901
|
+
self._parts = parts
|
|
2902
|
+
|
|
2903
|
+
def _repr_(self):
|
|
2904
|
+
"""
|
|
2905
|
+
TESTS::
|
|
2906
|
+
|
|
2907
|
+
sage: SetPartitions(4, [2,2])
|
|
2908
|
+
Set partitions of {1, 2, 3, 4} with sizes in [2, 2]
|
|
2909
|
+
"""
|
|
2910
|
+
return "Set partitions of %s with sizes in %s" % (Set(self._set), self._parts)
|
|
2911
|
+
|
|
2912
|
+
def shape(self):
|
|
2913
|
+
r"""
|
|
2914
|
+
Return the partition of block sizes of the set partitions in ``self``.
|
|
2915
|
+
|
|
2916
|
+
EXAMPLES::
|
|
2917
|
+
|
|
2918
|
+
sage: SetPartitions(5, [2,2,1]).shape()
|
|
2919
|
+
[2, 2, 1]
|
|
2920
|
+
"""
|
|
2921
|
+
return self._parts
|
|
2922
|
+
|
|
2923
|
+
def cardinality(self):
|
|
2924
|
+
r"""
|
|
2925
|
+
Return the cardinality of ``self``.
|
|
2926
|
+
|
|
2927
|
+
This algorithm counts for each block of the partition the
|
|
2928
|
+
number of ways to fill it using values from the set. Then,
|
|
2929
|
+
for each distinct value `v` of block size, we divide the result by
|
|
2930
|
+
the number of ways to arrange the blocks of size `v` in the
|
|
2931
|
+
set partition.
|
|
2932
|
+
|
|
2933
|
+
For example, if we want to count the number of set partitions
|
|
2934
|
+
of size 13 having [3,3,3,2,2] as underlying partition we
|
|
2935
|
+
compute the number of ways to fill each block of the
|
|
2936
|
+
partition, which is `\binom{13}{3} \binom{10}{3} \binom{7}{3}
|
|
2937
|
+
\binom{4}{2}\binom{2}{2}` and as we have three blocks of size
|
|
2938
|
+
`3` and two blocks of size `2`, we divide the result by
|
|
2939
|
+
`3!2!` which gives us `600600`.
|
|
2940
|
+
|
|
2941
|
+
EXAMPLES::
|
|
2942
|
+
|
|
2943
|
+
sage: SetPartitions(3, [2,1]).cardinality()
|
|
2944
|
+
3
|
|
2945
|
+
sage: SetPartitions(13, Partition([3,3,3,2,2])).cardinality()
|
|
2946
|
+
600600
|
|
2947
|
+
|
|
2948
|
+
TESTS::
|
|
2949
|
+
|
|
2950
|
+
sage: all((len(SetPartitions(size, part)) == SetPartitions(size, part).cardinality()
|
|
2951
|
+
....: for size in range(8) for part in Partitions(size)))
|
|
2952
|
+
True
|
|
2953
|
+
sage: sum((SetPartitions(13, p).cardinality() # needs sage.libs.flint
|
|
2954
|
+
....: for p in Partitions(13))) == SetPartitions(13).cardinality()
|
|
2955
|
+
True
|
|
2956
|
+
"""
|
|
2957
|
+
from sage.misc.misc_c import prod
|
|
2958
|
+
|
|
2959
|
+
remaining_subset_size = Integer(len(self._set))
|
|
2960
|
+
cardinal = Integer(1)
|
|
2961
|
+
for subset_size in self._parts:
|
|
2962
|
+
cardinal *= remaining_subset_size.binomial(subset_size)
|
|
2963
|
+
remaining_subset_size -= subset_size
|
|
2964
|
+
|
|
2965
|
+
repetitions = (Integer(rep).factorial()
|
|
2966
|
+
for rep in self._parts.to_exp_dict().values()
|
|
2967
|
+
if rep != 1)
|
|
2968
|
+
cardinal /= prod(repetitions)
|
|
2969
|
+
return Integer(cardinal)
|
|
2970
|
+
|
|
2971
|
+
def _set_partition_poset(self):
|
|
2972
|
+
r"""
|
|
2973
|
+
Return the Hasse diagram of a poset whose linear extensions correspond
|
|
2974
|
+
to the set partitions with specified block sizes.
|
|
2975
|
+
|
|
2976
|
+
TESTS::
|
|
2977
|
+
|
|
2978
|
+
sage: P = SetPartitions(["a", "b", "c", "d", "e"], # needs sage.graphs
|
|
2979
|
+
....: [2,2,1])._set_partition_poset()
|
|
2980
|
+
sage: P.cover_relations() # needs sage.graphs
|
|
2981
|
+
[(1, 2), (1, 3), (3, 4)]
|
|
2982
|
+
|
|
2983
|
+
sage: n = 9
|
|
2984
|
+
sage: all(SetPartitions(n, mu).cardinality() == # needs sage.graphs sage.modules
|
|
2985
|
+
....: len(list(SetPartitions(n, mu)._set_partition_poset().linear_extensions()))
|
|
2986
|
+
....: for mu in Partitions(n))
|
|
2987
|
+
True
|
|
2988
|
+
"""
|
|
2989
|
+
c = self._parts.to_exp_dict()
|
|
2990
|
+
covers = {}
|
|
2991
|
+
i = 0
|
|
2992
|
+
for s in sorted(c):
|
|
2993
|
+
# s is the block size
|
|
2994
|
+
# each block is one tree in the poset
|
|
2995
|
+
for m in range(c[s]):
|
|
2996
|
+
# m is the multiplicity of blocks with size s
|
|
2997
|
+
#
|
|
2998
|
+
# the first element in each non-final block has an
|
|
2999
|
+
# additional cover
|
|
3000
|
+
first = i
|
|
3001
|
+
if s == 1:
|
|
3002
|
+
covers[i] = []
|
|
3003
|
+
else:
|
|
3004
|
+
for _ in range(s - 1):
|
|
3005
|
+
covers[i] = [i + 1]
|
|
3006
|
+
i += 1
|
|
3007
|
+
i += 1
|
|
3008
|
+
if m < c[s] - 1:
|
|
3009
|
+
covers[first].append(i)
|
|
3010
|
+
return HasseDiagram(covers)
|
|
3011
|
+
|
|
3012
|
+
def __iter__(self):
|
|
3013
|
+
"""
|
|
3014
|
+
An iterator for all the set partitions of the given set with
|
|
3015
|
+
the given sizes.
|
|
3016
|
+
|
|
3017
|
+
EXAMPLES::
|
|
3018
|
+
|
|
3019
|
+
sage: SetPartitions(3, [2,1]).list() # needs sage.graphs sage.rings.finite_rings
|
|
3020
|
+
[{{1}, {2, 3}}, {{1, 2}, {3}}, {{1, 3}, {2}}]
|
|
3021
|
+
|
|
3022
|
+
sage: SetPartitions(["a", "b", "c"], [2,1]).list() # needs sage.graphs sage.rings.finite_rings
|
|
3023
|
+
[{{'a'}, {'b', 'c'}}, {{'a', 'b'}, {'c'}}, {{'a', 'c'}, {'b'}}]
|
|
3024
|
+
|
|
3025
|
+
TESTS::
|
|
3026
|
+
|
|
3027
|
+
sage: n = 8
|
|
3028
|
+
sage: all(SetPartitions(n, mu).cardinality() # needs sage.graphs sage.rings.finite_rings
|
|
3029
|
+
....: == len(list(SetPartitions(n, mu))) for mu in Partitions(n))
|
|
3030
|
+
True
|
|
3031
|
+
"""
|
|
3032
|
+
# Ruskey, Combinatorial Generation, sec. 5.10.1 and Knuth TAOCP 4A 7.2.1.5, Exercise 6
|
|
3033
|
+
k = len(self._parts)
|
|
3034
|
+
n = len(self._set)
|
|
3035
|
+
P = self._set_partition_poset()
|
|
3036
|
+
try:
|
|
3037
|
+
s = sorted(self._set)
|
|
3038
|
+
except TypeError:
|
|
3039
|
+
s = sorted(self._set, key=str)
|
|
3040
|
+
|
|
3041
|
+
sums = [0]
|
|
3042
|
+
for b in sorted(self._parts):
|
|
3043
|
+
sums.append(sums[-1] + b)
|
|
3044
|
+
|
|
3045
|
+
for ext in P.linear_extensions():
|
|
3046
|
+
pi = [None] * n
|
|
3047
|
+
for i in range(n):
|
|
3048
|
+
pi[ext[i]] = s[i]
|
|
3049
|
+
sp = [[pi[j] for j in range(sums[i], sums[i + 1])]
|
|
3050
|
+
for i in range(k)]
|
|
3051
|
+
yield self.element_class(self, sp, check=False)
|
|
3052
|
+
|
|
3053
|
+
def __contains__(self, x):
|
|
3054
|
+
"""
|
|
3055
|
+
Check containment.
|
|
3056
|
+
|
|
3057
|
+
TESTS::
|
|
3058
|
+
|
|
3059
|
+
sage: S = SetPartitions(4, [3,1])
|
|
3060
|
+
sage: Set([Set([1,2,3]), Set([4])]) in S
|
|
3061
|
+
True
|
|
3062
|
+
sage: Set([Set([1,3]), Set([2,4])]) in S
|
|
3063
|
+
False
|
|
3064
|
+
sage: Set([Set([1,2,3,4])]) in S
|
|
3065
|
+
False
|
|
3066
|
+
"""
|
|
3067
|
+
if not SetPartitions_set.__contains__(self, x):
|
|
3068
|
+
return False
|
|
3069
|
+
return sorted(map(len, x), reverse=True) == self._parts
|
|
3070
|
+
|
|
3071
|
+
def random_element(self):
|
|
3072
|
+
r"""
|
|
3073
|
+
Return a random set partition of ``self``.
|
|
3074
|
+
|
|
3075
|
+
ALGORITHM:
|
|
3076
|
+
|
|
3077
|
+
Based on the cardinality method. For each block size `k_i`,
|
|
3078
|
+
we choose a uniformly random subset `X_i \subseteq S_i` of
|
|
3079
|
+
size `k_i` of the elements `S_i` that have not yet been selected.
|
|
3080
|
+
Thus, we define `S_{i+1} = S_i \setminus X_i` with `S_i = S`
|
|
3081
|
+
being the defining set. This is not yet proven to be uniformly
|
|
3082
|
+
distributed, but numerical tests show this is likely uniform.
|
|
3083
|
+
|
|
3084
|
+
EXAMPLES::
|
|
3085
|
+
|
|
3086
|
+
sage: S = SetPartitions(10, [4,3,2,1])
|
|
3087
|
+
sage: s = S.random_element()
|
|
3088
|
+
sage: s.parent() is S
|
|
3089
|
+
True
|
|
3090
|
+
sage: assert s in S, s
|
|
3091
|
+
|
|
3092
|
+
sage: S = SetPartitions(["a", "b", "c", "d"], [2,2])
|
|
3093
|
+
sage: s = S.random_element()
|
|
3094
|
+
sage: s.parent() is S
|
|
3095
|
+
True
|
|
3096
|
+
sage: assert s in S, s
|
|
3097
|
+
"""
|
|
3098
|
+
base_set = list(self.base_set())
|
|
3099
|
+
N = len(base_set)
|
|
3100
|
+
ret = []
|
|
3101
|
+
for p in self._parts:
|
|
3102
|
+
X = sample(range(N), p)
|
|
3103
|
+
ret.append([base_set[i] for i in X])
|
|
3104
|
+
for i in sorted(X, reverse=True):
|
|
3105
|
+
del base_set[i]
|
|
3106
|
+
N -= p
|
|
3107
|
+
|
|
3108
|
+
return self.element_class(self, ret, check=False)
|
|
3109
|
+
|
|
3110
|
+
|
|
3111
|
+
class SetPartitions_setn(SetPartitions_set):
|
|
3112
|
+
"""
|
|
3113
|
+
Set partitions with a given number of blocks.
|
|
3114
|
+
"""
|
|
3115
|
+
@staticmethod
|
|
3116
|
+
def __classcall_private__(cls, s, k):
|
|
3117
|
+
"""
|
|
3118
|
+
Normalize ``s`` to ensure a unique representation.
|
|
3119
|
+
|
|
3120
|
+
EXAMPLES::
|
|
3121
|
+
|
|
3122
|
+
sage: S1 = SetPartitions(set([2,1,4]), 2)
|
|
3123
|
+
sage: S2 = SetPartitions([4,1,2], 2)
|
|
3124
|
+
sage: S3 = SetPartitions((1,2,4), 2)
|
|
3125
|
+
sage: S1 is S2, S1 is S3
|
|
3126
|
+
(True, True)
|
|
3127
|
+
"""
|
|
3128
|
+
return super().__classcall__(cls, frozenset(s), k)
|
|
3129
|
+
|
|
3130
|
+
def __init__(self, s, k):
|
|
3131
|
+
"""
|
|
3132
|
+
TESTS::
|
|
3133
|
+
|
|
3134
|
+
sage: S = SetPartitions(5, 3)
|
|
3135
|
+
sage: TestSuite(S).run()
|
|
3136
|
+
"""
|
|
3137
|
+
self._k = k
|
|
3138
|
+
SetPartitions_set.__init__(self, s)
|
|
3139
|
+
|
|
3140
|
+
def _repr_(self):
|
|
3141
|
+
"""
|
|
3142
|
+
TESTS::
|
|
3143
|
+
|
|
3144
|
+
sage: SetPartitions(5, 3)
|
|
3145
|
+
Set partitions of {1, 2, 3, 4, 5} with 3 parts
|
|
3146
|
+
"""
|
|
3147
|
+
return "Set partitions of %s with %s parts" % (Set(self._set), self._k)
|
|
3148
|
+
|
|
3149
|
+
def number_of_blocks(self):
|
|
3150
|
+
r"""
|
|
3151
|
+
Return the number of blocks of the set partitions in ``self``.
|
|
3152
|
+
|
|
3153
|
+
EXAMPLES::
|
|
3154
|
+
|
|
3155
|
+
sage: SetPartitions(5, 3).number_of_blocks()
|
|
3156
|
+
3
|
|
3157
|
+
"""
|
|
3158
|
+
return self._k
|
|
3159
|
+
|
|
3160
|
+
def cardinality(self):
|
|
3161
|
+
"""
|
|
3162
|
+
The Stirling number of the second kind is the number of partitions
|
|
3163
|
+
of a set of size `n` into `k` blocks.
|
|
3164
|
+
|
|
3165
|
+
EXAMPLES::
|
|
3166
|
+
|
|
3167
|
+
sage: SetPartitions(5, 3).cardinality()
|
|
3168
|
+
25
|
|
3169
|
+
sage: stirling_number2(5,3)
|
|
3170
|
+
25
|
|
3171
|
+
"""
|
|
3172
|
+
return stirling2(len(self._set), self._k)
|
|
3173
|
+
|
|
3174
|
+
def __iter__(self):
|
|
3175
|
+
"""
|
|
3176
|
+
Iterate over ``self``.
|
|
3177
|
+
|
|
3178
|
+
EXAMPLES::
|
|
3179
|
+
|
|
3180
|
+
sage: SetPartitions(4, 2).list()
|
|
3181
|
+
[{{1, 3, 4}, {2}},
|
|
3182
|
+
{{1, 4}, {2, 3}},
|
|
3183
|
+
{{1, 2, 4}, {3}},
|
|
3184
|
+
{{1, 3}, {2, 4}},
|
|
3185
|
+
{{1}, {2, 3, 4}},
|
|
3186
|
+
{{1, 2}, {3, 4}},
|
|
3187
|
+
{{1, 2, 3}, {4}}]
|
|
3188
|
+
|
|
3189
|
+
sage: SetPartitions(["a", "b", "c"], 2).list()
|
|
3190
|
+
[{{'a', 'c'}, {'b'}}, {{'a'}, {'b', 'c'}}, {{'a', 'b'}, {'c'}}]
|
|
3191
|
+
"""
|
|
3192
|
+
try:
|
|
3193
|
+
s = sorted(self._set)
|
|
3194
|
+
except TypeError:
|
|
3195
|
+
s = sorted(self._set, key=str)
|
|
3196
|
+
for sp in set_partition_iterator_blocks(s, self._k):
|
|
3197
|
+
yield self.element_class(self, sp, check=False)
|
|
3198
|
+
|
|
3199
|
+
def __contains__(self, x):
|
|
3200
|
+
"""
|
|
3201
|
+
Check containment.
|
|
3202
|
+
|
|
3203
|
+
TESTS::
|
|
3204
|
+
|
|
3205
|
+
sage: S = SetPartitions(4, 2)
|
|
3206
|
+
sage: Set([Set([1,2,3]), Set([4])]) in S
|
|
3207
|
+
True
|
|
3208
|
+
sage: Set([Set([1,3]), Set([2,4])]) in S
|
|
3209
|
+
True
|
|
3210
|
+
sage: Set([Set([1,2,3,4])]) in S
|
|
3211
|
+
False
|
|
3212
|
+
"""
|
|
3213
|
+
if not SetPartitions_set.__contains__(self, x):
|
|
3214
|
+
return False
|
|
3215
|
+
return len(x) == self._k
|
|
3216
|
+
|
|
3217
|
+
def random_element(self):
|
|
3218
|
+
r"""
|
|
3219
|
+
Return a random set partition of ``self``.
|
|
3220
|
+
|
|
3221
|
+
See https://mathoverflow.net/questions/141999.
|
|
3222
|
+
|
|
3223
|
+
EXAMPLES::
|
|
3224
|
+
|
|
3225
|
+
sage: S = SetPartitions(10, 4)
|
|
3226
|
+
sage: s = S.random_element()
|
|
3227
|
+
sage: s.parent() is S
|
|
3228
|
+
True
|
|
3229
|
+
sage: assert s in S, s
|
|
3230
|
+
|
|
3231
|
+
sage: S = SetPartitions(["a", "b", "c"], 2)
|
|
3232
|
+
sage: s = S.random_element()
|
|
3233
|
+
sage: s.parent() is S
|
|
3234
|
+
True
|
|
3235
|
+
sage: assert s in S, s
|
|
3236
|
+
"""
|
|
3237
|
+
def re(N, k):
|
|
3238
|
+
if N == 0:
|
|
3239
|
+
return [[]]
|
|
3240
|
+
if N == 1:
|
|
3241
|
+
return [[0]]
|
|
3242
|
+
if stirling2(N - 1, k - 1) > random() * stirling2(N, k):
|
|
3243
|
+
return [[N - 1]] + re(N - 1, k - 1)
|
|
3244
|
+
|
|
3245
|
+
p = re(N - 1, k)
|
|
3246
|
+
p[randint(0, len(p) - 1)].append(N - 1)
|
|
3247
|
+
return p
|
|
3248
|
+
|
|
3249
|
+
base_set = list(self.base_set())
|
|
3250
|
+
N = len(base_set)
|
|
3251
|
+
k = self._k
|
|
3252
|
+
p = re(N, k)
|
|
3253
|
+
return self.element_class(self, [[base_set[e] for e in b] for b in p], check=False)
|
|
3254
|
+
|
|
3255
|
+
|
|
3256
|
+
def cyclic_permutations_of_set_partition(set_part):
|
|
3257
|
+
"""
|
|
3258
|
+
Return all combinations of cyclic permutations of each cell of the
|
|
3259
|
+
set partition.
|
|
3260
|
+
|
|
3261
|
+
AUTHORS:
|
|
3262
|
+
|
|
3263
|
+
- Robert L. Miller
|
|
3264
|
+
|
|
3265
|
+
EXAMPLES::
|
|
3266
|
+
|
|
3267
|
+
sage: from sage.combinat.set_partition import cyclic_permutations_of_set_partition
|
|
3268
|
+
sage: cyclic_permutations_of_set_partition([[1,2,3,4],[5,6,7]])
|
|
3269
|
+
[[[1, 2, 3, 4], [5, 6, 7]],
|
|
3270
|
+
[[1, 2, 4, 3], [5, 6, 7]],
|
|
3271
|
+
[[1, 3, 2, 4], [5, 6, 7]],
|
|
3272
|
+
[[1, 3, 4, 2], [5, 6, 7]],
|
|
3273
|
+
[[1, 4, 2, 3], [5, 6, 7]],
|
|
3274
|
+
[[1, 4, 3, 2], [5, 6, 7]],
|
|
3275
|
+
[[1, 2, 3, 4], [5, 7, 6]],
|
|
3276
|
+
[[1, 2, 4, 3], [5, 7, 6]],
|
|
3277
|
+
[[1, 3, 2, 4], [5, 7, 6]],
|
|
3278
|
+
[[1, 3, 4, 2], [5, 7, 6]],
|
|
3279
|
+
[[1, 4, 2, 3], [5, 7, 6]],
|
|
3280
|
+
[[1, 4, 3, 2], [5, 7, 6]]]
|
|
3281
|
+
"""
|
|
3282
|
+
return list(cyclic_permutations_of_set_partition_iterator(set_part))
|
|
3283
|
+
|
|
3284
|
+
|
|
3285
|
+
def cyclic_permutations_of_set_partition_iterator(set_part):
|
|
3286
|
+
"""
|
|
3287
|
+
Iterate over all combinations of cyclic permutations of each cell
|
|
3288
|
+
of the set partition.
|
|
3289
|
+
|
|
3290
|
+
AUTHORS:
|
|
3291
|
+
|
|
3292
|
+
- Robert L. Miller
|
|
3293
|
+
|
|
3294
|
+
EXAMPLES::
|
|
3295
|
+
|
|
3296
|
+
sage: from sage.combinat.set_partition import cyclic_permutations_of_set_partition_iterator
|
|
3297
|
+
sage: list(cyclic_permutations_of_set_partition_iterator([[1,2,3,4],[5,6,7]]))
|
|
3298
|
+
[[[1, 2, 3, 4], [5, 6, 7]],
|
|
3299
|
+
[[1, 2, 4, 3], [5, 6, 7]],
|
|
3300
|
+
[[1, 3, 2, 4], [5, 6, 7]],
|
|
3301
|
+
[[1, 3, 4, 2], [5, 6, 7]],
|
|
3302
|
+
[[1, 4, 2, 3], [5, 6, 7]],
|
|
3303
|
+
[[1, 4, 3, 2], [5, 6, 7]],
|
|
3304
|
+
[[1, 2, 3, 4], [5, 7, 6]],
|
|
3305
|
+
[[1, 2, 4, 3], [5, 7, 6]],
|
|
3306
|
+
[[1, 3, 2, 4], [5, 7, 6]],
|
|
3307
|
+
[[1, 3, 4, 2], [5, 7, 6]],
|
|
3308
|
+
[[1, 4, 2, 3], [5, 7, 6]],
|
|
3309
|
+
[[1, 4, 3, 2], [5, 7, 6]]]
|
|
3310
|
+
"""
|
|
3311
|
+
from sage.combinat.permutation import CyclicPermutations
|
|
3312
|
+
if len(set_part) == 1:
|
|
3313
|
+
for i in CyclicPermutations(set_part[0]):
|
|
3314
|
+
yield [i]
|
|
3315
|
+
else:
|
|
3316
|
+
for right in cyclic_permutations_of_set_partition_iterator(set_part[1:]):
|
|
3317
|
+
for perm in CyclicPermutations(set_part[0]):
|
|
3318
|
+
yield [perm] + right
|