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,3541 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-combinat
|
|
2
|
+
r"""
|
|
3
|
+
Ordered multiset partitions into sets and the minimaj crystal
|
|
4
|
+
|
|
5
|
+
This module provides element and parent classes for ordered multiset
|
|
6
|
+
partitions. It also implements the minimaj crystal of Benkart et al.
|
|
7
|
+
[BCHOPSY2017]_. (See :class:`MinimajCrystal`.)
|
|
8
|
+
|
|
9
|
+
REFERENCES:
|
|
10
|
+
|
|
11
|
+
- [BCHOPSY2017]_
|
|
12
|
+
- [HRW2015]_
|
|
13
|
+
- [HRS2016]_
|
|
14
|
+
- [LM2018]_
|
|
15
|
+
|
|
16
|
+
EXAMPLES:
|
|
17
|
+
|
|
18
|
+
An ordered multiset partition into sets of the multiset `\{\{1, 3, 3, 5\}\}`::
|
|
19
|
+
|
|
20
|
+
sage: OrderedMultisetPartitionIntoSets([[5, 3], [1, 3]])
|
|
21
|
+
[{3,5}, {1,3}]
|
|
22
|
+
|
|
23
|
+
Ordered multiset partitions into sets of the multiset `\{\{1, 3, 3\}\}`::
|
|
24
|
+
|
|
25
|
+
sage: OrderedMultisetPartitionsIntoSets([1,1,3]).list()
|
|
26
|
+
[[{1}, {1}, {3}], [{1}, {1,3}], [{1}, {3}, {1}], [{1,3}, {1}], [{3}, {1}, {1}]]
|
|
27
|
+
|
|
28
|
+
Ordered multiset partitions into sets of the integer 4::
|
|
29
|
+
|
|
30
|
+
sage: OrderedMultisetPartitionsIntoSets(4).list()
|
|
31
|
+
[[{4}], [{1,3}], [{3}, {1}], [{1,2}, {1}], [{2}, {2}], [{2}, {1}, {1}],
|
|
32
|
+
[{1}, {3}], [{1}, {1,2}], [{1}, {2}, {1}], [{1}, {1}, {2}], [{1}, {1}, {1}, {1}]]
|
|
33
|
+
|
|
34
|
+
Ordered multiset partitions into sets on the alphabet `\{1, 4\}` of order 3::
|
|
35
|
+
|
|
36
|
+
sage: OrderedMultisetPartitionsIntoSets([1,4], 3).list()
|
|
37
|
+
[[{1,4}, {1}], [{1,4}, {4}], [{1}, {1,4}], [{4}, {1,4}], [{1}, {1}, {1}],
|
|
38
|
+
[{1}, {1}, {4}], [{1}, {4}, {1}], [{1}, {4}, {4}], [{4}, {1}, {1}],
|
|
39
|
+
[{4}, {1}, {4}], [{4}, {4}, {1}], [{4}, {4}, {4}]]
|
|
40
|
+
|
|
41
|
+
Crystal of ordered multiset partitions into sets on the alphabet `\{1,2,3\}`
|
|
42
|
+
with 4 letters divided into 2 blocks::
|
|
43
|
+
|
|
44
|
+
sage: crystals.Minimaj(3, 4, 2).list() # needs sage.modules
|
|
45
|
+
[((2, 3, 1), (1,)), ((2, 3), (1, 2)), ((2, 3), (1, 3)), ((2, 1), (1, 2)),
|
|
46
|
+
((3, 1), (1, 2)), ((3, 1, 2), (2,)), ((3, 1), (1, 3)), ((3, 1), (2, 3)),
|
|
47
|
+
((3, 2), (2, 3)), ((2, 1), (1, 3)), ((2,), (1, 2, 3)), ((3,), (1, 2, 3)),
|
|
48
|
+
((1,), (1, 2, 3)), ((1, 2), (2, 3)), ((1, 2, 3), (3,))]
|
|
49
|
+
|
|
50
|
+
AUTHORS:
|
|
51
|
+
|
|
52
|
+
- Aaron Lauve (2018): initial implementation. First draft of minimaj crystal
|
|
53
|
+
code provided by Anne Schilling.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
# ****************************************************************************
|
|
57
|
+
# Copyright (C) 2018 Aaron Lauve <lauve at math.luc.edu>
|
|
58
|
+
#
|
|
59
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
60
|
+
# as published by the Free Software Foundation; either version 2 of
|
|
61
|
+
# the License, or (at your option) any later version.
|
|
62
|
+
# https://www.gnu.org/licenses/
|
|
63
|
+
# ****************************************************************************
|
|
64
|
+
from functools import reduce
|
|
65
|
+
from itertools import chain, product
|
|
66
|
+
|
|
67
|
+
from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets
|
|
68
|
+
from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
|
|
69
|
+
from sage.categories.classical_crystals import ClassicalCrystals
|
|
70
|
+
from sage.categories.tensor import tensor
|
|
71
|
+
from sage.structure.unique_representation import UniqueRepresentation
|
|
72
|
+
from sage.structure.list_clone import ClonableArray
|
|
73
|
+
from sage.structure.parent import Parent
|
|
74
|
+
from sage.structure.element_wrapper import ElementWrapper
|
|
75
|
+
from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass
|
|
76
|
+
from sage.misc.misc_c import prod, running_total
|
|
77
|
+
from sage.misc.latex import latex
|
|
78
|
+
from sage.misc.lazy_import import lazy_import
|
|
79
|
+
from sage.sets.set import Set_object
|
|
80
|
+
from sage.rings.infinity import infinity
|
|
81
|
+
from sage.rings.integer_ring import ZZ
|
|
82
|
+
from sage.rings.power_series_ring import PowerSeriesRing
|
|
83
|
+
from sage.arith.misc import binomial
|
|
84
|
+
|
|
85
|
+
from sage.combinat.subset import Subsets_sk
|
|
86
|
+
from sage.combinat.composition import Composition, Compositions, composition_iterator_fast
|
|
87
|
+
from sage.combinat.permutation import Permutations_mset
|
|
88
|
+
from sage.combinat.integer_lists.invlex import IntegerListsLex
|
|
89
|
+
from sage.combinat.combinatorial_map import combinatorial_map
|
|
90
|
+
from sage.combinat.shuffle import ShuffleProduct, ShuffleProduct_overlapping
|
|
91
|
+
|
|
92
|
+
lazy_import('sage.combinat.crystals.letters', 'CrystalOfLetters', as_='Letters')
|
|
93
|
+
lazy_import('sage.combinat.root_system.cartan_type', 'CartanType')
|
|
94
|
+
lazy_import('sage.combinat.sf.sf', 'SymmetricFunctions')
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class OrderedMultisetPartitionIntoSets(ClonableArray,
|
|
98
|
+
metaclass=InheritComparisonClasscallMetaclass):
|
|
99
|
+
r"""
|
|
100
|
+
Ordered Multiset Partition into sets.
|
|
101
|
+
|
|
102
|
+
An *ordered multiset partition into sets* `c` of a multiset `X` is a list
|
|
103
|
+
`[c_1, \ldots, c_r]` of nonempty subsets of `X` (note: not
|
|
104
|
+
sub-multisets), called the *blocks* of `c`, whose multi-union is `X`.
|
|
105
|
+
|
|
106
|
+
EXAMPLES:
|
|
107
|
+
|
|
108
|
+
The simplest way to create an ordered multiset partition into sets is by
|
|
109
|
+
specifying its blocks as a list or tuple::
|
|
110
|
+
|
|
111
|
+
sage: OrderedMultisetPartitionIntoSets([[3],[2,1]])
|
|
112
|
+
[{3}, {1,2}]
|
|
113
|
+
sage: OrderedMultisetPartitionIntoSets(((3,), (1,2)))
|
|
114
|
+
[{3}, {1,2}]
|
|
115
|
+
sage: OrderedMultisetPartitionIntoSets([set([i]) for i in range(2,5)])
|
|
116
|
+
[{2}, {3}, {4}]
|
|
117
|
+
|
|
118
|
+
REFERENCES:
|
|
119
|
+
|
|
120
|
+
- [HRW2015]_
|
|
121
|
+
- [HRS2016]_
|
|
122
|
+
- [LM2018]_
|
|
123
|
+
"""
|
|
124
|
+
@staticmethod
|
|
125
|
+
def __classcall_private__(cls, co):
|
|
126
|
+
"""
|
|
127
|
+
Create an ordered multiset partition into sets (i.e., a list of sets)
|
|
128
|
+
from the passed arguments with the appropriate parent.
|
|
129
|
+
|
|
130
|
+
EXAMPLES::
|
|
131
|
+
|
|
132
|
+
sage: OrderedMultisetPartitionIntoSets([[3], [2,1]])
|
|
133
|
+
[{3}, {1,2}]
|
|
134
|
+
sage: c = OrderedMultisetPartitionsIntoSets()([{2}, {3}, {4}, {5}]); c
|
|
135
|
+
[{2}, {3}, {4}, {5}]
|
|
136
|
+
sage: d = OrderedMultisetPartitionsIntoSets((1,1,1,2,3,5))([{1}, {5, 1, 3}, {2, 1}]); d
|
|
137
|
+
[{1}, {1,3,5}, {1,2}]
|
|
138
|
+
|
|
139
|
+
TESTS::
|
|
140
|
+
|
|
141
|
+
sage: c.parent() == OrderedMultisetPartitionsIntoSets([2,3,4,5])
|
|
142
|
+
False
|
|
143
|
+
sage: d.parent() == OrderedMultisetPartitionsIntoSets([1,1,1,2,3,5])
|
|
144
|
+
True
|
|
145
|
+
sage: repr(OrderedMultisetPartitionIntoSets([]).parent())
|
|
146
|
+
'Ordered Multiset Partitions into Sets of multiset {{}}'
|
|
147
|
+
"""
|
|
148
|
+
if not co:
|
|
149
|
+
P = OrderedMultisetPartitionsIntoSets([])
|
|
150
|
+
return P.element_class(P, [])
|
|
151
|
+
else:
|
|
152
|
+
X = _concatenate(co)
|
|
153
|
+
P = OrderedMultisetPartitionsIntoSets(_get_weight(X))
|
|
154
|
+
return P.element_class(P, co)
|
|
155
|
+
|
|
156
|
+
def __init__(self, parent, data):
|
|
157
|
+
"""
|
|
158
|
+
Initialize ``self``.
|
|
159
|
+
|
|
160
|
+
EXAMPLES::
|
|
161
|
+
|
|
162
|
+
sage: c = OrderedMultisetPartitionsIntoSets(7)([[1,3], [1,2]])
|
|
163
|
+
sage: OrderedMultisetPartitionIntoSets([[1,3], [1,2]]) == c
|
|
164
|
+
True
|
|
165
|
+
sage: c.weight()
|
|
166
|
+
{1: 2, 2: 1, 3: 1}
|
|
167
|
+
|
|
168
|
+
TESTS::
|
|
169
|
+
|
|
170
|
+
sage: OMP = OrderedMultisetPartitionIntoSets
|
|
171
|
+
sage: c0 = OMP([])
|
|
172
|
+
sage: OMP([[]]) == c0
|
|
173
|
+
True
|
|
174
|
+
sage: TestSuite(c0).run()
|
|
175
|
+
|
|
176
|
+
sage: d = OMP([[1, 3], [1, 'a', 'b']])
|
|
177
|
+
sage: TestSuite(d).run()
|
|
178
|
+
|
|
179
|
+
sage: OMPs = OrderedMultisetPartitionsIntoSets()
|
|
180
|
+
sage: d = OMPs([['a','b','c'],['a','b'],['a']])
|
|
181
|
+
sage: TestSuite(d).run()
|
|
182
|
+
|
|
183
|
+
sage: c.size() == 7
|
|
184
|
+
True
|
|
185
|
+
sage: d.size() == None
|
|
186
|
+
True
|
|
187
|
+
"""
|
|
188
|
+
# Delete empty blocks
|
|
189
|
+
co = [block for block in data if block]
|
|
190
|
+
if not _has_nonempty_sets(co):
|
|
191
|
+
raise ValueError("cannot view %s as an ordered partition of %s" % (co, parent._Xtup))
|
|
192
|
+
|
|
193
|
+
ClonableArray.__init__(self, parent, [frozenset(k) for k in co])
|
|
194
|
+
self._multiset = _get_multiset(co)
|
|
195
|
+
self._weight = _get_weight(self._multiset)
|
|
196
|
+
self._order = sum(len(block) for block in self)
|
|
197
|
+
if all((a in ZZ and a > 0) for a in self._multiset):
|
|
198
|
+
self._n = ZZ(sum(self._multiset))
|
|
199
|
+
else:
|
|
200
|
+
self._n = None
|
|
201
|
+
|
|
202
|
+
def check(self):
|
|
203
|
+
"""
|
|
204
|
+
Check that we are a valid ordered multiset partition into sets.
|
|
205
|
+
|
|
206
|
+
EXAMPLES::
|
|
207
|
+
|
|
208
|
+
sage: c = OrderedMultisetPartitionsIntoSets(4)([[1], [1,2]])
|
|
209
|
+
sage: c.check()
|
|
210
|
+
|
|
211
|
+
sage: OMPs = OrderedMultisetPartitionsIntoSets()
|
|
212
|
+
sage: c = OMPs([[1], [1], ['a']])
|
|
213
|
+
sage: c.check()
|
|
214
|
+
|
|
215
|
+
TESTS::
|
|
216
|
+
|
|
217
|
+
sage: c = OMPs([[1, 1], [1, 4]])
|
|
218
|
+
Traceback (most recent call last):
|
|
219
|
+
...
|
|
220
|
+
ValueError: cannot convert [[1, 1], [1, 4]] into an element
|
|
221
|
+
of Ordered Multiset Partitions into Sets
|
|
222
|
+
"""
|
|
223
|
+
if self not in self.parent():
|
|
224
|
+
raise ValueError("{} not an element of {}".format(self, self.parent()))
|
|
225
|
+
|
|
226
|
+
def _repr_(self):
|
|
227
|
+
"""
|
|
228
|
+
Return a string representation of ``self``.
|
|
229
|
+
|
|
230
|
+
EXAMPLES::
|
|
231
|
+
|
|
232
|
+
sage: A = OrderedMultisetPartitionIntoSets([[4], [1,2,4], [2,3], [1]])
|
|
233
|
+
sage: A
|
|
234
|
+
[{4}, {1,2,4}, {2,3}, {1}]
|
|
235
|
+
"""
|
|
236
|
+
return self._repr_tight()
|
|
237
|
+
|
|
238
|
+
def _repr_normal(self):
|
|
239
|
+
r"""
|
|
240
|
+
Viewing ``self`` as a list `[A_1, \ldots, A_r]` of sets,
|
|
241
|
+
return the standard Sage string representation of `[A_1, \ldots, A_r]`.
|
|
242
|
+
|
|
243
|
+
EXAMPLES::
|
|
244
|
+
|
|
245
|
+
sage: OrderedMultisetPartitionIntoSets([[4,1,3], [3,2,5]])._repr_normal()
|
|
246
|
+
'[{1, 3, 4}, {2, 3, 5}]'
|
|
247
|
+
sage: OrderedMultisetPartitionIntoSets([[4,1,3,11], [3,'a',5]])._repr_normal()
|
|
248
|
+
"[{1, 11, 3, 4}, {3, 5, 'a'}]"
|
|
249
|
+
"""
|
|
250
|
+
# TODO: simplify if/once ``_repr_`` method for ``Set`` sorts its elements.
|
|
251
|
+
if self._n:
|
|
252
|
+
string_parts = (str(sorted(k)) for k in self)
|
|
253
|
+
else:
|
|
254
|
+
string_parts = (str(sorted(k, key=str)) for k in self)
|
|
255
|
+
string = ", ".join(string_parts).replace("[", "{").replace("]", "}")
|
|
256
|
+
return "[" + string + "]"
|
|
257
|
+
|
|
258
|
+
def _repr_tight(self):
|
|
259
|
+
r"""
|
|
260
|
+
Starting from the standard Sage string representation of ``self``
|
|
261
|
+
as a list `[A_1, \ldots, A_r]` of sets, return the shorter string
|
|
262
|
+
gotten by deleting spaces within ``repr(A_i)``.
|
|
263
|
+
|
|
264
|
+
EXAMPLES::
|
|
265
|
+
|
|
266
|
+
sage: A = OrderedMultisetPartitionIntoSets([[4], [1,2,4], [2,3], [1]])
|
|
267
|
+
sage: A._repr_normal()
|
|
268
|
+
'[{4}, {1, 2, 4}, {2, 3}, {1}]'
|
|
269
|
+
sage: A._repr_tight()
|
|
270
|
+
'[{4}, {1,2,4}, {2,3}, {1}]'
|
|
271
|
+
"""
|
|
272
|
+
repr = self._repr_normal()
|
|
273
|
+
# eliminate spacing within blocks
|
|
274
|
+
return repr.replace(", ", ",").replace("},{", "}, {")
|
|
275
|
+
|
|
276
|
+
def __hash__(self):
|
|
277
|
+
"""
|
|
278
|
+
Return the hash of ``self``.
|
|
279
|
+
|
|
280
|
+
The parent is not included as part of the hash.
|
|
281
|
+
|
|
282
|
+
EXAMPLES::
|
|
283
|
+
|
|
284
|
+
sage: OMP = OrderedMultisetPartitionsIntoSets(4)
|
|
285
|
+
sage: A = OMP([[1], [1, 2]])
|
|
286
|
+
sage: B = OMP([{1}, {1, 2}])
|
|
287
|
+
sage: hash(A) == hash(B)
|
|
288
|
+
True
|
|
289
|
+
"""
|
|
290
|
+
return sum(hash(x) for x in self)
|
|
291
|
+
|
|
292
|
+
def __eq__(self, y):
|
|
293
|
+
"""
|
|
294
|
+
Check equality of ``self`` and ``y``.
|
|
295
|
+
|
|
296
|
+
The parent is not included as part of the equality check.
|
|
297
|
+
|
|
298
|
+
TESTS::
|
|
299
|
+
|
|
300
|
+
sage: OMP_n = OrderedMultisetPartitionsIntoSets(4)
|
|
301
|
+
sage: OMP_X = OrderedMultisetPartitionsIntoSets([1,1,2])
|
|
302
|
+
sage: OMP_Ad = OrderedMultisetPartitionsIntoSets(2, 3)
|
|
303
|
+
sage: mu = [[1], [1, 2]]
|
|
304
|
+
sage: OMP_n(mu) == OMP_X(mu) == OMP_Ad(mu)
|
|
305
|
+
True
|
|
306
|
+
sage: OMP_n(mu) == mu
|
|
307
|
+
False
|
|
308
|
+
sage: OMP_n(mu) == OMP_n([{1}, {3}])
|
|
309
|
+
False
|
|
310
|
+
sage: OMP_n(mu) == OMP_X([[1], [1,2]])
|
|
311
|
+
True
|
|
312
|
+
"""
|
|
313
|
+
if not isinstance(y, OrderedMultisetPartitionIntoSets):
|
|
314
|
+
return False
|
|
315
|
+
return list(self) == list(y)
|
|
316
|
+
|
|
317
|
+
def __ne__(self, y):
|
|
318
|
+
"""
|
|
319
|
+
Check lack of equality of ``self`` and ``y``.
|
|
320
|
+
|
|
321
|
+
The parent is not included as part of the equality check.
|
|
322
|
+
|
|
323
|
+
TESTS::
|
|
324
|
+
|
|
325
|
+
sage: OMP = OrderedMultisetPartitionsIntoSets(4)
|
|
326
|
+
sage: mu = [[1], [1, 2]]
|
|
327
|
+
sage: OMP(mu).__ne__(mu)
|
|
328
|
+
True
|
|
329
|
+
sage: nu = [[1], [2], [1]]
|
|
330
|
+
sage: OMP(mu).__ne__(OMP(nu))
|
|
331
|
+
True
|
|
332
|
+
"""
|
|
333
|
+
return not (self == y)
|
|
334
|
+
|
|
335
|
+
def __add__(self, other):
|
|
336
|
+
"""
|
|
337
|
+
Return the concatenation of two ordered multiset partitions into sets.
|
|
338
|
+
|
|
339
|
+
This operation represents the product in Hopf algebra of ordered multiset
|
|
340
|
+
partitions into sets in its natural basis [LM2018]_.
|
|
341
|
+
|
|
342
|
+
EXAMPLES::
|
|
343
|
+
|
|
344
|
+
sage: OMP = OrderedMultisetPartitionIntoSets
|
|
345
|
+
sage: OMP([[1],[1],[1,3]]) + OMP([[4,1],[2]])
|
|
346
|
+
[{1}, {1}, {1,3}, {1,4}, {2}]
|
|
347
|
+
|
|
348
|
+
TESTS::
|
|
349
|
+
|
|
350
|
+
sage: OMP([]) + OMP([]) == OMP([])
|
|
351
|
+
True
|
|
352
|
+
sage: OMP([[1],[1],[1,3]]) + OMP([]) == OMP([[1],[1],[1,3]])
|
|
353
|
+
True
|
|
354
|
+
"""
|
|
355
|
+
co = list(self) + list(other)
|
|
356
|
+
X = _concatenate(co)
|
|
357
|
+
return OrderedMultisetPartitionsIntoSets(_get_weight(X))(co)
|
|
358
|
+
|
|
359
|
+
@combinatorial_map(order=2, name='reversal')
|
|
360
|
+
def reversal(self):
|
|
361
|
+
r"""
|
|
362
|
+
Return the reverse ordered multiset partition into sets of ``self``.
|
|
363
|
+
|
|
364
|
+
Given an ordered multiset partition into sets `(B_1, B_2, \ldots, B_k)`,
|
|
365
|
+
its reversal is defined to be the ordered multiset partition into sets
|
|
366
|
+
`(B_k, \ldots, B_2, B_1)`.
|
|
367
|
+
|
|
368
|
+
EXAMPLES::
|
|
369
|
+
|
|
370
|
+
sage: C = OrderedMultisetPartitionIntoSets([[1], [1, 3], [2, 3, 4]]); C
|
|
371
|
+
[{1}, {1,3}, {2,3,4}]
|
|
372
|
+
sage: C.reversal()
|
|
373
|
+
[{2,3,4}, {1,3}, {1}]
|
|
374
|
+
"""
|
|
375
|
+
return self.parent()(list(reversed(self)))
|
|
376
|
+
|
|
377
|
+
def shape_from_cardinality(self):
|
|
378
|
+
"""
|
|
379
|
+
Return a composition that records the cardinality of each block of ``self``.
|
|
380
|
+
|
|
381
|
+
EXAMPLES::
|
|
382
|
+
|
|
383
|
+
sage: C = OrderedMultisetPartitionIntoSets([[3, 4, 1], [2], [1, 2, 3, 7]]); C
|
|
384
|
+
[{1,3,4}, {2}, {1,2,3,7}]
|
|
385
|
+
sage: C.shape_from_cardinality()
|
|
386
|
+
[3, 1, 4]
|
|
387
|
+
sage: OrderedMultisetPartitionIntoSets([]).shape_from_cardinality() == Composition([])
|
|
388
|
+
True
|
|
389
|
+
"""
|
|
390
|
+
return Composition([len(k) for k in self])
|
|
391
|
+
|
|
392
|
+
def shape_from_size(self):
|
|
393
|
+
"""
|
|
394
|
+
Return a composition that records the sum of entries of each
|
|
395
|
+
block of ``self``.
|
|
396
|
+
|
|
397
|
+
EXAMPLES::
|
|
398
|
+
|
|
399
|
+
sage: C = OrderedMultisetPartitionIntoSets([[3, 4, 1], [2], [1, 2, 3, 7]]); C
|
|
400
|
+
[{1,3,4}, {2}, {1,2,3,7}]
|
|
401
|
+
sage: C.shape_from_size()
|
|
402
|
+
[8, 2, 13]
|
|
403
|
+
|
|
404
|
+
TESTS::
|
|
405
|
+
|
|
406
|
+
sage: OrderedMultisetPartitionIntoSets([]).shape_from_size() == Composition([])
|
|
407
|
+
True
|
|
408
|
+
sage: D = OrderedMultisetPartitionIntoSets([['a', 'b'], ['a']]); D
|
|
409
|
+
[{'a','b'}, {'a'}]
|
|
410
|
+
sage: D.shape_from_size() == None
|
|
411
|
+
True
|
|
412
|
+
"""
|
|
413
|
+
if self._n is not None:
|
|
414
|
+
return Composition([sum(k) for k in self])
|
|
415
|
+
|
|
416
|
+
def letters(self):
|
|
417
|
+
"""
|
|
418
|
+
Return the set of distinct elements occurring within the blocks
|
|
419
|
+
of ``self``.
|
|
420
|
+
|
|
421
|
+
EXAMPLES::
|
|
422
|
+
|
|
423
|
+
sage: C = OrderedMultisetPartitionIntoSets([[3, 4, 1], [2], [1, 2, 3, 7]]); C
|
|
424
|
+
[{1,3,4}, {2}, {1,2,3,7}]
|
|
425
|
+
sage: C.letters()
|
|
426
|
+
frozenset({1, 2, 3, 4, 7})
|
|
427
|
+
"""
|
|
428
|
+
return _union_of_sets(list(self))
|
|
429
|
+
|
|
430
|
+
def multiset(self, as_dict=False):
|
|
431
|
+
"""
|
|
432
|
+
Return the multiset corresponding to ``self``.
|
|
433
|
+
|
|
434
|
+
INPUT:
|
|
435
|
+
|
|
436
|
+
- ``as_dict`` -- boolean (default: ``False``); whether to return the multiset
|
|
437
|
+
as a tuple of a dict of multiplicities
|
|
438
|
+
|
|
439
|
+
EXAMPLES::
|
|
440
|
+
|
|
441
|
+
sage: C = OrderedMultisetPartitionIntoSets([[3, 4, 1], [2], [1, 2, 3, 7]]); C
|
|
442
|
+
[{1,3,4}, {2}, {1,2,3,7}]
|
|
443
|
+
sage: C.multiset()
|
|
444
|
+
(1, 1, 2, 2, 3, 3, 4, 7)
|
|
445
|
+
sage: C.multiset(as_dict=True)
|
|
446
|
+
{1: 2, 2: 2, 3: 2, 4: 1, 7: 1}
|
|
447
|
+
sage: OrderedMultisetPartitionIntoSets([]).multiset() == ()
|
|
448
|
+
True
|
|
449
|
+
"""
|
|
450
|
+
if as_dict:
|
|
451
|
+
return self._weight
|
|
452
|
+
else:
|
|
453
|
+
return self._multiset
|
|
454
|
+
|
|
455
|
+
def max_letter(self):
|
|
456
|
+
"""
|
|
457
|
+
Return the maximum letter appearing in ``self.letters()`` of ``self``.
|
|
458
|
+
|
|
459
|
+
EXAMPLES::
|
|
460
|
+
|
|
461
|
+
sage: C = OrderedMultisetPartitionIntoSets([[3, 4, 1], [2], [1, 2, 3, 7]])
|
|
462
|
+
sage: C.max_letter()
|
|
463
|
+
7
|
|
464
|
+
sage: D = OrderedMultisetPartitionIntoSets([['a','b','c'],['a','b'],['a'],['b','c','f'],['c','d']])
|
|
465
|
+
sage: D.max_letter()
|
|
466
|
+
'f'
|
|
467
|
+
sage: C = OrderedMultisetPartitionIntoSets([])
|
|
468
|
+
sage: C.max_letter()
|
|
469
|
+
"""
|
|
470
|
+
if not self.letters():
|
|
471
|
+
return None
|
|
472
|
+
else:
|
|
473
|
+
return max(self.letters())
|
|
474
|
+
|
|
475
|
+
def size(self):
|
|
476
|
+
"""
|
|
477
|
+
Return the size of ``self`` (that is, the sum of all integers in
|
|
478
|
+
all blocks) if ``self`` is a list of subsets of positive integers.
|
|
479
|
+
|
|
480
|
+
Else, return ``None``.
|
|
481
|
+
|
|
482
|
+
EXAMPLES::
|
|
483
|
+
|
|
484
|
+
sage: C = OrderedMultisetPartitionIntoSets([[3, 4, 1], [2], [1, 2, 3, 7]]); C
|
|
485
|
+
[{1,3,4}, {2}, {1,2,3,7}]
|
|
486
|
+
sage: C.size()
|
|
487
|
+
23
|
|
488
|
+
sage: C.size() == sum(k for k in C.shape_from_size())
|
|
489
|
+
True
|
|
490
|
+
sage: OrderedMultisetPartitionIntoSets([[7,1],[3]]).size()
|
|
491
|
+
11
|
|
492
|
+
|
|
493
|
+
TESTS::
|
|
494
|
+
|
|
495
|
+
sage: OrderedMultisetPartitionIntoSets([]).size() == 0
|
|
496
|
+
True
|
|
497
|
+
sage: OrderedMultisetPartitionIntoSets([['a','b'],['a','b','c']]).size() is None
|
|
498
|
+
True
|
|
499
|
+
"""
|
|
500
|
+
return self._n
|
|
501
|
+
|
|
502
|
+
def order(self):
|
|
503
|
+
"""
|
|
504
|
+
Return the total number of elements in all blocks of ``self``.
|
|
505
|
+
|
|
506
|
+
EXAMPLES::
|
|
507
|
+
|
|
508
|
+
sage: C = OrderedMultisetPartitionIntoSets([[3, 4, 1], [2], [1, 2, 3, 7]]); C
|
|
509
|
+
[{1,3,4}, {2}, {1,2,3,7}]
|
|
510
|
+
sage: C.order()
|
|
511
|
+
8
|
|
512
|
+
sage: C.order() == sum(C.weight().values())
|
|
513
|
+
True
|
|
514
|
+
sage: C.order() == sum(k for k in C.shape_from_cardinality())
|
|
515
|
+
True
|
|
516
|
+
sage: OrderedMultisetPartitionIntoSets([[7,1],[3]]).order()
|
|
517
|
+
3
|
|
518
|
+
"""
|
|
519
|
+
return self._order
|
|
520
|
+
|
|
521
|
+
def length(self):
|
|
522
|
+
"""
|
|
523
|
+
Return the number of blocks of ``self``.
|
|
524
|
+
|
|
525
|
+
EXAMPLES::
|
|
526
|
+
|
|
527
|
+
sage: OrderedMultisetPartitionIntoSets([[7,1],[3]]).length()
|
|
528
|
+
2
|
|
529
|
+
"""
|
|
530
|
+
return len(self)
|
|
531
|
+
|
|
532
|
+
def weight(self, as_weak_comp=False):
|
|
533
|
+
r"""
|
|
534
|
+
Return a dictionary, with keys being the letters in ``self.letters()``
|
|
535
|
+
and values being their (positive) frequency.
|
|
536
|
+
|
|
537
|
+
Alternatively, if ``as_weak_comp`` is ``True``, count the number of instances
|
|
538
|
+
`n_i` for each distinct positive integer `i` across all blocks of ``self``.
|
|
539
|
+
Return as a list `[n_1, n_2, n_3, ..., n_k]`, where `k` is the max letter
|
|
540
|
+
appearing in ``self.letters()``.
|
|
541
|
+
|
|
542
|
+
EXAMPLES::
|
|
543
|
+
|
|
544
|
+
sage: c = OrderedMultisetPartitionIntoSets([[6,1],[1,3],[1,3,6]])
|
|
545
|
+
sage: c.weight()
|
|
546
|
+
{1: 3, 3: 2, 6: 2}
|
|
547
|
+
sage: c.weight(as_weak_comp=True)
|
|
548
|
+
[3, 0, 2, 0, 0, 2]
|
|
549
|
+
|
|
550
|
+
TESTS::
|
|
551
|
+
|
|
552
|
+
sage: OrderedMultisetPartitionIntoSets([]).weight() == {}
|
|
553
|
+
True
|
|
554
|
+
|
|
555
|
+
sage: c = OrderedMultisetPartitionIntoSets([['a','b'],['a','b','c'],['b'],['b'],['c']])
|
|
556
|
+
sage: c.weight()
|
|
557
|
+
{'a': 2, 'b': 4, 'c': 2}
|
|
558
|
+
sage: c.weight(as_weak_comp=True)
|
|
559
|
+
Traceback (most recent call last):
|
|
560
|
+
...
|
|
561
|
+
ValueError: {'a': 2, 'b': 4, 'c': 2} is not a numeric multiset
|
|
562
|
+
"""
|
|
563
|
+
from pprint import pformat
|
|
564
|
+
w = self._weight
|
|
565
|
+
if as_weak_comp:
|
|
566
|
+
if all(v in ZZ for v in w):
|
|
567
|
+
w = [w.get(i, 0) for i in range(1, self.max_letter() + 1)]
|
|
568
|
+
else:
|
|
569
|
+
raise ValueError("%s is not a numeric multiset" % pformat(w))
|
|
570
|
+
return w
|
|
571
|
+
|
|
572
|
+
def deconcatenate(self, k=2):
|
|
573
|
+
r"""
|
|
574
|
+
Return the list of `k`-deconcatenations of ``self``.
|
|
575
|
+
|
|
576
|
+
A `k`-tuple `(C_1, \ldots, C_k)` of ordered multiset partitions into sets
|
|
577
|
+
represents a `k`-deconcatenation of an ordered multiset partition into sets
|
|
578
|
+
`C` if `C_1 + \cdots + C_k = C`.
|
|
579
|
+
|
|
580
|
+
.. NOTE::
|
|
581
|
+
|
|
582
|
+
This is not to be confused with ``self.split_blocks()``,
|
|
583
|
+
which splits each block of ``self`` before making `k`-tuples
|
|
584
|
+
of ordered multiset partitions into sets.
|
|
585
|
+
|
|
586
|
+
EXAMPLES::
|
|
587
|
+
|
|
588
|
+
sage: OrderedMultisetPartitionIntoSets([[7,1],[3,4,5]]).deconcatenate()
|
|
589
|
+
[([{1,7}, {3,4,5}], []), ([{1,7}], [{3,4,5}]), ([], [{1,7}, {3,4,5}])]
|
|
590
|
+
sage: OrderedMultisetPartitionIntoSets([['b','c'],['a']]).deconcatenate()
|
|
591
|
+
[([{'b','c'}, {'a'}], []), ([{'b','c'}], [{'a'}]), ([], [{'b','c'}, {'a'}])]
|
|
592
|
+
sage: OrderedMultisetPartitionIntoSets([['a','b','c']]).deconcatenate(3)
|
|
593
|
+
[([{'a','b','c'}], [], []),
|
|
594
|
+
([], [{'a','b','c'}], []),
|
|
595
|
+
([], [], [{'a','b','c'}])]
|
|
596
|
+
|
|
597
|
+
TESTS::
|
|
598
|
+
|
|
599
|
+
sage: C = OrderedMultisetPartitionIntoSets([['a'],['b'],['c'],['d'],['e']]); C
|
|
600
|
+
[{'a'}, {'b'}, {'c'}, {'d'}, {'e'}]
|
|
601
|
+
sage: all( len(C.deconcatenate(k))
|
|
602
|
+
....: == binomial(C.length() + k-1, k-1)
|
|
603
|
+
....: for k in range(1, 5) )
|
|
604
|
+
True
|
|
605
|
+
"""
|
|
606
|
+
P = OrderedMultisetPartitionsIntoSets(alphabet=self.letters(),
|
|
607
|
+
max_length=self.length())
|
|
608
|
+
out = []
|
|
609
|
+
for c in IntegerListsLex(self.length(), length=k):
|
|
610
|
+
ps = [sum(c[:i]) for i in range(k+1)]
|
|
611
|
+
out.append(tuple([P(self[ps[i]:ps[i+1]]) for i in range(len(ps)-1)]))
|
|
612
|
+
return out
|
|
613
|
+
|
|
614
|
+
def split_blocks(self, k=2):
|
|
615
|
+
r"""
|
|
616
|
+
Return a dictionary representing the `k`-splittings of ``self``.
|
|
617
|
+
|
|
618
|
+
A `k`-tuple `(A^1, \ldots, A^k)` of ordered multiset partitions into sets
|
|
619
|
+
represents a `k`-splitting of an ordered multiset partition into sets
|
|
620
|
+
`A = [b_1, \ldots, b_r]` if one can express each block `b_i` as
|
|
621
|
+
an (ordered) disjoint union of sets `b_i = b^1_i \sqcup \cdots
|
|
622
|
+
\sqcup b^k_i` (some possibly empty) so that each `A^j` is the
|
|
623
|
+
ordered multiset partition into sets corresponding to the list `[b^j_1,
|
|
624
|
+
b^j_2, \ldots, b^j_r]`, excising empty sets appearing therein.
|
|
625
|
+
|
|
626
|
+
This operation represents the coproduct in Hopf algebra of ordered
|
|
627
|
+
multiset partitions into sets in its natural basis [LM2018]_.
|
|
628
|
+
|
|
629
|
+
EXAMPLES::
|
|
630
|
+
|
|
631
|
+
sage: sorted(OrderedMultisetPartitionIntoSets([[1,2],[3,4]]).split_blocks(), key=str)
|
|
632
|
+
[([], [{1,2}, {3,4}]),
|
|
633
|
+
([{1,2}, {3,4}], []),
|
|
634
|
+
([{1,2}, {3}], [{4}]),
|
|
635
|
+
([{1,2}, {4}], [{3}]),
|
|
636
|
+
([{1,2}], [{3,4}]),
|
|
637
|
+
([{1}, {3,4}], [{2}]),
|
|
638
|
+
([{1}, {3}], [{2}, {4}]),
|
|
639
|
+
([{1}, {4}], [{2}, {3}]),
|
|
640
|
+
([{1}], [{2}, {3,4}]),
|
|
641
|
+
([{2}, {3,4}], [{1}]),
|
|
642
|
+
([{2}, {3}], [{1}, {4}]),
|
|
643
|
+
([{2}, {4}], [{1}, {3}]),
|
|
644
|
+
([{2}], [{1}, {3,4}]),
|
|
645
|
+
([{3,4}], [{1,2}]),
|
|
646
|
+
([{3}], [{1,2}, {4}]),
|
|
647
|
+
([{4}], [{1,2}, {3}])]
|
|
648
|
+
sage: sorted(OrderedMultisetPartitionIntoSets([[1,2]]).split_blocks(3), key=str)
|
|
649
|
+
[([], [], [{1,2}]), ([], [{1,2}], []), ([], [{1}], [{2}]),
|
|
650
|
+
([], [{2}], [{1}]), ([{1,2}], [], []), ([{1}], [], [{2}]),
|
|
651
|
+
([{1}], [{2}], []), ([{2}], [], [{1}]), ([{2}], [{1}], [])]
|
|
652
|
+
sage: OrderedMultisetPartitionIntoSets([[4],[4]]).split_blocks()
|
|
653
|
+
{([], [{4}, {4}]): 1, ([{4}], [{4}]): 2, ([{4}, {4}], []): 1}
|
|
654
|
+
|
|
655
|
+
TESTS::
|
|
656
|
+
|
|
657
|
+
sage: C = OrderedMultisetPartitionIntoSets([[1,2],[4,5,6]]); C
|
|
658
|
+
[{1,2}, {4,5,6}]
|
|
659
|
+
sage: sum(C.split_blocks().values()) == 2**len(C[0]) * 2**len(C[1])
|
|
660
|
+
True
|
|
661
|
+
sage: sum(C.split_blocks(3).values()) == (1+2)**len(C[0]) * (1+2)**len(C[1])
|
|
662
|
+
True
|
|
663
|
+
sage: C = OrderedMultisetPartitionIntoSets([])
|
|
664
|
+
sage: C.split_blocks(3) == {(C, C, C): 1}
|
|
665
|
+
True
|
|
666
|
+
"""
|
|
667
|
+
P = OrderedMultisetPartitionsIntoSets(alphabet=self.letters(),
|
|
668
|
+
max_length=self.length())
|
|
669
|
+
|
|
670
|
+
# corner case
|
|
671
|
+
if not self:
|
|
672
|
+
return {tuple([self]*k): 1}
|
|
673
|
+
|
|
674
|
+
out: dict[tuple, int] = {}
|
|
675
|
+
for t in product(*[_split_block(block, k) for block in self]):
|
|
676
|
+
tt = tuple([P([l for l in c if l]) for c in zip(*t)])
|
|
677
|
+
out[tt] = out.get(tt, 0) + 1
|
|
678
|
+
return out
|
|
679
|
+
|
|
680
|
+
def finer(self, strong=False):
|
|
681
|
+
r"""
|
|
682
|
+
Return the set of ordered multiset partitions into sets that are finer
|
|
683
|
+
than ``self``.
|
|
684
|
+
|
|
685
|
+
An ordered multiset partition into sets `A` is finer than another `B`
|
|
686
|
+
if, reading left-to-right, every block of `B` is the union of some
|
|
687
|
+
consecutive blocks of `A`.
|
|
688
|
+
|
|
689
|
+
If optional argument ``strong`` is set to ``True``, then return
|
|
690
|
+
only those `A` whose blocks are deconcatenations of blocks of `B`.
|
|
691
|
+
(Here, we view blocks of `B` as sorted lists instead of sets.)
|
|
692
|
+
|
|
693
|
+
EXAMPLES::
|
|
694
|
+
|
|
695
|
+
sage: C = OrderedMultisetPartitionIntoSets([[3,2]]).finer()
|
|
696
|
+
sage: len(C)
|
|
697
|
+
3
|
|
698
|
+
sage: sorted(C, key=str)
|
|
699
|
+
[[{2,3}], [{2}, {3}], [{3}, {2}]]
|
|
700
|
+
sage: OrderedMultisetPartitionIntoSets([]).finer()
|
|
701
|
+
{[]}
|
|
702
|
+
sage: O = OrderedMultisetPartitionsIntoSets([1, 1, 'a', 'b'])
|
|
703
|
+
sage: o = O([{1}, {'a', 'b'}, {1}])
|
|
704
|
+
sage: sorted(o.finer(), key=str)
|
|
705
|
+
[[{1}, {'a','b'}, {1}], [{1}, {'a'}, {'b'}, {1}], [{1}, {'b'}, {'a'}, {1}]]
|
|
706
|
+
sage: o.finer() & o.fatter() == set([o])
|
|
707
|
+
True
|
|
708
|
+
"""
|
|
709
|
+
P = OrderedMultisetPartitionsIntoSets(self._multiset)
|
|
710
|
+
|
|
711
|
+
if not self:
|
|
712
|
+
return set([self])
|
|
713
|
+
|
|
714
|
+
CP = product(*[_refine_block(block, strong) for block in self])
|
|
715
|
+
return set(P(_concatenate(map(list, c))) for c in CP)
|
|
716
|
+
|
|
717
|
+
def is_finer(self, co):
|
|
718
|
+
"""
|
|
719
|
+
Return ``True`` if the ordered multiset partition into sets ``self``
|
|
720
|
+
is finer than the composition ``co``; otherwise, return ``False``.
|
|
721
|
+
|
|
722
|
+
EXAMPLES::
|
|
723
|
+
|
|
724
|
+
sage: OrderedMultisetPartitionIntoSets([[4],[1],[2]]).is_finer([[1,4],[2]])
|
|
725
|
+
True
|
|
726
|
+
sage: OrderedMultisetPartitionIntoSets([[1],[4],[2]]).is_finer([[1,4],[2]])
|
|
727
|
+
True
|
|
728
|
+
sage: OrderedMultisetPartitionIntoSets([[1,4],[1],[1]]).is_finer([[1,4],[2]])
|
|
729
|
+
False
|
|
730
|
+
"""
|
|
731
|
+
X = _concatenate(co)
|
|
732
|
+
if self.weight() != OrderedMultisetPartitionsIntoSets(_get_weight(X))(co).weight():
|
|
733
|
+
return False
|
|
734
|
+
|
|
735
|
+
# trim common prefix and suffix to make the search-space smaller
|
|
736
|
+
co1 = list(map(set, self))
|
|
737
|
+
co2 = list(map(set, co))
|
|
738
|
+
while co1[0] == co2[0]:
|
|
739
|
+
co1 = co1[1:]
|
|
740
|
+
co2 = co2[1:]
|
|
741
|
+
while co1[-1] == co2[-1]:
|
|
742
|
+
co1 = co1[:-1]
|
|
743
|
+
co2 = co2[:-1]
|
|
744
|
+
|
|
745
|
+
co1 = OrderedMultisetPartitionIntoSets(co1)
|
|
746
|
+
co2 = OrderedMultisetPartitionIntoSets(co2)
|
|
747
|
+
return co1 in co2.finer()
|
|
748
|
+
|
|
749
|
+
def fatten(self, grouping):
|
|
750
|
+
r"""
|
|
751
|
+
Return the ordered multiset partition into sets fatter than ``self``,
|
|
752
|
+
obtained by grouping together consecutive parts according to ``grouping``
|
|
753
|
+
(whenever this does not violate the strictness condition).
|
|
754
|
+
|
|
755
|
+
INPUT:
|
|
756
|
+
|
|
757
|
+
- ``grouping`` -- a composition (or list) whose sum is the length
|
|
758
|
+
of ``self``
|
|
759
|
+
|
|
760
|
+
EXAMPLES:
|
|
761
|
+
|
|
762
|
+
Let us start with the composition::
|
|
763
|
+
|
|
764
|
+
sage: C = OrderedMultisetPartitionIntoSets([[4,1,5], [2], [7,1]]); C
|
|
765
|
+
[{1,4,5}, {2}, {1,7}]
|
|
766
|
+
|
|
767
|
+
With ``grouping`` equal to `(1, 1, 1)`, `C` is left unchanged::
|
|
768
|
+
|
|
769
|
+
sage: C.fatten([1,1,1])
|
|
770
|
+
[{1,4,5}, {2}, {1,7}]
|
|
771
|
+
|
|
772
|
+
With ``grouping`` equal to `(2,1)` or `(1,2)`, a union of consecutive
|
|
773
|
+
parts is achieved::
|
|
774
|
+
|
|
775
|
+
sage: C.fatten([2,1])
|
|
776
|
+
[{1,2,4,5}, {1,7}]
|
|
777
|
+
sage: C.fatten([1,2])
|
|
778
|
+
[{1,4,5}, {1,2,7}]
|
|
779
|
+
|
|
780
|
+
However, the ``grouping`` `(3)` will throw an error, as `1` cannot
|
|
781
|
+
appear twice in any block of ``C``::
|
|
782
|
+
|
|
783
|
+
sage: C.fatten(Composition([3]))
|
|
784
|
+
Traceback (most recent call last):
|
|
785
|
+
...
|
|
786
|
+
ValueError: [{1,4,5,2,1,7}] is not a valid ordered multiset partition into sets
|
|
787
|
+
"""
|
|
788
|
+
if sum(list(grouping)) != self.length():
|
|
789
|
+
raise ValueError("%s is not a composition of ``self.length()`` (=%s)"
|
|
790
|
+
% (grouping, self.length()))
|
|
791
|
+
|
|
792
|
+
valid = True
|
|
793
|
+
result = []
|
|
794
|
+
for i in range(len(grouping)):
|
|
795
|
+
result_i = self[sum(grouping[:i]) : sum(grouping[:i+1])]
|
|
796
|
+
# check that grouping[i] is allowed, i.e., `|A\cup B| = |A| + |B|`
|
|
797
|
+
strict_size = sum(map(len, result_i))
|
|
798
|
+
size = len(_union_of_sets(result_i))
|
|
799
|
+
if size < strict_size:
|
|
800
|
+
valid = False
|
|
801
|
+
result.append(_concatenate(result_i))
|
|
802
|
+
if not valid:
|
|
803
|
+
str_rep = '['
|
|
804
|
+
for i in range(len(grouping)):
|
|
805
|
+
st = ",".join(str(k) for k in result[i])
|
|
806
|
+
str_rep += "{" + st + "}"
|
|
807
|
+
str_rep = str_rep.replace("}{", "}, {") + "]"
|
|
808
|
+
raise ValueError("%s is not a valid ordered multiset partition into sets" % (str_rep))
|
|
809
|
+
else:
|
|
810
|
+
return OrderedMultisetPartitionsIntoSets(self._multiset)(result)
|
|
811
|
+
|
|
812
|
+
def fatter(self):
|
|
813
|
+
"""
|
|
814
|
+
Return the set of ordered multiset partitions into sets which are fatter
|
|
815
|
+
than ``self``.
|
|
816
|
+
|
|
817
|
+
An ordered multiset partition into sets `A` is fatter than another `B`
|
|
818
|
+
if, reading left-to-right, every block of `A` is the union of some
|
|
819
|
+
consecutive blocks of `B`.
|
|
820
|
+
|
|
821
|
+
EXAMPLES::
|
|
822
|
+
|
|
823
|
+
sage: C = OrderedMultisetPartitionIntoSets([{1,4,5}, {2}, {1,7}]).fatter()
|
|
824
|
+
sage: len(C)
|
|
825
|
+
3
|
|
826
|
+
sage: sorted(C)
|
|
827
|
+
[[{1,4,5}, {2}, {1,7}], [{1,4,5}, {1,2,7}], [{1,2,4,5}, {1,7}]]
|
|
828
|
+
sage: sorted(OrderedMultisetPartitionIntoSets([['a','b'],['c'],['a']]).fatter())
|
|
829
|
+
[[{'a','b'}, {'c'}, {'a'}], [{'a','b'}, {'a','c'}], [{'a','b','c'}, {'a'}]]
|
|
830
|
+
|
|
831
|
+
Some extreme cases::
|
|
832
|
+
|
|
833
|
+
sage: list(OrderedMultisetPartitionIntoSets([['a','b','c']]).fatter())
|
|
834
|
+
[[{'a','b','c'}]]
|
|
835
|
+
sage: list(OrderedMultisetPartitionIntoSets([]).fatter())
|
|
836
|
+
[[]]
|
|
837
|
+
sage: A = OrderedMultisetPartitionIntoSets([[1], [2], [3], [4]])
|
|
838
|
+
sage: B = OrderedMultisetPartitionIntoSets([[1,2,3,4]])
|
|
839
|
+
sage: A.fatter().issubset(B.finer())
|
|
840
|
+
True
|
|
841
|
+
"""
|
|
842
|
+
out = set()
|
|
843
|
+
for c in composition_iterator_fast(self.length()):
|
|
844
|
+
try:
|
|
845
|
+
out.add(self.fatten(c))
|
|
846
|
+
except ValueError:
|
|
847
|
+
pass
|
|
848
|
+
return out
|
|
849
|
+
|
|
850
|
+
def minimaj(self):
|
|
851
|
+
r"""
|
|
852
|
+
Return the minimaj statistic on ordered multiset partitions into sets.
|
|
853
|
+
|
|
854
|
+
We define `minimaj` via an example:
|
|
855
|
+
|
|
856
|
+
1. Sort the block in ``self`` as prescribed by ``self.minimaj_word()``,
|
|
857
|
+
keeping track of the original separation into blocks::
|
|
858
|
+
|
|
859
|
+
in: [{1,5,7}, {2,4}, {5,6}, {4,6,8}, {1,3}, {1,2,3}]
|
|
860
|
+
out: ( 5,7,1 / 2,4 / 5,6 / 4,6,8 / 3,1 / 1,2,3 )
|
|
861
|
+
|
|
862
|
+
2. Record the indices where descents in this word occur::
|
|
863
|
+
|
|
864
|
+
word: (5, 7, 1 / 2, 4 / 5, 6 / 4, 6, 8 / 3, 1 / 1, 2, 3)
|
|
865
|
+
indices: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
|
866
|
+
descents: { 2, 7, 10, 11 }
|
|
867
|
+
|
|
868
|
+
3. Compute the sum of the descents::
|
|
869
|
+
|
|
870
|
+
minimaj = 2 + 7 + 10 + 11 = 30
|
|
871
|
+
|
|
872
|
+
REFERENCES:
|
|
873
|
+
|
|
874
|
+
- [HRW2015]_
|
|
875
|
+
|
|
876
|
+
EXAMPLES::
|
|
877
|
+
|
|
878
|
+
sage: C = OrderedMultisetPartitionIntoSets([{1,5,7}, {2,4}, {5,6}, {4,6,8}, {1,3}, {1,2,3}])
|
|
879
|
+
sage: C, C.minimaj_word()
|
|
880
|
+
([{1,5,7}, {2,4}, {5,6}, {4,6,8}, {1,3}, {1,2,3}],
|
|
881
|
+
(5, 7, 1, 2, 4, 5, 6, 4, 6, 8, 3, 1, 1, 2, 3))
|
|
882
|
+
sage: C.minimaj()
|
|
883
|
+
30
|
|
884
|
+
sage: C = OrderedMultisetPartitionIntoSets([{2,4}, {1,2,3}, {1,6,8}, {2,3}])
|
|
885
|
+
sage: C, C.minimaj_word()
|
|
886
|
+
([{2,4}, {1,2,3}, {1,6,8}, {2,3}], (2, 4, 1, 2, 3, 6, 8, 1, 2, 3))
|
|
887
|
+
sage: C.minimaj()
|
|
888
|
+
9
|
|
889
|
+
sage: OrderedMultisetPartitionIntoSets([]).minimaj()
|
|
890
|
+
0
|
|
891
|
+
sage: C = OrderedMultisetPartitionIntoSets([['b','d'],['a','b','c'],['b']])
|
|
892
|
+
sage: C, C.minimaj_word()
|
|
893
|
+
([{'b','d'}, {'a','b','c'}, {'b'}], ('d', 'b', 'c', 'a', 'b', 'b'))
|
|
894
|
+
sage: C.minimaj()
|
|
895
|
+
4
|
|
896
|
+
"""
|
|
897
|
+
D = _descents(self.minimaj_word())
|
|
898
|
+
return sum(D) + len(D)
|
|
899
|
+
|
|
900
|
+
def minimaj_word(self):
|
|
901
|
+
"""
|
|
902
|
+
Return an ordering of ``self._multiset`` derived from the minimaj
|
|
903
|
+
ordering on blocks of ``self``.
|
|
904
|
+
|
|
905
|
+
.. SEEALSO::
|
|
906
|
+
|
|
907
|
+
:meth:`OrderedMultisetPartitionIntoSets.minimaj_blocks()`.
|
|
908
|
+
|
|
909
|
+
EXAMPLES::
|
|
910
|
+
|
|
911
|
+
sage: C = OrderedMultisetPartitionIntoSets([[2,1], [1,2,3], [1,2], [3], [1]]); C
|
|
912
|
+
[{1,2}, {1,2,3}, {1,2}, {3}, {1}]
|
|
913
|
+
sage: C.minimaj_blocks()
|
|
914
|
+
((1, 2), (2, 3, 1), (1, 2), (3,), (1,))
|
|
915
|
+
sage: C.minimaj_word()
|
|
916
|
+
(1, 2, 2, 3, 1, 1, 2, 3, 1)
|
|
917
|
+
"""
|
|
918
|
+
return _concatenate(self.minimaj_blocks())
|
|
919
|
+
|
|
920
|
+
def minimaj_blocks(self):
|
|
921
|
+
r"""
|
|
922
|
+
Return the minimaj ordering on blocks of ``self``.
|
|
923
|
+
|
|
924
|
+
We define the ordering via the example below.
|
|
925
|
+
|
|
926
|
+
Sort the blocks `[B_1,...,B_k]` of ``self`` from right to left via:
|
|
927
|
+
|
|
928
|
+
1. Sort the last block `B_k` in increasing order, call it the word `W_k`
|
|
929
|
+
|
|
930
|
+
2. If blocks `B_{i+1}, \ldots, B_k` have been converted to words
|
|
931
|
+
`W_{i+1}, \ldots, W_k`, use the letters in `B_i` to make the unique
|
|
932
|
+
word `W_i` that has a factorization `W_i = (u, v)` satisfying:
|
|
933
|
+
|
|
934
|
+
- letters of `u` and `v` appear in increasing order, with `v`
|
|
935
|
+
possibly empty;
|
|
936
|
+
- letters in `vu` appear in increasing order;
|
|
937
|
+
- ``v[-1]`` is the largest letter `a \in B_i` satisfying
|
|
938
|
+
``a <= W_{i+1}[0]``.
|
|
939
|
+
|
|
940
|
+
EXAMPLES::
|
|
941
|
+
|
|
942
|
+
sage: OrderedMultisetPartitionIntoSets([[1,5,7], [2,4], [5,6], [4,6,8], [1,3], [1,2,3]])
|
|
943
|
+
[{1,5,7}, {2,4}, {5,6}, {4,6,8}, {1,3}, {1,2,3}]
|
|
944
|
+
sage: _.minimaj_blocks()
|
|
945
|
+
((5, 7, 1), (2, 4), (5, 6), (4, 6, 8), (3, 1), (1, 2, 3))
|
|
946
|
+
sage: OrderedMultisetPartitionIntoSets([]).minimaj_blocks()
|
|
947
|
+
()
|
|
948
|
+
"""
|
|
949
|
+
if not self:
|
|
950
|
+
return ()
|
|
951
|
+
|
|
952
|
+
C = [sorted(self[-1])]
|
|
953
|
+
for i in range(1, len(self)):
|
|
954
|
+
lower = []
|
|
955
|
+
upper = []
|
|
956
|
+
for j in self[-1 - i]:
|
|
957
|
+
if j <= C[0][0]:
|
|
958
|
+
lower.append(j)
|
|
959
|
+
else:
|
|
960
|
+
upper.append(j)
|
|
961
|
+
C = [sorted(upper) + sorted(lower)] + C
|
|
962
|
+
return tuple(map(tuple, C))
|
|
963
|
+
|
|
964
|
+
def to_tableaux_words(self):
|
|
965
|
+
r"""
|
|
966
|
+
Return a sequence of lists corresponding to row words
|
|
967
|
+
of (skew-)tableaux.
|
|
968
|
+
|
|
969
|
+
OUTPUT:
|
|
970
|
+
|
|
971
|
+
The minimaj bijection `\phi` of [BCHOPSY2017]_
|
|
972
|
+
applied to ``self``.
|
|
973
|
+
|
|
974
|
+
.. TODO::
|
|
975
|
+
|
|
976
|
+
Implement option for mapping to sequence of (skew-)tableaux?
|
|
977
|
+
|
|
978
|
+
EXAMPLES::
|
|
979
|
+
|
|
980
|
+
sage: co = ((1,2,4),(4,5),(3,),(4,6,1),(2,3,1),(1,),(2,5))
|
|
981
|
+
sage: OrderedMultisetPartitionIntoSets(co).to_tableaux_words()
|
|
982
|
+
[[5, 1], [3, 1], [6], [5, 4, 2], [1, 4, 3, 4, 2, 1, 2]]
|
|
983
|
+
"""
|
|
984
|
+
if not self:
|
|
985
|
+
return []
|
|
986
|
+
bb = self.minimaj_blocks()
|
|
987
|
+
b = [block[0] for block in bb]
|
|
988
|
+
beginning = [0]+running_total(self.shape_from_cardinality())
|
|
989
|
+
w = _concatenate(bb)
|
|
990
|
+
D = [0] + _descents(w) + [len(w)]
|
|
991
|
+
pieces = [b]
|
|
992
|
+
for i in range(len(D)-1):
|
|
993
|
+
p = [w[j] for j in range(D[i]+1,D[i+1]+1) if j not in beginning]
|
|
994
|
+
pieces = [p[::-1]] + pieces
|
|
995
|
+
return pieces
|
|
996
|
+
|
|
997
|
+
def major_index(self):
|
|
998
|
+
r"""
|
|
999
|
+
Return the major index of ``self``.
|
|
1000
|
+
|
|
1001
|
+
The major index is a statistic on ordered multiset partitions into sets,
|
|
1002
|
+
which we define here via an example.
|
|
1003
|
+
|
|
1004
|
+
1. Sort each block in the list ``self`` in descending order to create
|
|
1005
|
+
a word `w`, keeping track of the original separation into blocks::
|
|
1006
|
+
|
|
1007
|
+
in: [{3,4,5}, {2,3,4}, {1}, {4,5}]
|
|
1008
|
+
out: [ 5,4,3 / 4,3,2 / 1 / 5,4 ]
|
|
1009
|
+
|
|
1010
|
+
2. Create a sequence `v = (v_0, v_1, v_2, \ldots)` of length
|
|
1011
|
+
``self.order()+1``, built recursively by:
|
|
1012
|
+
|
|
1013
|
+
1. `v_0 = 0`
|
|
1014
|
+
2. `v_j = v_{j-1} + \delta(j)`, where `\delta(j) = 1` if `j` is
|
|
1015
|
+
the index of an end of a block, and zero otherwise.
|
|
1016
|
+
|
|
1017
|
+
::
|
|
1018
|
+
|
|
1019
|
+
in: [ 5,4,3 / 4,3,2 / 1 / 5,4]
|
|
1020
|
+
out: (0, 0,0,1, 1,1,2, 3, 3,4)
|
|
1021
|
+
|
|
1022
|
+
3. Compute `\sum_j v_j`, restricted to descent positions in `w`, i.e.,
|
|
1023
|
+
sum over those `j` with `w_j > w_{j+1}`::
|
|
1024
|
+
|
|
1025
|
+
in: w: [5, 4, 3, 4, 3, 2, 1, 5, 4]
|
|
1026
|
+
v: (0 0, 0, 1, 1, 1, 2, 3, 3, 4)
|
|
1027
|
+
maj := 0 +0 +1 +1 +2 +3 = 7
|
|
1028
|
+
|
|
1029
|
+
REFERENCES:
|
|
1030
|
+
|
|
1031
|
+
- [HRW2015]_
|
|
1032
|
+
|
|
1033
|
+
EXAMPLES::
|
|
1034
|
+
|
|
1035
|
+
sage: C = OrderedMultisetPartitionIntoSets([{1,5,7}, {2,4}, {5,6}, {4,6,8}, {1,3}, {1,2,3}])
|
|
1036
|
+
sage: C.major_index()
|
|
1037
|
+
27
|
|
1038
|
+
sage: C = OrderedMultisetPartitionIntoSets([{3,4,5}, {2,3,4}, {1}, {4,5}])
|
|
1039
|
+
sage: C.major_index()
|
|
1040
|
+
7
|
|
1041
|
+
"""
|
|
1042
|
+
ew = [enumerate(sorted(k)) for k in self]
|
|
1043
|
+
w = []
|
|
1044
|
+
v = [0]
|
|
1045
|
+
for eblock in ew:
|
|
1046
|
+
for (i,wj) in sorted(eblock, reverse=True):
|
|
1047
|
+
vj = v[-1]
|
|
1048
|
+
if i == 0:
|
|
1049
|
+
vj += 1
|
|
1050
|
+
v.append(vj)
|
|
1051
|
+
w.append(wj)
|
|
1052
|
+
maj = [v[j+1] for j in range(len(w)-1) if w[j] > w[j+1]]
|
|
1053
|
+
return sum(maj)
|
|
1054
|
+
|
|
1055
|
+
def shuffle_product(self, other, overlap=False):
|
|
1056
|
+
r"""
|
|
1057
|
+
Return the shuffles (with multiplicity) of blocks of ``self``
|
|
1058
|
+
with blocks of ``other``.
|
|
1059
|
+
|
|
1060
|
+
In case optional argument ``overlap`` is ``True``, instead return
|
|
1061
|
+
the allowable overlapping shuffles. An overlapping shuffle `C` is
|
|
1062
|
+
allowable if, whenever one of its blocks `c` comes from the union
|
|
1063
|
+
`c = a \cup b` of a block of ``self`` and a block of ``other``,
|
|
1064
|
+
then this union is disjoint.
|
|
1065
|
+
|
|
1066
|
+
.. SEEALSO::
|
|
1067
|
+
|
|
1068
|
+
:meth:`Composition.shuffle_product()`
|
|
1069
|
+
|
|
1070
|
+
EXAMPLES::
|
|
1071
|
+
|
|
1072
|
+
sage: A = OrderedMultisetPartitionIntoSets([[2,1,3], [1,2]]); A
|
|
1073
|
+
[{1,2,3}, {1,2}]
|
|
1074
|
+
sage: B = OrderedMultisetPartitionIntoSets([[3,4]]); B
|
|
1075
|
+
[{3,4}]
|
|
1076
|
+
sage: C = OrderedMultisetPartitionIntoSets([[4,5]]); C
|
|
1077
|
+
[{4,5}]
|
|
1078
|
+
sage: list(A.shuffle_product(B))
|
|
1079
|
+
[[{1,2,3}, {1,2}, {3,4}], [{3,4}, {1,2,3}, {1,2}], [{1,2,3}, {3,4}, {1,2}]]
|
|
1080
|
+
sage: list(A.shuffle_product(B, overlap=True))
|
|
1081
|
+
[[{1,2,3}, {1,2}, {3,4}], [{1,2,3}, {3,4}, {1,2}],
|
|
1082
|
+
[{3,4}, {1,2,3}, {1,2}], [{1,2,3}, {1,2,3,4}]]
|
|
1083
|
+
sage: list(A.shuffle_product(C, overlap=True))
|
|
1084
|
+
[[{1,2,3}, {1,2}, {4,5}], [{1,2,3}, {4,5}, {1,2}], [{4,5}, {1,2,3}, {1,2}],
|
|
1085
|
+
[{1,2,3,4,5}, {1,2}], [{1,2,3}, {1,2,4,5}]]
|
|
1086
|
+
"""
|
|
1087
|
+
other = OrderedMultisetPartitionIntoSets(other)
|
|
1088
|
+
P = OrderedMultisetPartitionsIntoSets(self._multiset + other._multiset)
|
|
1089
|
+
if not overlap:
|
|
1090
|
+
for term in ShuffleProduct(self, other, element_constructor=P):
|
|
1091
|
+
yield term
|
|
1092
|
+
else:
|
|
1093
|
+
A = list(map(tuple, self))
|
|
1094
|
+
B = list(map(tuple, other))
|
|
1095
|
+
for term in ShuffleProduct_overlapping(A, B):
|
|
1096
|
+
if len(_concatenate(map(frozenset, term))) == len(P._Xtup):
|
|
1097
|
+
yield P(term)
|
|
1098
|
+
|
|
1099
|
+
##############################################################
|
|
1100
|
+
|
|
1101
|
+
|
|
1102
|
+
class OrderedMultisetPartitionsIntoSets(UniqueRepresentation, Parent):
|
|
1103
|
+
r"""
|
|
1104
|
+
Ordered Multiset Partitions into Sets.
|
|
1105
|
+
|
|
1106
|
+
An *ordered multiset partition into sets* `c` of a multiset `X` is
|
|
1107
|
+
a list of nonempty subsets (not multisets), called the *blocks* of `c`,
|
|
1108
|
+
whose multi-union is `X`.
|
|
1109
|
+
|
|
1110
|
+
The number of blocks of `c` is called its *length*. The *order* of `c`
|
|
1111
|
+
is the cardinality of the multiset `X`. If, additionally, `X` is a
|
|
1112
|
+
multiset of positive integers, then the *size* of `c` is the sum of
|
|
1113
|
+
all elements of `X`.
|
|
1114
|
+
|
|
1115
|
+
The user may wish to focus on ordered multiset partitions into sets
|
|
1116
|
+
of a given size, or over a given alphabet. Hence, this class allows
|
|
1117
|
+
a variety of arguments as input.
|
|
1118
|
+
|
|
1119
|
+
INPUT:
|
|
1120
|
+
|
|
1121
|
+
Expects one or two arguments, with different behaviors resulting:
|
|
1122
|
+
|
|
1123
|
+
- One Argument:
|
|
1124
|
+
|
|
1125
|
+
+ `X` -- a dictionary or list or tuple
|
|
1126
|
+
(representing a multiset for `c`),
|
|
1127
|
+
or an integer (representing the size of `c`)
|
|
1128
|
+
|
|
1129
|
+
- Two Arguments:
|
|
1130
|
+
|
|
1131
|
+
+ `A` -- list (representing allowable letters within blocks of `c`),
|
|
1132
|
+
or a positive integer (representing the maximal allowable letter)
|
|
1133
|
+
+ `n` -- a nonnegative integer (the total number of letters within `c`)
|
|
1134
|
+
|
|
1135
|
+
Optional keyword arguments are as follows:
|
|
1136
|
+
(See corresponding methods in see :class:`OrderedMultisetPartitionIntoSets` for more details.)
|
|
1137
|
+
|
|
1138
|
+
- ``weight=X`` (list or dictionary `X`) specifies the multiset for `c`
|
|
1139
|
+
- ``size=n`` (integer `n`) specifies the size of `c`
|
|
1140
|
+
- ``alphabet=A`` (iterable `A`) specifies allowable elements for the blocks of `c`
|
|
1141
|
+
- ``length=k`` (integer `k`) specifies the number of blocks in the partition
|
|
1142
|
+
- ``min_length=k`` (integer `k`) specifies minimum number of blocks in the partition
|
|
1143
|
+
- ``max_length=k`` (integer `k`) specifies maximum number of blocks in the partition
|
|
1144
|
+
- ``order=n`` (integer `n`) specifies the cardinality of the multiset that `c` partitions
|
|
1145
|
+
- ``min_order=n`` (integer `n`) specifies minimum number of elements in the partition
|
|
1146
|
+
- ``max_order=n`` (integer `n`) specifies maximum number of elements in the partition
|
|
1147
|
+
|
|
1148
|
+
EXAMPLES:
|
|
1149
|
+
|
|
1150
|
+
Passing one argument to :class:`OrderedMultisetPartitionsIntoSets`:
|
|
1151
|
+
|
|
1152
|
+
There are 5 ordered multiset partitions into sets of the multiset
|
|
1153
|
+
`\{\{1, 1, 4\}\}`::
|
|
1154
|
+
|
|
1155
|
+
sage: OrderedMultisetPartitionsIntoSets([1,1,4]).cardinality()
|
|
1156
|
+
5
|
|
1157
|
+
|
|
1158
|
+
Here is the list of them::
|
|
1159
|
+
|
|
1160
|
+
sage: OrderedMultisetPartitionsIntoSets([1,1,4]).list()
|
|
1161
|
+
[[{1}, {1}, {4}], [{1}, {1,4}], [{1}, {4}, {1}], [{1,4}, {1}], [{4}, {1}, {1}]]
|
|
1162
|
+
|
|
1163
|
+
By chance, there are also 5 ordered multiset partitions into sets of
|
|
1164
|
+
the integer 3::
|
|
1165
|
+
|
|
1166
|
+
sage: OrderedMultisetPartitionsIntoSets(3).cardinality()
|
|
1167
|
+
5
|
|
1168
|
+
|
|
1169
|
+
Here is the list of them::
|
|
1170
|
+
|
|
1171
|
+
sage: OrderedMultisetPartitionsIntoSets(3).list()
|
|
1172
|
+
[[{3}], [{1,2}], [{2}, {1}], [{1}, {2}], [{1}, {1}, {1}]]
|
|
1173
|
+
|
|
1174
|
+
Passing two arguments to :class:`OrderedMultisetPartitionsIntoSets`:
|
|
1175
|
+
|
|
1176
|
+
There are also 5 ordered multiset partitions into sets of order 2
|
|
1177
|
+
over the alphabet `\{1, 4\}`::
|
|
1178
|
+
|
|
1179
|
+
sage: OrderedMultisetPartitionsIntoSets([1, 4], 2)
|
|
1180
|
+
Ordered Multiset Partitions into Sets of order 2 over alphabet {1, 4}
|
|
1181
|
+
sage: OrderedMultisetPartitionsIntoSets([1, 4], 2).cardinality()
|
|
1182
|
+
5
|
|
1183
|
+
|
|
1184
|
+
Here is the list of them::
|
|
1185
|
+
|
|
1186
|
+
sage: OrderedMultisetPartitionsIntoSets([1, 4], 2).list()
|
|
1187
|
+
[[{1,4}], [{1}, {1}], [{1}, {4}], [{4}, {1}], [{4}, {4}]]
|
|
1188
|
+
|
|
1189
|
+
If no arguments are passed to :class:`OrderedMultisetPartitionsIntoSets`,
|
|
1190
|
+
then the code returns all ordered multiset partitions into sets::
|
|
1191
|
+
|
|
1192
|
+
sage: OrderedMultisetPartitionsIntoSets()
|
|
1193
|
+
Ordered Multiset Partitions into Sets
|
|
1194
|
+
sage: [] in OrderedMultisetPartitionsIntoSets()
|
|
1195
|
+
True
|
|
1196
|
+
sage: [[2,3], [1]] in OrderedMultisetPartitionsIntoSets()
|
|
1197
|
+
True
|
|
1198
|
+
sage: [['a','b'], ['a']] in OrderedMultisetPartitionsIntoSets()
|
|
1199
|
+
True
|
|
1200
|
+
sage: [[-2,3], [3]] in OrderedMultisetPartitionsIntoSets()
|
|
1201
|
+
True
|
|
1202
|
+
sage: [[2], [3,3]] in OrderedMultisetPartitionsIntoSets()
|
|
1203
|
+
False
|
|
1204
|
+
|
|
1205
|
+
The following examples show how to test whether or not an object
|
|
1206
|
+
is an ordered multiset partition into sets::
|
|
1207
|
+
|
|
1208
|
+
sage: [[3,2],[2]] in OrderedMultisetPartitionsIntoSets()
|
|
1209
|
+
True
|
|
1210
|
+
sage: [[3,2],[2]] in OrderedMultisetPartitionsIntoSets(7)
|
|
1211
|
+
True
|
|
1212
|
+
sage: [[3,2],[2]] in OrderedMultisetPartitionsIntoSets([2,2,3])
|
|
1213
|
+
True
|
|
1214
|
+
sage: [[3,2],[2]] in OrderedMultisetPartitionsIntoSets(5)
|
|
1215
|
+
False
|
|
1216
|
+
|
|
1217
|
+
.. RUBRIC:: Optional keyword arguments
|
|
1218
|
+
|
|
1219
|
+
Passing keyword arguments that are incompatible with required requirements
|
|
1220
|
+
results in an error; otherwise, the collection of ordered multiset partitions
|
|
1221
|
+
into sets is restricted accordingly:
|
|
1222
|
+
|
|
1223
|
+
*The* ``weight`` *keyword:*
|
|
1224
|
+
|
|
1225
|
+
This is used to specify which multiset `X` is to be considered,
|
|
1226
|
+
if this multiset was not passed as one of the required arguments for
|
|
1227
|
+
:class:`OrderedMultisetPartitionsIntoSets`. In principle, it is a dictionary,
|
|
1228
|
+
but weak compositions are also allowed. For example, the ordered multiset
|
|
1229
|
+
partitions into sets of integer 4 are listed by weight below::
|
|
1230
|
+
|
|
1231
|
+
sage: OrderedMultisetPartitionsIntoSets(4, weight=[0,0,0,1])
|
|
1232
|
+
Ordered Multiset Partitions into Sets of integer 4 with constraint: weight={4: 1}
|
|
1233
|
+
sage: OrderedMultisetPartitionsIntoSets(4, weight=[0,0,0,1]).list()
|
|
1234
|
+
[[{4}]]
|
|
1235
|
+
sage: OrderedMultisetPartitionsIntoSets(4, weight=[1,0,1]).list()
|
|
1236
|
+
[[{1}, {3}], [{1,3}], [{3}, {1}]]
|
|
1237
|
+
sage: OrderedMultisetPartitionsIntoSets(4, weight=[0,2]).list()
|
|
1238
|
+
[[{2}, {2}]]
|
|
1239
|
+
sage: OrderedMultisetPartitionsIntoSets(4, weight=[0,1,1]).list()
|
|
1240
|
+
[]
|
|
1241
|
+
sage: OrderedMultisetPartitionsIntoSets(4, weight=[2,1]).list()
|
|
1242
|
+
[[{1}, {1}, {2}], [{1}, {1,2}], [{1}, {2}, {1}], [{1,2}, {1}], [{2}, {1}, {1}]]
|
|
1243
|
+
sage: O1 = OrderedMultisetPartitionsIntoSets(weight=[2,0,1])
|
|
1244
|
+
sage: O2 = OrderedMultisetPartitionsIntoSets(weight={1:2, 3:1})
|
|
1245
|
+
sage: O1 == O2
|
|
1246
|
+
True
|
|
1247
|
+
sage: OrderedMultisetPartitionsIntoSets(4, weight=[4]).list()
|
|
1248
|
+
[[{1}, {1}, {1}, {1}]]
|
|
1249
|
+
|
|
1250
|
+
*The* ``size`` *keyword:*
|
|
1251
|
+
|
|
1252
|
+
This is used to constrain the sum of entries across all blocks of the ordered
|
|
1253
|
+
multiset partition into sets. (This size is not pre-determined when alphabet
|
|
1254
|
+
`A` and order `d` are passed as required arguments.) For example, the ordered
|
|
1255
|
+
multiset partitions into sets of order 3 over the alphabet `[1,2,4]` that have
|
|
1256
|
+
size equal to 5 are as follows::
|
|
1257
|
+
|
|
1258
|
+
sage: OMPs = OrderedMultisetPartitionsIntoSets
|
|
1259
|
+
sage: OMPs([1,2,4], 3, size=5).list()
|
|
1260
|
+
[[{1,2}, {2}], [{2}, {1,2}], [{2}, {2}, {1}],
|
|
1261
|
+
[{2}, {1}, {2}], [{1}, {2}, {2}]]
|
|
1262
|
+
|
|
1263
|
+
*The* ``alphabet`` *option:*
|
|
1264
|
+
|
|
1265
|
+
This is used to constrain which integers appear across all blocks of the
|
|
1266
|
+
ordered multiset partition into sets. For example, the ordered multiset
|
|
1267
|
+
partitions into sets of integer 4 are listed for different choices of alphabet
|
|
1268
|
+
below. Note that ``alphabet`` is allowed to be an integer or an iterable::
|
|
1269
|
+
|
|
1270
|
+
sage: OMPs = OrderedMultisetPartitionsIntoSets
|
|
1271
|
+
sage: OMPs(4, alphabet=3).list()
|
|
1272
|
+
[[{1,3}], [{3}, {1}],
|
|
1273
|
+
[{1,2}, {1}], [{2}, {2}],
|
|
1274
|
+
[{2}, {1}, {1}], [{1}, {3}],
|
|
1275
|
+
[{1}, {1,2}], [{1}, {2}, {1}],
|
|
1276
|
+
[{1}, {1}, {2}], [{1}, {1}, {1}, {1}]]
|
|
1277
|
+
sage: OMPs(4, alphabet=3) == OMPs(4, alphabet=[1,2,3])
|
|
1278
|
+
True
|
|
1279
|
+
sage: OMPs(4, alphabet=[3]).list()
|
|
1280
|
+
[]
|
|
1281
|
+
sage: OMPs(4, alphabet=[1,3]).list()
|
|
1282
|
+
[[{1,3}], [{3}, {1}], [{1}, {3}], [{1}, {1}, {1}, {1}]]
|
|
1283
|
+
sage: OMPs(4, alphabet=[2]).list()
|
|
1284
|
+
[[{2}, {2}]]
|
|
1285
|
+
sage: OMPs(4, alphabet=[1,2]).list()
|
|
1286
|
+
[[{1,2}, {1}], [{2}, {2}], [{2}, {1}, {1}], [{1}, {1,2}],
|
|
1287
|
+
[{1}, {2}, {1}], [{1}, {1}, {2}], [{1}, {1}, {1}, {1}]]
|
|
1288
|
+
sage: OMPs(4, alphabet=4).list() == OMPs(4).list()
|
|
1289
|
+
True
|
|
1290
|
+
|
|
1291
|
+
*The* ``length``, ``min_length``, *and* ``max_length`` *options:*
|
|
1292
|
+
|
|
1293
|
+
These are used to constrain the number of blocks within the ordered multiset
|
|
1294
|
+
partitions into sets. For example, the ordered multiset partitions into sets
|
|
1295
|
+
of integer 4 of length exactly 2, at least 2, and at most 2 are given by::
|
|
1296
|
+
|
|
1297
|
+
sage: OrderedMultisetPartitionsIntoSets(4, length=2).list()
|
|
1298
|
+
[[{3}, {1}], [{1,2}, {1}], [{2}, {2}], [{1}, {3}], [{1}, {1,2}]]
|
|
1299
|
+
sage: OrderedMultisetPartitionsIntoSets(4, min_length=3).list()
|
|
1300
|
+
[[{2}, {1}, {1}], [{1}, {2}, {1}], [{1}, {1}, {2}], [{1}, {1}, {1}, {1}]]
|
|
1301
|
+
sage: OrderedMultisetPartitionsIntoSets(4, max_length=2).list()
|
|
1302
|
+
[[{4}], [{1,3}], [{3}, {1}], [{1,2}, {1}], [{2}, {2}], [{1}, {3}],
|
|
1303
|
+
[{1}, {1,2}]]
|
|
1304
|
+
|
|
1305
|
+
*The* ``order``, ``min_order``, *and* ``max_order`` *options:*
|
|
1306
|
+
|
|
1307
|
+
These are used to constrain the number of elements across all blocks of the
|
|
1308
|
+
ordered multiset partitions into sets. For example, the ordered multiset
|
|
1309
|
+
partitions into sets of integer 4 are listed by order below::
|
|
1310
|
+
|
|
1311
|
+
sage: OrderedMultisetPartitionsIntoSets(4, order=1).list()
|
|
1312
|
+
[[{4}]]
|
|
1313
|
+
sage: OrderedMultisetPartitionsIntoSets(4, order=2).list()
|
|
1314
|
+
[[{1,3}], [{3}, {1}], [{2}, {2}], [{1}, {3}]]
|
|
1315
|
+
sage: OrderedMultisetPartitionsIntoSets(4, order=3).list()
|
|
1316
|
+
[[{1,2}, {1}], [{2}, {1}, {1}], [{1}, {1,2}], [{1}, {2}, {1}], [{1}, {1}, {2}]]
|
|
1317
|
+
sage: OrderedMultisetPartitionsIntoSets(4, order=4).list()
|
|
1318
|
+
[[{1}, {1}, {1}, {1}]]
|
|
1319
|
+
|
|
1320
|
+
Also, here is a use of ``max_order``, giving the ordered multiset
|
|
1321
|
+
partitions into sets of integer 4 with order 1 or 2::
|
|
1322
|
+
|
|
1323
|
+
sage: OrderedMultisetPartitionsIntoSets(4, max_order=2).list()
|
|
1324
|
+
[[{4}], [{1,3}], [{3}, {1}], [{2}, {2}], [{1}, {3}]]
|
|
1325
|
+
|
|
1326
|
+
TESTS::
|
|
1327
|
+
|
|
1328
|
+
sage: C = OrderedMultisetPartitionsIntoSets(8, length=3); C.cardinality()
|
|
1329
|
+
72
|
|
1330
|
+
sage: TestSuite(C).run()
|
|
1331
|
+
"""
|
|
1332
|
+
@staticmethod
|
|
1333
|
+
def __classcall_private__(self, *args, **constraints):
|
|
1334
|
+
"""
|
|
1335
|
+
Return the correct parent based upon the input:
|
|
1336
|
+
|
|
1337
|
+
EXAMPLES::
|
|
1338
|
+
|
|
1339
|
+
sage: OrderedMultisetPartitionsIntoSets()
|
|
1340
|
+
Ordered Multiset Partitions into Sets
|
|
1341
|
+
sage: OrderedMultisetPartitionsIntoSets(4)
|
|
1342
|
+
Ordered Multiset Partitions into Sets of integer 4
|
|
1343
|
+
sage: OrderedMultisetPartitionsIntoSets(4, max_order=2)
|
|
1344
|
+
Ordered Multiset Partitions into Sets of integer 4 with constraint: max_order=2
|
|
1345
|
+
|
|
1346
|
+
sage: OrderedMultisetPartitionsIntoSets({1:2, 3:1})
|
|
1347
|
+
Ordered Multiset Partitions into Sets of multiset {{1, 1, 3}}
|
|
1348
|
+
sage: OrderedMultisetPartitionsIntoSets({1:2, 3:1}) == OrderedMultisetPartitionsIntoSets([1,1,3])
|
|
1349
|
+
True
|
|
1350
|
+
sage: OrderedMultisetPartitionsIntoSets({'a':2, 'c':1}, length=2)
|
|
1351
|
+
Ordered Multiset Partitions into Sets of multiset {{a, a, c}} with constraint: length=2
|
|
1352
|
+
sage: OrderedMultisetPartitionsIntoSets({'a':2, 'c':1}, length=4).list()
|
|
1353
|
+
[]
|
|
1354
|
+
|
|
1355
|
+
sage: OrderedMultisetPartitionsIntoSets(4, 3)
|
|
1356
|
+
Ordered Multiset Partitions into Sets of order 3 over alphabet {1, 2, 3, 4}
|
|
1357
|
+
sage: OrderedMultisetPartitionsIntoSets(['a', 'd'], 3)
|
|
1358
|
+
Ordered Multiset Partitions into Sets of order 3 over alphabet {a, d}
|
|
1359
|
+
sage: OrderedMultisetPartitionsIntoSets([2,4], 3, min_length=2)
|
|
1360
|
+
Ordered Multiset Partitions into Sets of order 3 over alphabet {2, 4}
|
|
1361
|
+
with constraint: min_length=2
|
|
1362
|
+
|
|
1363
|
+
TESTS:
|
|
1364
|
+
|
|
1365
|
+
The alphabet and order keywords cannot be used if they are also passed
|
|
1366
|
+
as required arguments, even if the values are compatible::
|
|
1367
|
+
|
|
1368
|
+
sage: OrderedMultisetPartitionsIntoSets([1,2,4], 4, alphabet=[2,4], order=3)
|
|
1369
|
+
Traceback (most recent call last):
|
|
1370
|
+
...
|
|
1371
|
+
ValueError: cannot pass alphabet as first argument and keyword argument
|
|
1372
|
+
sage: OrderedMultisetPartitionsIntoSets([1,2,4], 4, order=4)
|
|
1373
|
+
Traceback (most recent call last):
|
|
1374
|
+
...
|
|
1375
|
+
ValueError: cannot pass order as second argument and keyword argument
|
|
1376
|
+
|
|
1377
|
+
The weight, size, and order keywords cannot be used if a multiset is
|
|
1378
|
+
passed as a required argument, even if the values are compatible::
|
|
1379
|
+
|
|
1380
|
+
sage: OrderedMultisetPartitionsIntoSets([1,1,4], weight={1:3, 2:1}).list()
|
|
1381
|
+
Traceback (most recent call last):
|
|
1382
|
+
...
|
|
1383
|
+
ValueError: cannot pass multiset as first argument and weight as keyword argument
|
|
1384
|
+
sage: OrderedMultisetPartitionsIntoSets([1,1,4], size=6).list()
|
|
1385
|
+
Traceback (most recent call last):
|
|
1386
|
+
...
|
|
1387
|
+
ValueError: cannot pass multiset as first argument and size as keyword argument
|
|
1388
|
+
sage: OrderedMultisetPartitionsIntoSets([1,1,4], weight={1:3, 2:1}, order=2).list()
|
|
1389
|
+
Traceback (most recent call last):
|
|
1390
|
+
...
|
|
1391
|
+
ValueError: cannot pass multiset as first argument and ['order', 'weight'] as keyword arguments
|
|
1392
|
+
|
|
1393
|
+
The size keyword cannot be used if it is also passed as a required argument,
|
|
1394
|
+
even if the value is compatible::
|
|
1395
|
+
|
|
1396
|
+
sage: OrderedMultisetPartitionsIntoSets(5, size=5)
|
|
1397
|
+
Traceback (most recent call last):
|
|
1398
|
+
...
|
|
1399
|
+
ValueError: cannot pass size as first argument and keyword argument
|
|
1400
|
+
"""
|
|
1401
|
+
constraints = dict(constraints)
|
|
1402
|
+
if "weight" in constraints:
|
|
1403
|
+
# Should be a 'dictionary' of letter-frequencies, but accept a weak composition
|
|
1404
|
+
w = constraints["weight"]
|
|
1405
|
+
if not isinstance(w, dict):
|
|
1406
|
+
# make sure we didn't receive ``some_dict.items()``
|
|
1407
|
+
if len(w) > 0 and isinstance(w[0], (list, tuple)):
|
|
1408
|
+
w = dict(w)
|
|
1409
|
+
else:
|
|
1410
|
+
w = {i+1: w[i] for i in range(len(w)) if w[i] > 0}
|
|
1411
|
+
if not all((a in ZZ and a > 0) for a in w.values()):
|
|
1412
|
+
raise ValueError("%s must be a dictionary of letter-frequencies or a weak composition" % w)
|
|
1413
|
+
else:
|
|
1414
|
+
constraints["weight"] = tuple(w.items())
|
|
1415
|
+
|
|
1416
|
+
if "alphabet" in constraints:
|
|
1417
|
+
A = constraints["alphabet"]
|
|
1418
|
+
if A in ZZ:
|
|
1419
|
+
A = range(1, A + 1)
|
|
1420
|
+
constraints["alphabet"] = frozenset(A)
|
|
1421
|
+
|
|
1422
|
+
if len(args) == 2: # treat as `alphabet` & `order`
|
|
1423
|
+
alph = args[0]
|
|
1424
|
+
order = args[1]
|
|
1425
|
+
if alph in ZZ:
|
|
1426
|
+
alph = range(1, alph + 1)
|
|
1427
|
+
if (alph and len(set(alph)) == len(alph)) and (order in ZZ and order >= 0):
|
|
1428
|
+
if "alphabet" in constraints:
|
|
1429
|
+
raise ValueError("cannot pass alphabet as first argument and keyword argument")
|
|
1430
|
+
elif "order" in constraints:
|
|
1431
|
+
raise ValueError("cannot pass order as second argument and keyword argument")
|
|
1432
|
+
if constraints == {}:
|
|
1433
|
+
return OrderedMultisetPartitionsIntoSets_alph_d(frozenset(alph), order)
|
|
1434
|
+
else:
|
|
1435
|
+
return OrderedMultisetPartitionsIntoSets_alph_d_constraints(frozenset(alph), order, **constraints)
|
|
1436
|
+
elif frozenset(alph) == frozenset() and order == 0:
|
|
1437
|
+
return OrderedMultisetPartitionsIntoSets_alph_d_constraints(frozenset(alph), order, **constraints)
|
|
1438
|
+
else:
|
|
1439
|
+
raise ValueError("alphabet=%s must be a nonempty set and order=%s must be a nonnegative integer" % (alph, order))
|
|
1440
|
+
|
|
1441
|
+
elif len(args) == 1: # treat as `size` or `multiset`
|
|
1442
|
+
X = args[0]
|
|
1443
|
+
if isinstance(X, (list, tuple)):
|
|
1444
|
+
tmp = {}
|
|
1445
|
+
for i in X:
|
|
1446
|
+
tmp[i] = tmp.get(i, 0) + 1
|
|
1447
|
+
X = tmp
|
|
1448
|
+
if isinstance(X, dict):
|
|
1449
|
+
over_determined = set(["size", "weight", "alphabet", "order", "min_order", "max_order"]).intersection(set(constraints))
|
|
1450
|
+
if over_determined:
|
|
1451
|
+
if len(over_determined) > 1:
|
|
1452
|
+
suff = "s"
|
|
1453
|
+
offenses = str(sorted(over_determined))
|
|
1454
|
+
else:
|
|
1455
|
+
suff = ""
|
|
1456
|
+
offenses = str(over_determined.pop())
|
|
1457
|
+
raise ValueError("cannot pass multiset as first argument and %s as keyword argument%s" % (offenses, suff))
|
|
1458
|
+
X_items = tuple(X.items())
|
|
1459
|
+
if constraints == {}:
|
|
1460
|
+
return OrderedMultisetPartitionsIntoSets_X(X_items)
|
|
1461
|
+
else:
|
|
1462
|
+
return OrderedMultisetPartitionsIntoSets_X_constraints(X_items, **constraints)
|
|
1463
|
+
|
|
1464
|
+
elif X in ZZ and X >= 0:
|
|
1465
|
+
if "size" in constraints:
|
|
1466
|
+
raise ValueError("cannot pass size as first argument and keyword argument")
|
|
1467
|
+
if constraints == {}:
|
|
1468
|
+
return OrderedMultisetPartitionsIntoSets_n(X)
|
|
1469
|
+
else:
|
|
1470
|
+
return OrderedMultisetPartitionsIntoSets_n_constraints(X, **constraints)
|
|
1471
|
+
|
|
1472
|
+
else:
|
|
1473
|
+
# zero arguments are passed?
|
|
1474
|
+
raise ValueError("%s must be a nonnegative integer or a list or dictionary representing a multiset" % X)
|
|
1475
|
+
|
|
1476
|
+
elif len(args) > 2:
|
|
1477
|
+
raise ValueError("OrderedMultisetPartitonsIntoSets takes 1, 2, or 3 arguments")
|
|
1478
|
+
else:
|
|
1479
|
+
# try to do better than a generic parent
|
|
1480
|
+
if "weight" in constraints:
|
|
1481
|
+
X = constraints.pop("weight")
|
|
1482
|
+
return OrderedMultisetPartitionsIntoSets(dict(X), **constraints)
|
|
1483
|
+
elif "size" in constraints:
|
|
1484
|
+
n = constraints.pop("size")
|
|
1485
|
+
return OrderedMultisetPartitionsIntoSets(n, **constraints)
|
|
1486
|
+
elif "alphabet" in constraints and "order" in constraints:
|
|
1487
|
+
A = constraints.pop("alphabet")
|
|
1488
|
+
d = constraints.pop("order")
|
|
1489
|
+
return OrderedMultisetPartitionsIntoSets(A, d, **constraints)
|
|
1490
|
+
|
|
1491
|
+
# generic parent
|
|
1492
|
+
return OrderedMultisetPartitionsIntoSets_all_constraints(**constraints)
|
|
1493
|
+
|
|
1494
|
+
def __init__(self, is_finite=None, **constraints):
|
|
1495
|
+
"""
|
|
1496
|
+
Initialize ``self``.
|
|
1497
|
+
|
|
1498
|
+
TESTS::
|
|
1499
|
+
|
|
1500
|
+
sage: c = {"length":4, "max_order":6, "alphabet":[2,4,5,6]}
|
|
1501
|
+
sage: OrderedMultisetPartitionsIntoSets(**c).constraints
|
|
1502
|
+
{'alphabet': frozenset({2, 4, 5, 6}), 'length': 4, 'max_order': 6}
|
|
1503
|
+
sage: OrderedMultisetPartitionsIntoSets(17, **c).constraints
|
|
1504
|
+
{'alphabet': frozenset({2, 4, 5, 6}), 'length': 4, 'max_order': 6}
|
|
1505
|
+
sage: OrderedMultisetPartitionsIntoSets(17, **c).full_constraints
|
|
1506
|
+
{'alphabet': frozenset({2, 4, 5, 6}), 'length': 4, 'max_order': 6, 'size': 17}
|
|
1507
|
+
|
|
1508
|
+
sage: c = {"length":4, "min_length":5, "max_order":6, "order":5, "alphabet":4}
|
|
1509
|
+
sage: OrderedMultisetPartitionsIntoSets(**c).full_constraints
|
|
1510
|
+
{'alphabet': frozenset({1, 2, 3, 4}), 'length': 4, 'order': 5}
|
|
1511
|
+
sage: OrderedMultisetPartitionsIntoSets(**c).constraints
|
|
1512
|
+
{'length': 4}
|
|
1513
|
+
sage: OrderedMultisetPartitionsIntoSets(4, 5, **c).constraints
|
|
1514
|
+
Traceback (most recent call last):
|
|
1515
|
+
...
|
|
1516
|
+
ValueError: cannot pass alphabet as first argument and keyword argument
|
|
1517
|
+
|
|
1518
|
+
sage: c = {"weight":[2,2,0,3], "min_length":5, "max_order":6, "order":5, "alphabet":4}
|
|
1519
|
+
sage: OrderedMultisetPartitionsIntoSets(**c).constraints
|
|
1520
|
+
Traceback (most recent call last):
|
|
1521
|
+
...
|
|
1522
|
+
ValueError: cannot pass multiset as first argument and ['alphabet', 'max_order', 'order'] as keyword arguments
|
|
1523
|
+
"""
|
|
1524
|
+
constraints = dict(constraints)
|
|
1525
|
+
|
|
1526
|
+
# standardize values for certain keywords
|
|
1527
|
+
if "alphabet" in constraints:
|
|
1528
|
+
if constraints["alphabet"] in ZZ:
|
|
1529
|
+
constraints["alphabet"] = frozenset(range(1, constraints["alphabet"]+1))
|
|
1530
|
+
else:
|
|
1531
|
+
constraints["alphabet"] = frozenset(constraints["alphabet"])
|
|
1532
|
+
|
|
1533
|
+
if "weight" in constraints:
|
|
1534
|
+
X = dict(constraints["weight"])
|
|
1535
|
+
constraints["weight"] = X
|
|
1536
|
+
constraints.pop("alphabet", None)
|
|
1537
|
+
constraints.pop("min_order", None)
|
|
1538
|
+
constraints.pop("order", None)
|
|
1539
|
+
constraints.pop("max_order", None)
|
|
1540
|
+
constraints.pop("size", None)
|
|
1541
|
+
|
|
1542
|
+
if "length" in constraints:
|
|
1543
|
+
constraints.pop("min_length", None)
|
|
1544
|
+
constraints.pop("max_length", None)
|
|
1545
|
+
min_k = constraints.get("min_length", 0)
|
|
1546
|
+
max_k = constraints.get("max_length", infinity)
|
|
1547
|
+
assert min_k <= max_k, "min_length=%s <= max_length=%s" % (min_k, max_k)
|
|
1548
|
+
if min_k == max_k:
|
|
1549
|
+
constraints["length"] = constraints.pop("min_length",
|
|
1550
|
+
constraints.pop("max_length"))
|
|
1551
|
+
|
|
1552
|
+
if "order" in constraints:
|
|
1553
|
+
constraints.pop("min_order", None)
|
|
1554
|
+
constraints.pop("max_order", None)
|
|
1555
|
+
min_ord = constraints.get("min_order", 0)
|
|
1556
|
+
max_ord = constraints.get("max_order", infinity)
|
|
1557
|
+
assert min_ord <= max_ord, "min_order=%s <= max_order=%s" % (min_ord, max_ord)
|
|
1558
|
+
if min_ord == max_ord:
|
|
1559
|
+
constraints["order"] = constraints.pop("min_order",
|
|
1560
|
+
constraints.pop("max_order"))
|
|
1561
|
+
|
|
1562
|
+
# pop keys with empty values, with the exception of 'size' or 'order'
|
|
1563
|
+
self.constraints = {}
|
|
1564
|
+
for (key,val) in constraints.items():
|
|
1565
|
+
if val:
|
|
1566
|
+
self.constraints[key] = val
|
|
1567
|
+
elif key in ("size", "order", "length") and val is not None:
|
|
1568
|
+
self.constraints[key] = val
|
|
1569
|
+
|
|
1570
|
+
self.full_constraints = dict(self.constraints)
|
|
1571
|
+
if hasattr(self, "_X"):
|
|
1572
|
+
self.full_constraints["weight"] = dict(self._X)
|
|
1573
|
+
self.constraints.pop("weight", None)
|
|
1574
|
+
if hasattr(self, "_n"):
|
|
1575
|
+
self.full_constraints["size"] = self._n
|
|
1576
|
+
self.constraints.pop("size", None)
|
|
1577
|
+
if hasattr(self, "_alphabet"):
|
|
1578
|
+
self.full_constraints["alphabet"] = self._alphabet
|
|
1579
|
+
self.constraints.pop("alphabet", None)
|
|
1580
|
+
self.full_constraints["order"] = self._order
|
|
1581
|
+
self.constraints.pop("order", None)
|
|
1582
|
+
|
|
1583
|
+
if is_finite or _is_finite(constraints):
|
|
1584
|
+
Parent.__init__(self, category=FiniteEnumeratedSets())
|
|
1585
|
+
else:
|
|
1586
|
+
Parent.__init__(self, category=InfiniteEnumeratedSets())
|
|
1587
|
+
|
|
1588
|
+
def _repr_(self):
|
|
1589
|
+
"""
|
|
1590
|
+
Return a string representation of ``self``.
|
|
1591
|
+
|
|
1592
|
+
TESTS::
|
|
1593
|
+
|
|
1594
|
+
sage: OrderedMultisetPartitionsIntoSets()
|
|
1595
|
+
Ordered Multiset Partitions into Sets
|
|
1596
|
+
"""
|
|
1597
|
+
return "Ordered Multiset Partitions into Sets"
|
|
1598
|
+
|
|
1599
|
+
def _constraint_repr_(self, cdict=None):
|
|
1600
|
+
"""
|
|
1601
|
+
Return a string representation of all constraints
|
|
1602
|
+
appearing within ``self.constraints``.
|
|
1603
|
+
|
|
1604
|
+
A helper method for ``self._repr_()``.
|
|
1605
|
+
|
|
1606
|
+
EXAMPLES::
|
|
1607
|
+
|
|
1608
|
+
sage: OMPs = OrderedMultisetPartitionsIntoSets()
|
|
1609
|
+
sage: c = {"length":4, "max_order":6, "alphabet":frozenset([2,4,5,6])}
|
|
1610
|
+
sage: OMPs._constraint_repr_(c)
|
|
1611
|
+
' with constraints: alphabet={2, 4, 5, 6}, length=4, max_order=6'
|
|
1612
|
+
sage: c = {"size":14}
|
|
1613
|
+
sage: OMPs._constraint_repr_(c)
|
|
1614
|
+
' with constraint: size=14'
|
|
1615
|
+
"""
|
|
1616
|
+
if not cdict:
|
|
1617
|
+
cdict = dict(self.constraints)
|
|
1618
|
+
if "alphabet" in cdict:
|
|
1619
|
+
# make, e.g., `set([2,3,4])` print as `{2, 3, 4}`
|
|
1620
|
+
if not all(l in ZZ for l in cdict["alphabet"]):
|
|
1621
|
+
A = sorted(cdict["alphabet"], key=str)
|
|
1622
|
+
else:
|
|
1623
|
+
A = sorted(cdict["alphabet"])
|
|
1624
|
+
cdict["alphabet"] = "{" + repr(A)[1:-1] + "}"
|
|
1625
|
+
constr = ""
|
|
1626
|
+
ss = ['%s=%s' % item for item in cdict.items()]
|
|
1627
|
+
ss = sorted(ss)
|
|
1628
|
+
if len(ss) > 1:
|
|
1629
|
+
constr = " with constraints: " + ", ".join(ss)
|
|
1630
|
+
elif len(ss) == 1:
|
|
1631
|
+
constr = " with constraint: " + ", ".join(ss)
|
|
1632
|
+
return constr
|
|
1633
|
+
|
|
1634
|
+
def _element_constructor_(self, lst):
|
|
1635
|
+
"""
|
|
1636
|
+
Construct an element of ``self`` from ``lst``.
|
|
1637
|
+
|
|
1638
|
+
EXAMPLES::
|
|
1639
|
+
|
|
1640
|
+
sage: P = OrderedMultisetPartitionsIntoSets()
|
|
1641
|
+
sage: A = P([[3],[3,1]]) ; A # indirect doctest
|
|
1642
|
+
[{3}, {1,3}]
|
|
1643
|
+
sage: P1 = OrderedMultisetPartitionsIntoSets(7, alphabet=3)
|
|
1644
|
+
sage: A1 = P1([[3],[3,1]]); A1
|
|
1645
|
+
[{3}, {1,3}]
|
|
1646
|
+
sage: P2 = OrderedMultisetPartitionsIntoSets(alphabet=3)
|
|
1647
|
+
sage: A2 = P2([[3],[3,1]]); A2
|
|
1648
|
+
[{3}, {1,3}]
|
|
1649
|
+
sage: A == A1 == A2
|
|
1650
|
+
True
|
|
1651
|
+
sage: P = OrderedMultisetPartitionsIntoSets(3)
|
|
1652
|
+
sage: P([[3],[3,1]])
|
|
1653
|
+
Traceback (most recent call last):
|
|
1654
|
+
...
|
|
1655
|
+
ValueError: cannot convert [[3], [3, 1]] into an element of
|
|
1656
|
+
Ordered Multiset Partitions into Sets of integer 3
|
|
1657
|
+
"""
|
|
1658
|
+
if not lst:
|
|
1659
|
+
omp = []
|
|
1660
|
+
else:
|
|
1661
|
+
omp = [list(z) for z in lst]
|
|
1662
|
+
|
|
1663
|
+
if omp in self:
|
|
1664
|
+
return self.element_class(self, list(map(frozenset, omp)))
|
|
1665
|
+
else:
|
|
1666
|
+
raise ValueError("cannot convert %s into an element of %s" % (lst, self))
|
|
1667
|
+
|
|
1668
|
+
Element = OrderedMultisetPartitionIntoSets
|
|
1669
|
+
|
|
1670
|
+
def __contains__(self, x):
|
|
1671
|
+
"""
|
|
1672
|
+
Return if ``x`` is contained in ``self``.
|
|
1673
|
+
|
|
1674
|
+
TESTS::
|
|
1675
|
+
|
|
1676
|
+
sage: [[2,1], [1,3]] in OrderedMultisetPartitionsIntoSets()
|
|
1677
|
+
True
|
|
1678
|
+
sage: [[2,1], [1,3]] in OrderedMultisetPartitionsIntoSets(7)
|
|
1679
|
+
True
|
|
1680
|
+
sage: [[2,2], [1,3]] in OrderedMultisetPartitionsIntoSets()
|
|
1681
|
+
False
|
|
1682
|
+
sage: [] in OrderedMultisetPartitionsIntoSets()
|
|
1683
|
+
True
|
|
1684
|
+
sage: [] in OrderedMultisetPartitionsIntoSets(0)
|
|
1685
|
+
True
|
|
1686
|
+
sage: [] in OrderedMultisetPartitionsIntoSets(2)
|
|
1687
|
+
False
|
|
1688
|
+
sage: [[2, 1]] in OrderedMultisetPartitionsIntoSets(3, length=2)
|
|
1689
|
+
False
|
|
1690
|
+
sage: [[2, -1]] in OrderedMultisetPartitionsIntoSets()
|
|
1691
|
+
True
|
|
1692
|
+
"""
|
|
1693
|
+
if not isinstance(x, (OrderedMultisetPartitionIntoSets, list, tuple)):
|
|
1694
|
+
return False
|
|
1695
|
+
return _has_nonempty_sets(x) and self._satisfies_constraints(x)
|
|
1696
|
+
|
|
1697
|
+
def _satisfies_constraints(self, x):
|
|
1698
|
+
"""
|
|
1699
|
+
Check whether or not ``x`` satisfies all of the constraints
|
|
1700
|
+
appearing within ``self.full_constraints`` (Boolean output).
|
|
1701
|
+
|
|
1702
|
+
.. NOTE::
|
|
1703
|
+
|
|
1704
|
+
This test will cause an infinite recursion with
|
|
1705
|
+
``self._element_constructor_()`` if the ``__contains__``
|
|
1706
|
+
method in ``OrderedMultisetPartitionsIntoSets_X`` is removed.
|
|
1707
|
+
|
|
1708
|
+
TESTS::
|
|
1709
|
+
|
|
1710
|
+
sage: c = {"length":3, "max_order":5, "alphabet":[1,2,4], "size":12}
|
|
1711
|
+
sage: OMPs = OrderedMultisetPartitionsIntoSets(**c)
|
|
1712
|
+
sage: OMPs._satisfies_constraints([{2,4}, {1}, {1,4}])
|
|
1713
|
+
True
|
|
1714
|
+
sage: failures = {((2,4), (2,4)), ((1,2,4), (1,), (1,4)),
|
|
1715
|
+
....: ((2,4), (3,), (3,)), ((2,4), (1,), (2,4))}
|
|
1716
|
+
sage: any(OMPs._satisfies_constraints(x) for x in failures)
|
|
1717
|
+
False
|
|
1718
|
+
sage: c = {"max_length":4, "weight":{1:2, 2:1, 4:2}}
|
|
1719
|
+
sage: OMPs = OrderedMultisetPartitionsIntoSets(**c)
|
|
1720
|
+
sage: OMPs._satisfies_constraints([{2,4}, {1}, {1,4}])
|
|
1721
|
+
True
|
|
1722
|
+
sage: failures = {((2,), (4,), (1,), (1,), (4,)), ((1,), (1,), (2,4), (2,4))}
|
|
1723
|
+
sage: any(OMPs._satisfies_constraints(x) for x in failures)
|
|
1724
|
+
False
|
|
1725
|
+
"""
|
|
1726
|
+
X = _concatenate(x)
|
|
1727
|
+
P = OrderedMultisetPartitionsIntoSets_X(tuple(_get_weight(X).items()))
|
|
1728
|
+
x = P.element_class(P, [frozenset(block) for block in x])
|
|
1729
|
+
constr = self.full_constraints
|
|
1730
|
+
tsts = []
|
|
1731
|
+
if 'size' in constr:
|
|
1732
|
+
tsts.append( x.size() == constr['size'] )
|
|
1733
|
+
if 'weight' in constr:
|
|
1734
|
+
tsts.append( x.weight() == constr['weight'] )
|
|
1735
|
+
if 'alphabet' in constr:
|
|
1736
|
+
tsts.append( frozenset(x.letters()).issubset(constr['alphabet']) )
|
|
1737
|
+
if 'length' in constr:
|
|
1738
|
+
tsts.append( x.length() == constr['length'] )
|
|
1739
|
+
if 'min_length' in constr:
|
|
1740
|
+
tsts.append( x.length() >= constr['min_length'] )
|
|
1741
|
+
if 'max_length' in constr:
|
|
1742
|
+
tsts.append( x.length() <= constr['max_length'] )
|
|
1743
|
+
if 'order' in constr:
|
|
1744
|
+
tsts.append( x.order() == constr['order'] )
|
|
1745
|
+
if 'min_order' in constr:
|
|
1746
|
+
tsts.append( x.order() >= constr['min_order'] )
|
|
1747
|
+
if 'max_order' in constr:
|
|
1748
|
+
tsts.append( x.order() <= constr['max_order'] )
|
|
1749
|
+
|
|
1750
|
+
return all(tsts)
|
|
1751
|
+
|
|
1752
|
+
def _from_list(self, lst):
|
|
1753
|
+
"""
|
|
1754
|
+
Return an ordered multiset partition into sets of singleton blocks, whose
|
|
1755
|
+
singletons are the elements ``lst``.
|
|
1756
|
+
|
|
1757
|
+
If any of the elements of ``lst`` are zero (or '0'), then use
|
|
1758
|
+
these as breaks points for the blocks.
|
|
1759
|
+
|
|
1760
|
+
.. SEEALSO::
|
|
1761
|
+
|
|
1762
|
+
:meth:`OrderedMultisetPartitionsIntoSets._from_list_with_zeros()`.
|
|
1763
|
+
|
|
1764
|
+
INPUT:
|
|
1765
|
+
|
|
1766
|
+
- ``lst`` -- an iterable
|
|
1767
|
+
|
|
1768
|
+
EXAMPLES::
|
|
1769
|
+
|
|
1770
|
+
sage: OMPs = OrderedMultisetPartitionsIntoSets()
|
|
1771
|
+
sage: OMPs._from_list([1,4,0,8])
|
|
1772
|
+
[{1,4}, {8}]
|
|
1773
|
+
sage: OMPs._from_list([1,4,8])
|
|
1774
|
+
[{1}, {4}, {8}]
|
|
1775
|
+
sage: OMPs._from_list([1,4,8,0]) == OrderedMultisetPartitionIntoSets([[1,4,8]])
|
|
1776
|
+
True
|
|
1777
|
+
sage: OMPs._from_list('abaa')
|
|
1778
|
+
[{'a'}, {'b'}, {'a'}, {'a'}]
|
|
1779
|
+
sage: OMPs._from_list('ab0a0a')
|
|
1780
|
+
[{'a','b'}, {'a'}, {'a'}]
|
|
1781
|
+
|
|
1782
|
+
TESTS::
|
|
1783
|
+
|
|
1784
|
+
sage: OMPs._from_list([1,0,2,3,1]) == OrderedMultisetPartitionIntoSets([[1], [2,3,1]])
|
|
1785
|
+
True
|
|
1786
|
+
sage: OMPs._from_list([1,2,'3',0,1]) == OrderedMultisetPartitionIntoSets([{1,2,'3'}, [1]])
|
|
1787
|
+
True
|
|
1788
|
+
"""
|
|
1789
|
+
if all(a in ZZ for a in lst) and any(a < 0 for a in lst):
|
|
1790
|
+
raise ValueError("`_from_list` does not expect to see negative integers; received {}".format(str(lst)))
|
|
1791
|
+
if 0 in list(lst) or '0' in list(lst):
|
|
1792
|
+
return self._from_list_with_zeros(lst)
|
|
1793
|
+
|
|
1794
|
+
d = [frozenset([x]) for x in lst]
|
|
1795
|
+
c = self.element_class(self, d)
|
|
1796
|
+
# give a better parent, if self is generic
|
|
1797
|
+
if isinstance(self, OrderedMultisetPartitionsIntoSets_all_constraints):
|
|
1798
|
+
P = OrderedMultisetPartitionsIntoSets(_get_weight(lst))
|
|
1799
|
+
return P.element_class(P, c)
|
|
1800
|
+
else:
|
|
1801
|
+
return self.element_class(self, c)
|
|
1802
|
+
|
|
1803
|
+
def _from_list_with_zeros(self, lst_with_zeros):
|
|
1804
|
+
r"""
|
|
1805
|
+
Return an ordered multiset partition into sets from a list of nonnegative
|
|
1806
|
+
integers (or their string equivalents).
|
|
1807
|
+
|
|
1808
|
+
Blocks are separated by zeros. Consecutive zeros are ignored.
|
|
1809
|
+
|
|
1810
|
+
EXAMPLES::
|
|
1811
|
+
|
|
1812
|
+
sage: OrderedMultisetPartitionsIntoSets()._from_list([1,2,4])
|
|
1813
|
+
[{1}, {2}, {4}]
|
|
1814
|
+
sage: OrderedMultisetPartitionsIntoSets()._from_list_with_zeros([1,2,4])
|
|
1815
|
+
[{1,2,4}]
|
|
1816
|
+
sage: OrderedMultisetPartitionsIntoSets()._from_list_with_zeros([1,0,2,0,0,4])
|
|
1817
|
+
[{1}, {2}, {4}]
|
|
1818
|
+
sage: OrderedMultisetPartitionsIntoSets()._from_list_with_zeros('abc00a0b')
|
|
1819
|
+
[{'a','b','c'}, {'a'}, {'b'}]
|
|
1820
|
+
"""
|
|
1821
|
+
from_zero_lst = list(lst_with_zeros)
|
|
1822
|
+
if from_zero_lst[-1] not in {0, '0'}:
|
|
1823
|
+
from_zero_lst += [0]
|
|
1824
|
+
co = []
|
|
1825
|
+
block = []
|
|
1826
|
+
for a in from_zero_lst:
|
|
1827
|
+
if a in {0, '0'}:
|
|
1828
|
+
if block:
|
|
1829
|
+
co.append(block)
|
|
1830
|
+
block = []
|
|
1831
|
+
else:
|
|
1832
|
+
block.append(a)
|
|
1833
|
+
if co in self:
|
|
1834
|
+
c = self.element_class(self, map(frozenset, co))
|
|
1835
|
+
# give a better parent, if `self` is generic
|
|
1836
|
+
if isinstance(self, OrderedMultisetPartitionsIntoSets_all_constraints):
|
|
1837
|
+
P = OrderedMultisetPartitionsIntoSets(c.weight())
|
|
1838
|
+
return P.element_class(P, c)
|
|
1839
|
+
else:
|
|
1840
|
+
return c
|
|
1841
|
+
else:
|
|
1842
|
+
raise ValueError("ordered multiset partitions into sets do not have repeated entries within blocks (%s received)" % str(co))
|
|
1843
|
+
|
|
1844
|
+
def __iter__(self):
|
|
1845
|
+
"""
|
|
1846
|
+
Iterate over ordered multiset partitions into sets.
|
|
1847
|
+
|
|
1848
|
+
EXAMPLES::
|
|
1849
|
+
|
|
1850
|
+
sage: OrderedMultisetPartitionsIntoSets(3).list()
|
|
1851
|
+
[[{3}], [{1,2}], [{2}, {1}], [{1}, {2}], [{1}, {1}, {1}]]
|
|
1852
|
+
sage: OrderedMultisetPartitionsIntoSets(0).list()
|
|
1853
|
+
[[]]
|
|
1854
|
+
sage: C = OrderedMultisetPartitionsIntoSets()
|
|
1855
|
+
sage: it = C.__iter__()
|
|
1856
|
+
sage: [next(it) for i in range(16)]
|
|
1857
|
+
[[], [{1}], [{2}], [{1}, {1}], [{3}], [{1,2}], [{2}, {1}],
|
|
1858
|
+
[{1}, {2}], [{1}, {1}, {1}], [{4}], [{1,3}], [{3}, {1}],
|
|
1859
|
+
[{1,2}, {1}], [{2}, {2}], [{2}, {1}, {1}], [{1}, {3}]]
|
|
1860
|
+
|
|
1861
|
+
TESTS::
|
|
1862
|
+
|
|
1863
|
+
sage: OrderedMultisetPartitionsIntoSets(alphabet=[1,3], max_length=2).list()
|
|
1864
|
+
[[], [{1}], [{3}], [{1,3}], [{1}, {1}], [{1}, {3}],
|
|
1865
|
+
[{3}, {1}], [{3}, {3}], [{1,3}, {1}], [{1,3}, {3}],
|
|
1866
|
+
[{1}, {1,3}], [{3}, {1,3}], [{1,3}, {1,3}]]
|
|
1867
|
+
sage: C = OrderedMultisetPartitionsIntoSets(min_length=2, max_order=2)
|
|
1868
|
+
sage: it = C.__iter__()
|
|
1869
|
+
sage: [next(it) for i in range(15)]
|
|
1870
|
+
[[{1}, {1}], [{2}, {1}], [{1}, {2}], [{3}, {1}], [{2}, {2}],
|
|
1871
|
+
[{1}, {3}], [{4}, {1}], [{3}, {2}], [{2}, {3}], [{1}, {4}],
|
|
1872
|
+
[{5}, {1}], [{4}, {2}], [{3}, {3}], [{2}, {4}], [{1}, {5}]]
|
|
1873
|
+
sage: OrderedMultisetPartitionsIntoSets(alphabet=[1,3], min_length=2).list()
|
|
1874
|
+
Traceback (most recent call last):
|
|
1875
|
+
...
|
|
1876
|
+
NotImplementedError: cannot list an infinite set
|
|
1877
|
+
"""
|
|
1878
|
+
# Look for evidence of ``FiniteEnumeratedSets()`` among constraints.
|
|
1879
|
+
# ``_base_iterator`` ignores most constraints in ``self.full_constraints``.
|
|
1880
|
+
iterator = _base_iterator(self.full_constraints)
|
|
1881
|
+
if iterator:
|
|
1882
|
+
for co in iterator:
|
|
1883
|
+
if self._satisfies_constraints(co):
|
|
1884
|
+
yield self.element_class(self, co)
|
|
1885
|
+
else:
|
|
1886
|
+
# iterate over blocks of letters over an alphabet
|
|
1887
|
+
if "alphabet" in self.constraints:
|
|
1888
|
+
A = self.constraints["alphabet"]
|
|
1889
|
+
# establish a cutoff order ``max_ell``
|
|
1890
|
+
max = self.constraints.get("max_length", infinity)
|
|
1891
|
+
max = self.constraints.get("length", max)
|
|
1892
|
+
max = max * len(A)
|
|
1893
|
+
max = self.constraints.get("max_order", max)
|
|
1894
|
+
max_ell = self.constraints.get("order", max)
|
|
1895
|
+
ell = 0
|
|
1896
|
+
while True and ell <= max_ell:
|
|
1897
|
+
for co in _iterator_order(A, ell):
|
|
1898
|
+
if self._satisfies_constraints(co):
|
|
1899
|
+
yield self.element_class(self, co)
|
|
1900
|
+
ell += 1
|
|
1901
|
+
# or iterate over partitions of multisets of positive integers
|
|
1902
|
+
else:
|
|
1903
|
+
n = 0
|
|
1904
|
+
while True:
|
|
1905
|
+
for co in _iterator_size(n):
|
|
1906
|
+
if self._satisfies_constraints(co):
|
|
1907
|
+
yield self.element_class(self, co)
|
|
1908
|
+
n += 1
|
|
1909
|
+
|
|
1910
|
+
def subset(self, size):
|
|
1911
|
+
"""
|
|
1912
|
+
Return a subset of all ordered multiset partitions into sets.
|
|
1913
|
+
|
|
1914
|
+
INPUT:
|
|
1915
|
+
|
|
1916
|
+
- ``size`` -- integer representing a slice of all ordered
|
|
1917
|
+
multiset partitions into sets
|
|
1918
|
+
|
|
1919
|
+
The slice alluded to above is taken with respect to length, or
|
|
1920
|
+
to order, or to size, depending on the constraints of ``self``.
|
|
1921
|
+
|
|
1922
|
+
EXAMPLES::
|
|
1923
|
+
|
|
1924
|
+
sage: C = OrderedMultisetPartitionsIntoSets(weight={2:2, 3:1, 5:1})
|
|
1925
|
+
sage: C.subset(3)
|
|
1926
|
+
Ordered Multiset Partitions into Sets of multiset {{2, 2, 3, 5}} with constraint: length=3
|
|
1927
|
+
sage: C = OrderedMultisetPartitionsIntoSets(weight={2:2, 3:1, 5:1}, min_length=2)
|
|
1928
|
+
sage: C.subset(3)
|
|
1929
|
+
Ordered Multiset Partitions into Sets of multiset {{2, 2, 3, 5}} with constraint: length=3
|
|
1930
|
+
sage: C = OrderedMultisetPartitionsIntoSets(alphabet=[2,3,5])
|
|
1931
|
+
sage: C.subset(3)
|
|
1932
|
+
Ordered Multiset Partitions into Sets of order 3 over alphabet {2, 3, 5}
|
|
1933
|
+
sage: C = OrderedMultisetPartitionsIntoSets(order=5)
|
|
1934
|
+
sage: C.subset(3)
|
|
1935
|
+
Ordered Multiset Partitions into Sets of integer 3 with constraint: order=5
|
|
1936
|
+
sage: C = OrderedMultisetPartitionsIntoSets(alphabet=[2,3,5], order=5, length=3)
|
|
1937
|
+
sage: C.subset(3)
|
|
1938
|
+
Ordered Multiset Partitions into Sets of order 3 over alphabet {2, 3, 5} with constraint: length=3
|
|
1939
|
+
sage: C = OrderedMultisetPartitionsIntoSets()
|
|
1940
|
+
sage: C.subset(3)
|
|
1941
|
+
Ordered Multiset Partitions into Sets of integer 3
|
|
1942
|
+
sage: C.subset(3) == OrderedMultisetPartitionsIntoSets(3)
|
|
1943
|
+
True
|
|
1944
|
+
"""
|
|
1945
|
+
fc = self.full_constraints
|
|
1946
|
+
|
|
1947
|
+
# slice by 'length'
|
|
1948
|
+
if "weight" in fc:
|
|
1949
|
+
return OrderedMultisetPartitionsIntoSets(fc["weight"], length=size, **self.constraints)
|
|
1950
|
+
elif "alphabet" in fc and "size" in fc:
|
|
1951
|
+
add_length = dict(self.constraints)
|
|
1952
|
+
add_length["length"] = size
|
|
1953
|
+
return OrderedMultisetPartitionsIntoSets(fc["alphabet"], fc["order"], **add_length)
|
|
1954
|
+
|
|
1955
|
+
# slice by 'order'
|
|
1956
|
+
if "alphabet" in fc:
|
|
1957
|
+
no_alpha = {k: v for (k, v) in self.constraints.items() if k != "alphabet"}
|
|
1958
|
+
return OrderedMultisetPartitionsIntoSets(fc["alphabet"], size, **no_alpha)
|
|
1959
|
+
|
|
1960
|
+
# slice by 'size'
|
|
1961
|
+
return OrderedMultisetPartitionsIntoSets(size, **self.constraints)
|
|
1962
|
+
|
|
1963
|
+
###############
|
|
1964
|
+
|
|
1965
|
+
|
|
1966
|
+
class OrderedMultisetPartitionsIntoSets_all_constraints(OrderedMultisetPartitionsIntoSets):
|
|
1967
|
+
r"""
|
|
1968
|
+
All ordered multiset partitions into sets (with or without constraints).
|
|
1969
|
+
|
|
1970
|
+
EXAMPLES::
|
|
1971
|
+
|
|
1972
|
+
sage: C = OrderedMultisetPartitionsIntoSets(); C
|
|
1973
|
+
Ordered Multiset Partitions into Sets
|
|
1974
|
+
sage: [[1],[1,'a']] in C
|
|
1975
|
+
True
|
|
1976
|
+
|
|
1977
|
+
sage: OrderedMultisetPartitionsIntoSets(weight=[2,0,1], length=2)
|
|
1978
|
+
Ordered Multiset Partitions into Sets of multiset {{1, 1, 3}} with constraint: length=2
|
|
1979
|
+
|
|
1980
|
+
TESTS::
|
|
1981
|
+
|
|
1982
|
+
sage: OMP = OrderedMultisetPartitionsIntoSets()
|
|
1983
|
+
sage: TestSuite(OMP).run() # long time
|
|
1984
|
+
|
|
1985
|
+
sage: C = OrderedMultisetPartitionsIntoSets(weight=[2,0,1], length=2)
|
|
1986
|
+
sage: TestSuite(C).run()
|
|
1987
|
+
|
|
1988
|
+
sage: D1 = OrderedMultisetPartitionsIntoSets(weight={1:2, 3:1}, min_length=2, max_length=2)
|
|
1989
|
+
sage: D2 = OrderedMultisetPartitionsIntoSets({1:2, 3:1}, min_length=2, max_length=2)
|
|
1990
|
+
sage: D3 = OrderedMultisetPartitionsIntoSets(5, weight={1:2, 3:1}, length=2)
|
|
1991
|
+
sage: D4 = OrderedMultisetPartitionsIntoSets([1,3], 3, weight={1:2, 3:1}, length=2)
|
|
1992
|
+
sage: D5 = OrderedMultisetPartitionsIntoSets([1,3], 3, size=5, length=2)
|
|
1993
|
+
sage: all(C != D for D in [D1, D2, D3, D4, D5])
|
|
1994
|
+
True
|
|
1995
|
+
sage: all(Set(C) == Set(D) for D in [D1, D2, D3, D4, D5])
|
|
1996
|
+
True
|
|
1997
|
+
sage: E = OrderedMultisetPartitionsIntoSets({1:2, 3:1}, min_length=2)
|
|
1998
|
+
sage: Set(C) == Set(E)
|
|
1999
|
+
False
|
|
2000
|
+
"""
|
|
2001
|
+
|
|
2002
|
+
def _repr_(self):
|
|
2003
|
+
"""
|
|
2004
|
+
Return a string representation of ``self``.
|
|
2005
|
+
|
|
2006
|
+
TESTS::
|
|
2007
|
+
|
|
2008
|
+
sage: OrderedMultisetPartitionsIntoSets(min_length=3, max_order=5)
|
|
2009
|
+
Ordered Multiset Partitions into Sets with constraints: max_order=5, min_length=3
|
|
2010
|
+
sage: OrderedMultisetPartitionsIntoSets(min_length=3, max_order=5, alphabet=[1,'a'])
|
|
2011
|
+
Ordered Multiset Partitions into Sets with constraints:
|
|
2012
|
+
alphabet={1, 'a'}, max_order=5, min_length=3
|
|
2013
|
+
"""
|
|
2014
|
+
return "Ordered Multiset Partitions into Sets" + self._constraint_repr_()
|
|
2015
|
+
|
|
2016
|
+
###############
|
|
2017
|
+
|
|
2018
|
+
|
|
2019
|
+
class OrderedMultisetPartitionsIntoSets_n(OrderedMultisetPartitionsIntoSets):
|
|
2020
|
+
"""
|
|
2021
|
+
Ordered multiset partitions into sets of a fixed integer `n`.
|
|
2022
|
+
"""
|
|
2023
|
+
|
|
2024
|
+
def __init__(self, n):
|
|
2025
|
+
"""
|
|
2026
|
+
Initialize ``self``.
|
|
2027
|
+
|
|
2028
|
+
TESTS::
|
|
2029
|
+
|
|
2030
|
+
sage: C = OrderedMultisetPartitionsIntoSets(Integer(4))
|
|
2031
|
+
sage: TestSuite(C).run()
|
|
2032
|
+
sage: C2 = OrderedMultisetPartitionsIntoSets(int(4))
|
|
2033
|
+
sage: C is C2
|
|
2034
|
+
True
|
|
2035
|
+
sage: C3 = OrderedMultisetPartitionsIntoSets(7/2)
|
|
2036
|
+
Traceback (most recent call last):
|
|
2037
|
+
...
|
|
2038
|
+
ValueError: 7/2 must be a nonnegative integer or a list or
|
|
2039
|
+
dictionary representing a multiset
|
|
2040
|
+
"""
|
|
2041
|
+
self._n = n
|
|
2042
|
+
OrderedMultisetPartitionsIntoSets.__init__(self, True)
|
|
2043
|
+
|
|
2044
|
+
def _repr_(self):
|
|
2045
|
+
"""
|
|
2046
|
+
Return a string representation of ``self``.
|
|
2047
|
+
|
|
2048
|
+
TESTS::
|
|
2049
|
+
|
|
2050
|
+
sage: OrderedMultisetPartitionsIntoSets(3)
|
|
2051
|
+
Ordered Multiset Partitions into Sets of integer 3
|
|
2052
|
+
"""
|
|
2053
|
+
return "Ordered Multiset Partitions into Sets of integer %s" % self._n
|
|
2054
|
+
|
|
2055
|
+
def cardinality(self):
|
|
2056
|
+
"""
|
|
2057
|
+
Return the number of elements in ``self``.
|
|
2058
|
+
|
|
2059
|
+
TESTS::
|
|
2060
|
+
|
|
2061
|
+
sage: len(OrderedMultisetPartitionsIntoSets(10).list())
|
|
2062
|
+
1500
|
|
2063
|
+
sage: OrderedMultisetPartitionsIntoSets(10).cardinality()
|
|
2064
|
+
1500
|
|
2065
|
+
"""
|
|
2066
|
+
# Dispense with the complex computation for small orders.
|
|
2067
|
+
if self._n <= 5:
|
|
2068
|
+
orders = {0: 1, 1: 1, 2: 2, 3: 5, 4: 11, 5: 25}
|
|
2069
|
+
return ZZ(orders[self._n])
|
|
2070
|
+
|
|
2071
|
+
# We view an ordered multiset partition into sets as a list of 2-regular integer partitions.
|
|
2072
|
+
#
|
|
2073
|
+
# The 2-regular partitions have a nice generating function (see OEIS:A000009).
|
|
2074
|
+
# Below, we take (products of) coefficients of polynomials to compute cardinality.
|
|
2075
|
+
t = PowerSeriesRing(ZZ, 't').gen().O(self._n + 1)
|
|
2076
|
+
partspoly = prod(1 + t**k for k in range(1, self._n + 1)).dict()
|
|
2077
|
+
deg = 0
|
|
2078
|
+
for alpha in composition_iterator_fast(self._n):
|
|
2079
|
+
deg += prod(partspoly[d] for d in alpha)
|
|
2080
|
+
return ZZ(deg)
|
|
2081
|
+
|
|
2082
|
+
def _an_element_(self):
|
|
2083
|
+
"""
|
|
2084
|
+
Return a typical element of ``self``.
|
|
2085
|
+
|
|
2086
|
+
EXAMPLES::
|
|
2087
|
+
|
|
2088
|
+
sage: OrderedMultisetPartitionsIntoSets(13).an_element()
|
|
2089
|
+
[{2,3}, {2,3}, {1,2}]
|
|
2090
|
+
sage: OrderedMultisetPartitionsIntoSets(14).an_element()
|
|
2091
|
+
[{2,3}, {2,3}, {4}]
|
|
2092
|
+
"""
|
|
2093
|
+
#output will have at most three blocks, each of size 1, 2, or 3.
|
|
2094
|
+
alpha = Compositions(self._n, max_part=self._n//3+1).an_element()
|
|
2095
|
+
out = []
|
|
2096
|
+
for a in alpha:
|
|
2097
|
+
if a in {1, 2, 4}:
|
|
2098
|
+
out.append([a])
|
|
2099
|
+
else:
|
|
2100
|
+
if a % 2:
|
|
2101
|
+
out.append([a//2+1, a//2])
|
|
2102
|
+
else:
|
|
2103
|
+
out.append([a//2, a//2-1, 1])
|
|
2104
|
+
return self.element_class(self, map(frozenset, out))
|
|
2105
|
+
|
|
2106
|
+
def random_element(self):
|
|
2107
|
+
"""
|
|
2108
|
+
Return a random element of ``self``.
|
|
2109
|
+
|
|
2110
|
+
This method does not return elements of ``self`` with uniform probability,
|
|
2111
|
+
but it does cover all elements. The scheme is as follows:
|
|
2112
|
+
|
|
2113
|
+
- produce a random composition `C`;
|
|
2114
|
+
- choose a random partition of `c` into distinct parts for each `c` in `C`.
|
|
2115
|
+
|
|
2116
|
+
EXAMPLES::
|
|
2117
|
+
|
|
2118
|
+
sage: OrderedMultisetPartitionsIntoSets(5).random_element() # random
|
|
2119
|
+
[{1,2}, {1}, {1}]
|
|
2120
|
+
sage: OrderedMultisetPartitionsIntoSets(5).random_element() # random
|
|
2121
|
+
[{2}, {1,2}]
|
|
2122
|
+
|
|
2123
|
+
sage: OMP = OrderedMultisetPartitionsIntoSets(5)
|
|
2124
|
+
sage: d = {}
|
|
2125
|
+
sage: for _ in range(1100):
|
|
2126
|
+
....: x = OMP.random_element()
|
|
2127
|
+
....: d[x] = d.get(x, 0) + 1
|
|
2128
|
+
sage: d.values() # random
|
|
2129
|
+
[72, 73, 162, 78, 135, 75, 109, 65, 135, 134, 62]
|
|
2130
|
+
"""
|
|
2131
|
+
C = Compositions(self._n).random_element()
|
|
2132
|
+
co = [IntegerListsLex(c, min_part=1, max_part=c,
|
|
2133
|
+
min_slope=1).random_element() for c in C]
|
|
2134
|
+
return self.element_class(self, map(frozenset, co))
|
|
2135
|
+
|
|
2136
|
+
def __iter__(self):
|
|
2137
|
+
"""
|
|
2138
|
+
Iterate over ``self``.
|
|
2139
|
+
|
|
2140
|
+
TESTS::
|
|
2141
|
+
|
|
2142
|
+
sage: O = OrderedMultisetPartitionsIntoSets(6)
|
|
2143
|
+
sage: it = O.__iter__()
|
|
2144
|
+
sage: [next(it) for _ in range(10)]
|
|
2145
|
+
[[{6}], [{2,4}], [{1,5}], [{1,2,3}],
|
|
2146
|
+
[{5}, {1}], [{2,3}, {1}], [{1,4}, {1}],
|
|
2147
|
+
[{4}, {2}], [{1,3}, {2}], [{4}, {1}, {1}]]
|
|
2148
|
+
"""
|
|
2149
|
+
for co in _iterator_size(self._n):
|
|
2150
|
+
yield self.element_class(self, co)
|
|
2151
|
+
|
|
2152
|
+
|
|
2153
|
+
class OrderedMultisetPartitionsIntoSets_n_constraints(OrderedMultisetPartitionsIntoSets):
|
|
2154
|
+
"""
|
|
2155
|
+
Class of ordered multiset partitions into sets of a fixed integer `n`
|
|
2156
|
+
satisfying constraints.
|
|
2157
|
+
"""
|
|
2158
|
+
|
|
2159
|
+
def __init__(self, n, **constraints):
|
|
2160
|
+
"""
|
|
2161
|
+
Mimic class ``OrderedMultisetPartitionsIntoSets_n`` to initialize.
|
|
2162
|
+
|
|
2163
|
+
TESTS::
|
|
2164
|
+
|
|
2165
|
+
sage: C = OrderedMultisetPartitionsIntoSets(6, length=3)
|
|
2166
|
+
sage: TestSuite(C).run()
|
|
2167
|
+
|
|
2168
|
+
sage: C = OrderedMultisetPartitionsIntoSets(6, weight=[3,0,1], length=3)
|
|
2169
|
+
sage: TestSuite(C).run()
|
|
2170
|
+
"""
|
|
2171
|
+
self._n = n
|
|
2172
|
+
OrderedMultisetPartitionsIntoSets.__init__(self, True, size=n, **constraints)
|
|
2173
|
+
|
|
2174
|
+
def _repr_(self):
|
|
2175
|
+
"""
|
|
2176
|
+
Return a string representation of ``self``.
|
|
2177
|
+
|
|
2178
|
+
EXAMPLES::
|
|
2179
|
+
|
|
2180
|
+
sage: O = OrderedMultisetPartitionsIntoSets(14, length=4, max_order=6, alphabet={2,4,5,6})
|
|
2181
|
+
sage: O
|
|
2182
|
+
Ordered Multiset Partitions into Sets of integer 14 with constraints:
|
|
2183
|
+
alphabet={2, 4, 5, 6}, length=4, max_order=6
|
|
2184
|
+
"""
|
|
2185
|
+
cdict = dict(self.constraints)
|
|
2186
|
+
cdict.pop("size", None)
|
|
2187
|
+
base_repr = "Ordered Multiset Partitions into Sets of integer %s" % self._n
|
|
2188
|
+
return base_repr + self._constraint_repr_(cdict)
|
|
2189
|
+
|
|
2190
|
+
###############
|
|
2191
|
+
|
|
2192
|
+
|
|
2193
|
+
class OrderedMultisetPartitionsIntoSets_X(OrderedMultisetPartitionsIntoSets):
|
|
2194
|
+
"""
|
|
2195
|
+
Class of ordered multiset partitions into sets of a fixed multiset `X`.
|
|
2196
|
+
"""
|
|
2197
|
+
|
|
2198
|
+
def __init__(self, X):
|
|
2199
|
+
"""
|
|
2200
|
+
Initialize ``self``.
|
|
2201
|
+
|
|
2202
|
+
TESTS::
|
|
2203
|
+
|
|
2204
|
+
sage: C = OrderedMultisetPartitionsIntoSets([1,1,4])
|
|
2205
|
+
sage: TestSuite(C).run()
|
|
2206
|
+
|
|
2207
|
+
sage: C2 = OrderedMultisetPartitionsIntoSets({1:2, 4:1})
|
|
2208
|
+
sage: C is C2
|
|
2209
|
+
True
|
|
2210
|
+
"""
|
|
2211
|
+
self._X = X
|
|
2212
|
+
# sort the multiset
|
|
2213
|
+
if all((k in ZZ and k > 0) for (k,v) in X):
|
|
2214
|
+
self._Xtup = tuple([k for (k,v) in sorted(X) for _ in range(v)])
|
|
2215
|
+
else:
|
|
2216
|
+
self._Xtup = tuple([k for (k,v) in sorted(X, key=str) for _ in range(v)])
|
|
2217
|
+
OrderedMultisetPartitionsIntoSets.__init__(self, True)
|
|
2218
|
+
|
|
2219
|
+
def _repr_(self):
|
|
2220
|
+
"""
|
|
2221
|
+
Return a string representation of ``self``.
|
|
2222
|
+
|
|
2223
|
+
TESTS::
|
|
2224
|
+
|
|
2225
|
+
sage: repr(OrderedMultisetPartitionsIntoSets([1,1,4]))
|
|
2226
|
+
'Ordered Multiset Partitions into Sets of multiset {{1, 1, 4}}'
|
|
2227
|
+
"""
|
|
2228
|
+
ms_rep = "{{" + ", ".join(map(str, self._Xtup)) + "}}"
|
|
2229
|
+
return "Ordered Multiset Partitions into Sets" + " of multiset %s" % ms_rep
|
|
2230
|
+
|
|
2231
|
+
def __contains__(self, x):
|
|
2232
|
+
"""
|
|
2233
|
+
Return if ``x`` is contained in ``self``.
|
|
2234
|
+
|
|
2235
|
+
TESTS::
|
|
2236
|
+
|
|
2237
|
+
sage: from sage.combinat.multiset_partition_into_sets_ordered import OrderedMultisetPartitionsIntoSets_X as OMPX
|
|
2238
|
+
sage: [[2,1], [1,3]] in OMPX(((1,2), (2,1), (3,1)))
|
|
2239
|
+
True
|
|
2240
|
+
sage: co = OrderedMultisetPartitionIntoSets([[2,1], [1,3]])
|
|
2241
|
+
sage: co in OMPX(((1,2), (2,1), (3,1)))
|
|
2242
|
+
True
|
|
2243
|
+
sage: [[2,1], [2,3]] in OMPX(((1,2), (2,1), (3,1)))
|
|
2244
|
+
False
|
|
2245
|
+
sage: [] in OMPX(())
|
|
2246
|
+
True
|
|
2247
|
+
sage: [[2, -1], [2,'a']] in OMPX(((2,2), (-1,1), ('a',1)))
|
|
2248
|
+
True
|
|
2249
|
+
"""
|
|
2250
|
+
if not isinstance(x, (OrderedMultisetPartitionIntoSets, list, tuple)):
|
|
2251
|
+
return False
|
|
2252
|
+
|
|
2253
|
+
x_Xtup = sorted(_concatenate(x), key=str)
|
|
2254
|
+
self_Xtup = sorted(self._Xtup, key=str)
|
|
2255
|
+
return _has_nonempty_sets(x) and x_Xtup == self_Xtup
|
|
2256
|
+
|
|
2257
|
+
def cardinality(self):
|
|
2258
|
+
"""
|
|
2259
|
+
Return the number of ordered partitions of multiset ``X``.
|
|
2260
|
+
|
|
2261
|
+
TESTS::
|
|
2262
|
+
|
|
2263
|
+
sage: len(OrderedMultisetPartitionsIntoSets([2,2,2,3,4,5]).list())
|
|
2264
|
+
535
|
|
2265
|
+
sage: OrderedMultisetPartitionsIntoSets([2,2,2,3,4,5]).cardinality()
|
|
2266
|
+
535
|
|
2267
|
+
"""
|
|
2268
|
+
if self._Xtup == ():
|
|
2269
|
+
return ZZ(0)
|
|
2270
|
+
|
|
2271
|
+
# We build ordered multiset partitions into sets of `X` by permutation + deconcatenation
|
|
2272
|
+
deg = 0
|
|
2273
|
+
for alpha in Permutations_mset(self._Xtup):
|
|
2274
|
+
fattest = _break_at_descents(alpha)
|
|
2275
|
+
deg += prod(2**(len(k)-1) for k in fattest)
|
|
2276
|
+
return ZZ(deg)
|
|
2277
|
+
|
|
2278
|
+
def _an_element_(self):
|
|
2279
|
+
"""
|
|
2280
|
+
Return a typical element of ``self``.
|
|
2281
|
+
|
|
2282
|
+
EXAMPLES::
|
|
2283
|
+
|
|
2284
|
+
sage: OrderedMultisetPartitionsIntoSets([2,2,2,3,4,5]).an_element()
|
|
2285
|
+
[{2}, {2}, {2,3,4}, {5}]
|
|
2286
|
+
sage: OrderedMultisetPartitionsIntoSets([2,2,2,3,4,4,5]).an_element()
|
|
2287
|
+
[{2}, {2}, {2,3}, {4}, {4,5}]
|
|
2288
|
+
"""
|
|
2289
|
+
if not self._Xtup:
|
|
2290
|
+
return self.element_class(self, [])
|
|
2291
|
+
alpha = Permutations_mset(self._Xtup).an_element()
|
|
2292
|
+
co = _break_at_descents(alpha)
|
|
2293
|
+
|
|
2294
|
+
# construct "an element" by breaking the first fat block of `co` in two
|
|
2295
|
+
elt = []
|
|
2296
|
+
for i in range(len(co)):
|
|
2297
|
+
if len(co[i]) == 1:
|
|
2298
|
+
elt.append(co[i])
|
|
2299
|
+
else:
|
|
2300
|
+
break
|
|
2301
|
+
elt.append(co[i][:len(co[i])//2 + 1])
|
|
2302
|
+
elt.append(co[i][len(co[i])//2 + 1:])
|
|
2303
|
+
elt.extend(co[i+1:])
|
|
2304
|
+
return self.element_class(self, map(frozenset, elt))
|
|
2305
|
+
|
|
2306
|
+
def random_element(self):
|
|
2307
|
+
"""
|
|
2308
|
+
Return a random element of ``self``.
|
|
2309
|
+
|
|
2310
|
+
This method does not return elements of ``self`` with uniform probability,
|
|
2311
|
+
but it does cover all elements. The scheme is as follows:
|
|
2312
|
+
|
|
2313
|
+
- produce a random permutation ``p`` of the multiset;
|
|
2314
|
+
- create blocks of an OMP ``fat`` by breaking ``p`` after non-ascents;
|
|
2315
|
+
- take a random element of ``fat.finer()``.
|
|
2316
|
+
|
|
2317
|
+
EXAMPLES::
|
|
2318
|
+
|
|
2319
|
+
sage: OrderedMultisetPartitionsIntoSets([1,1,3]).random_element() # random
|
|
2320
|
+
[{1}, {1,3}]
|
|
2321
|
+
sage: OrderedMultisetPartitionsIntoSets([1,1,3]).random_element() # random
|
|
2322
|
+
[{3}, {1}, {1}]
|
|
2323
|
+
|
|
2324
|
+
sage: OMP = OrderedMultisetPartitionsIntoSets([1,1,3,3])
|
|
2325
|
+
sage: d = {}
|
|
2326
|
+
sage: for _ in range(1000):
|
|
2327
|
+
....: x = OMP.random_element()
|
|
2328
|
+
....: d[x] = d.get(x, 0) + 1
|
|
2329
|
+
sage: d.values() # random
|
|
2330
|
+
[102, 25, 76, 24, 66, 88, 327, 27, 83, 83, 239, 72, 88]
|
|
2331
|
+
"""
|
|
2332
|
+
if not self._Xtup:
|
|
2333
|
+
return self.element_class(self, [])
|
|
2334
|
+
|
|
2335
|
+
alpha = Permutations_mset(self._Xtup).random_element()
|
|
2336
|
+
co = _break_at_descents(alpha)
|
|
2337
|
+
finer = self.element_class(self, map(frozenset,co)).finer()
|
|
2338
|
+
return FiniteEnumeratedSets()(finer).random_element()
|
|
2339
|
+
|
|
2340
|
+
def __iter__(self):
|
|
2341
|
+
"""
|
|
2342
|
+
Iterate over ``self``.
|
|
2343
|
+
|
|
2344
|
+
TESTS::
|
|
2345
|
+
|
|
2346
|
+
sage: O = OrderedMultisetPartitionsIntoSets(['a', 'b', 'a'])
|
|
2347
|
+
sage: sorted(O, key=str)
|
|
2348
|
+
[[{'a','b'}, {'a'}],
|
|
2349
|
+
[{'a'}, {'a','b'}],
|
|
2350
|
+
[{'a'}, {'a'}, {'b'}],
|
|
2351
|
+
[{'a'}, {'b'}, {'a'}],
|
|
2352
|
+
[{'b'}, {'a'}, {'a'}]]
|
|
2353
|
+
|
|
2354
|
+
sage: O = OrderedMultisetPartitionsIntoSets([1, 1, 2])
|
|
2355
|
+
sage: list(O)
|
|
2356
|
+
[[{1}, {1}, {2}], [{1}, {1,2}], [{1}, {2}, {1}],
|
|
2357
|
+
[{1,2}, {1}], [{2}, {1}, {1}]]
|
|
2358
|
+
"""
|
|
2359
|
+
for co in _iterator_weight(weight=dict(self._X)):
|
|
2360
|
+
yield self.element_class(self, co)
|
|
2361
|
+
|
|
2362
|
+
|
|
2363
|
+
class OrderedMultisetPartitionsIntoSets_X_constraints(OrderedMultisetPartitionsIntoSets):
|
|
2364
|
+
"""
|
|
2365
|
+
Class of ordered multiset partitions into sets of a fixed multiset `X`
|
|
2366
|
+
satisfying constraints.
|
|
2367
|
+
"""
|
|
2368
|
+
|
|
2369
|
+
def __init__(self, X, **constraints):
|
|
2370
|
+
"""
|
|
2371
|
+
Mimic class ``OrderedMultisetPartitionsIntoSets_X`` to initialize.
|
|
2372
|
+
|
|
2373
|
+
TESTS::
|
|
2374
|
+
|
|
2375
|
+
sage: C = OrderedMultisetPartitionsIntoSets([1,1,2,4], length=3)
|
|
2376
|
+
sage: TestSuite(C).run()
|
|
2377
|
+
|
|
2378
|
+
sage: C = OrderedMultisetPartitionsIntoSets([1,1,2,4], max_length=3)
|
|
2379
|
+
sage: TestSuite(C).run()
|
|
2380
|
+
"""
|
|
2381
|
+
self._X = X
|
|
2382
|
+
self._Xtup = tuple(k for (k,v) in sorted(X) for _ in range(v))
|
|
2383
|
+
OrderedMultisetPartitionsIntoSets.__init__(self, True, weight=X, **constraints)
|
|
2384
|
+
|
|
2385
|
+
def _repr_(self):
|
|
2386
|
+
"""
|
|
2387
|
+
Return a string representation of ``self``.
|
|
2388
|
+
|
|
2389
|
+
EXAMPLES::
|
|
2390
|
+
|
|
2391
|
+
sage: O = OrderedMultisetPartitionsIntoSets([2,2,2,3,4,4,5], min_length=4, max_length=5)
|
|
2392
|
+
sage: O
|
|
2393
|
+
Ordered Multiset Partitions into Sets of multiset {{2, 2, 2, 3, 4, 4, 5}}
|
|
2394
|
+
with constraints: max_length=5, min_length=4
|
|
2395
|
+
"""
|
|
2396
|
+
cdict = dict(self.constraints)
|
|
2397
|
+
cdict.pop("weight", None)
|
|
2398
|
+
ms_rep = "{{" + ", ".join(map(str, self._Xtup)) + "}}"
|
|
2399
|
+
base_repr = "Ordered Multiset Partitions into Sets" + " of multiset %s" % ms_rep
|
|
2400
|
+
return base_repr + self._constraint_repr_(cdict)
|
|
2401
|
+
|
|
2402
|
+
###############
|
|
2403
|
+
|
|
2404
|
+
|
|
2405
|
+
class OrderedMultisetPartitionsIntoSets_alph_d(OrderedMultisetPartitionsIntoSets):
|
|
2406
|
+
"""
|
|
2407
|
+
Class of ordered multiset partitions into sets of specified order `d`
|
|
2408
|
+
over a fixed alphabet `A`.
|
|
2409
|
+
"""
|
|
2410
|
+
|
|
2411
|
+
def __init__(self, A, d):
|
|
2412
|
+
"""
|
|
2413
|
+
Initialize ``self``.
|
|
2414
|
+
|
|
2415
|
+
TESTS::
|
|
2416
|
+
|
|
2417
|
+
sage: C = OrderedMultisetPartitionsIntoSets(3, 2)
|
|
2418
|
+
sage: TestSuite(C).run()
|
|
2419
|
+
|
|
2420
|
+
sage: C2 = OrderedMultisetPartitionsIntoSets([1,2,3], 2)
|
|
2421
|
+
sage: C is C2
|
|
2422
|
+
True
|
|
2423
|
+
|
|
2424
|
+
sage: list(OrderedMultisetPartitionsIntoSets([1,2,3], 2))
|
|
2425
|
+
[[{1,2}], [{1,3}], [{2,3}], [{1}, {1}], [{1}, {2}], [{1}, {3}], [{2}, {1}],
|
|
2426
|
+
[{2}, {2}], [{2}, {3}], [{3}, {1}], [{3}, {2}], [{3}, {3}]]
|
|
2427
|
+
"""
|
|
2428
|
+
self._alphabet = A
|
|
2429
|
+
self._order = d
|
|
2430
|
+
OrderedMultisetPartitionsIntoSets.__init__(self, True)
|
|
2431
|
+
|
|
2432
|
+
def _repr_(self):
|
|
2433
|
+
"""
|
|
2434
|
+
Return a string representation of ``self``.
|
|
2435
|
+
|
|
2436
|
+
TESTS::
|
|
2437
|
+
|
|
2438
|
+
sage: repr(OrderedMultisetPartitionsIntoSets(3, 2))
|
|
2439
|
+
'Ordered Multiset Partitions into Sets of order 2 over alphabet {1, 2, 3}'
|
|
2440
|
+
sage: repr(OrderedMultisetPartitionsIntoSets([1,3], 2))
|
|
2441
|
+
'Ordered Multiset Partitions into Sets of order 2 over alphabet {1, 3}'
|
|
2442
|
+
"""
|
|
2443
|
+
A_rep = "Ordered Multiset Partitions into Sets of order " + str(self._order)
|
|
2444
|
+
A_rep += " over alphabet {%s}" % (", ".join(map(str, sorted(self._alphabet))))
|
|
2445
|
+
return A_rep
|
|
2446
|
+
|
|
2447
|
+
def _an_element_(self):
|
|
2448
|
+
"""
|
|
2449
|
+
Return a typical element of ``OrderedMultisetPartitionIntoSets_alph_d``.
|
|
2450
|
+
|
|
2451
|
+
EXAMPLES::
|
|
2452
|
+
|
|
2453
|
+
sage: OrderedMultisetPartitionsIntoSets([2,3,4,5], 3).an_element()
|
|
2454
|
+
[{2,4,5}]
|
|
2455
|
+
"""
|
|
2456
|
+
alpha = Compositions(self._order, max_part=len(self._alphabet)).an_element()
|
|
2457
|
+
co = [Subsets_sk(self._alphabet, a).an_element() for a in alpha]
|
|
2458
|
+
return self.element_class(self, map(frozenset, co))
|
|
2459
|
+
|
|
2460
|
+
def random_element(self):
|
|
2461
|
+
r"""
|
|
2462
|
+
Return a random element of ``self``.
|
|
2463
|
+
|
|
2464
|
+
This method does not return elements of ``self`` with uniform probability,
|
|
2465
|
+
but it does cover all elements. The scheme is as follows:
|
|
2466
|
+
|
|
2467
|
+
- produce a random composition `C`;
|
|
2468
|
+
- choose random subsets of ``self._alphabet`` of size `c` for each `c` in `C`.
|
|
2469
|
+
|
|
2470
|
+
EXAMPLES::
|
|
2471
|
+
|
|
2472
|
+
sage: OrderedMultisetPartitionsIntoSets([1,4], 3).random_element() # random
|
|
2473
|
+
[{4}, {1,4}]
|
|
2474
|
+
sage: OrderedMultisetPartitionsIntoSets([1,3], 4).random_element() # random
|
|
2475
|
+
[{1,3}, {1}, {3}]
|
|
2476
|
+
|
|
2477
|
+
sage: OMP = OrderedMultisetPartitionsIntoSets([2,3,4], 2)
|
|
2478
|
+
sage: d = {}
|
|
2479
|
+
sage: for _ in range(1200):
|
|
2480
|
+
....: x = OMP.random_element()
|
|
2481
|
+
....: d[x] = d.get(x, 0) + 1
|
|
2482
|
+
sage: d.values() # random
|
|
2483
|
+
[192, 68, 73, 61, 69, 60, 77, 204, 210, 66, 53, 67]
|
|
2484
|
+
"""
|
|
2485
|
+
if not self._alphabet:
|
|
2486
|
+
return self.element_class(self, [])
|
|
2487
|
+
|
|
2488
|
+
alpha = Compositions(self._order, max_part=len(self._alphabet)).random_element()
|
|
2489
|
+
co = [Subsets_sk(self._alphabet, a).random_element() for a in alpha]
|
|
2490
|
+
return self.element_class(self, map(frozenset, co))
|
|
2491
|
+
|
|
2492
|
+
def __iter__(self):
|
|
2493
|
+
"""
|
|
2494
|
+
Iterate over ``self``.
|
|
2495
|
+
|
|
2496
|
+
TESTS::
|
|
2497
|
+
|
|
2498
|
+
sage: O = OrderedMultisetPartitionsIntoSets(['a', 'b'], 3)
|
|
2499
|
+
sage: it = O.__iter__()
|
|
2500
|
+
sage: sorted([next(it) for _ in range(O.cardinality())], key=str)
|
|
2501
|
+
[[{'a','b'}, {'a'}], [{'a','b'}, {'b'}], [{'a'}, {'a','b'}],
|
|
2502
|
+
[{'a'}, {'a'}, {'a'}], [{'a'}, {'a'}, {'b'}], [{'a'}, {'b'}, {'a'}],
|
|
2503
|
+
[{'a'}, {'b'}, {'b'}], [{'b'}, {'a','b'}], [{'b'}, {'a'}, {'a'}],
|
|
2504
|
+
[{'b'}, {'a'}, {'b'}], [{'b'}, {'b'}, {'a'}], [{'b'}, {'b'}, {'b'}]]
|
|
2505
|
+
"""
|
|
2506
|
+
for co in _iterator_order(self._alphabet, self._order):
|
|
2507
|
+
yield self.element_class(self, co)
|
|
2508
|
+
|
|
2509
|
+
def cardinality(self):
|
|
2510
|
+
"""
|
|
2511
|
+
Return the number of ordered partitions of order ``self._order`` on
|
|
2512
|
+
alphabet ``self._alphabet``.
|
|
2513
|
+
|
|
2514
|
+
TESTS::
|
|
2515
|
+
|
|
2516
|
+
sage: len(OrderedMultisetPartitionsIntoSets([1, 'a'], 3).list())
|
|
2517
|
+
12
|
|
2518
|
+
sage: OrderedMultisetPartitionsIntoSets([1, 'a'], 3).cardinality()
|
|
2519
|
+
12
|
|
2520
|
+
"""
|
|
2521
|
+
if self._order == 0:
|
|
2522
|
+
return ZZ(0)
|
|
2523
|
+
|
|
2524
|
+
# iteration scheme:
|
|
2525
|
+
# - start from an integer composition ``alpha`` of ``self._order``.
|
|
2526
|
+
# - for each ``a`` in ``alpha``, pick ``a`` letters from ``alphabet``
|
|
2527
|
+
min_length = self._order // len(self._alphabet)
|
|
2528
|
+
max_length = self._order
|
|
2529
|
+
|
|
2530
|
+
deg = 0
|
|
2531
|
+
for k in range(min_length, max_length+1):
|
|
2532
|
+
for alpha in IntegerListsLex(self._order, length=k, min_part=1, max_part=len(self._alphabet)):
|
|
2533
|
+
deg += prod(binomial(len(self._alphabet), a) for a in alpha)
|
|
2534
|
+
return ZZ(deg)
|
|
2535
|
+
|
|
2536
|
+
|
|
2537
|
+
class OrderedMultisetPartitionsIntoSets_alph_d_constraints(OrderedMultisetPartitionsIntoSets):
|
|
2538
|
+
"""
|
|
2539
|
+
Class of ordered multiset partitions into sets of specified order `d`
|
|
2540
|
+
over a fixed alphabet `A` satisfying constraints.
|
|
2541
|
+
"""
|
|
2542
|
+
|
|
2543
|
+
def __init__(self, A, d, **constraints):
|
|
2544
|
+
"""
|
|
2545
|
+
Mimic class ``OrderedMultisetPartitionsIntoSets_alph_d`` to initialize.
|
|
2546
|
+
|
|
2547
|
+
EXAMPLES::
|
|
2548
|
+
|
|
2549
|
+
sage: list(OrderedMultisetPartitionsIntoSets(3, 2, length=3))
|
|
2550
|
+
[]
|
|
2551
|
+
sage: list(OrderedMultisetPartitionsIntoSets([1,2,4], 2, length=1))
|
|
2552
|
+
[[{1,2}], [{1,4}], [{2,4}]]
|
|
2553
|
+
|
|
2554
|
+
TESTS::
|
|
2555
|
+
|
|
2556
|
+
sage: C = OrderedMultisetPartitionsIntoSets(3, 2, length=3)
|
|
2557
|
+
sage: TestSuite(C).run()
|
|
2558
|
+
|
|
2559
|
+
sage: C = OrderedMultisetPartitionsIntoSets([1,2,4], 4, min_length=3)
|
|
2560
|
+
sage: TestSuite(C).run()
|
|
2561
|
+
"""
|
|
2562
|
+
self._alphabet = A
|
|
2563
|
+
self._order = d
|
|
2564
|
+
OrderedMultisetPartitionsIntoSets.__init__(self, True, alphabet=A,
|
|
2565
|
+
order=d, **constraints)
|
|
2566
|
+
|
|
2567
|
+
def _repr_(self):
|
|
2568
|
+
"""
|
|
2569
|
+
Return a string representation of ``self``.
|
|
2570
|
+
|
|
2571
|
+
EXAMPLES::
|
|
2572
|
+
|
|
2573
|
+
sage: O = OrderedMultisetPartitionsIntoSets([2,3,4,5], 4, length=3)
|
|
2574
|
+
sage: O
|
|
2575
|
+
Ordered Multiset Partitions into Sets of order 4 over alphabet {2, 3, 4, 5}
|
|
2576
|
+
with constraint: length=3
|
|
2577
|
+
sage: O = OrderedMultisetPartitionsIntoSets([2,3,4,5], 4, max_length=4, size=10)
|
|
2578
|
+
sage: O
|
|
2579
|
+
Ordered Multiset Partitions into Sets of order 4 over alphabet {2, 3, 4, 5}
|
|
2580
|
+
with constraints: max_length=4, size=10
|
|
2581
|
+
"""
|
|
2582
|
+
cdict = dict(self.constraints)
|
|
2583
|
+
cdict.pop("alphabet", None)
|
|
2584
|
+
cdict.pop("order", None)
|
|
2585
|
+
base_repr = "Ordered Multiset Partitions into Sets of order " + str(self._order)
|
|
2586
|
+
base_repr += " over alphabet {%s}" % (", ".join(map(str, sorted(self._alphabet))))
|
|
2587
|
+
return base_repr + self._constraint_repr_(cdict)
|
|
2588
|
+
|
|
2589
|
+
###############
|
|
2590
|
+
|
|
2591
|
+
|
|
2592
|
+
def _get_multiset(co):
|
|
2593
|
+
"""
|
|
2594
|
+
Construct the multiset (as a sorted tuple) suggested by the lists
|
|
2595
|
+
of lists ``co``.
|
|
2596
|
+
|
|
2597
|
+
EXAMPLES::
|
|
2598
|
+
|
|
2599
|
+
sage: from sage.combinat.multiset_partition_into_sets_ordered import _get_multiset
|
|
2600
|
+
sage: L = ((1,), (1, 6), (6, 7), (1,), (1, 3))
|
|
2601
|
+
sage: _get_multiset(L)
|
|
2602
|
+
(1, 1, 1, 1, 3, 6, 6, 7)
|
|
2603
|
+
"""
|
|
2604
|
+
return tuple(sorted(_concatenate(co), key=str))
|
|
2605
|
+
|
|
2606
|
+
|
|
2607
|
+
def _get_weight(lst):
|
|
2608
|
+
"""
|
|
2609
|
+
Construct the multiset (as a dictionary) suggested by the
|
|
2610
|
+
multiset-as-list ``lst``.
|
|
2611
|
+
|
|
2612
|
+
EXAMPLES::
|
|
2613
|
+
|
|
2614
|
+
sage: from sage.combinat.multiset_partition_into_sets_ordered import _get_weight
|
|
2615
|
+
sage: L = (1, 1, 1, 3, 1, 6, 6, 7)
|
|
2616
|
+
sage: _get_weight(L)
|
|
2617
|
+
{1: 4, 3: 1, 6: 2, 7: 1}
|
|
2618
|
+
"""
|
|
2619
|
+
out = {}
|
|
2620
|
+
for k in lst:
|
|
2621
|
+
out[k] = out.get(k,0) + 1
|
|
2622
|
+
return out
|
|
2623
|
+
|
|
2624
|
+
|
|
2625
|
+
def _has_nonempty_sets(x):
|
|
2626
|
+
"""
|
|
2627
|
+
Blocks should be nonempty sets/lists/tuples of distinct elements.
|
|
2628
|
+
|
|
2629
|
+
TESTS::
|
|
2630
|
+
|
|
2631
|
+
sage: from sage.combinat.multiset_partition_into_sets_ordered import _has_nonempty_sets
|
|
2632
|
+
sage: _has_nonempty_sets([[2,4], {1}, (1,4)])
|
|
2633
|
+
True
|
|
2634
|
+
sage: _has_nonempty_sets([[2,4], {}, (1,4)])
|
|
2635
|
+
False
|
|
2636
|
+
sage: _has_nonempty_sets([(2,4), (1,1), (1,4)])
|
|
2637
|
+
False
|
|
2638
|
+
"""
|
|
2639
|
+
return all((isinstance(block, (list, tuple, set, frozenset, Set_object))
|
|
2640
|
+
and block and len(set(block)) == len(block))
|
|
2641
|
+
for block in x)
|
|
2642
|
+
|
|
2643
|
+
|
|
2644
|
+
def _union_of_sets(list_of_sets):
|
|
2645
|
+
"""
|
|
2646
|
+
Return the union of a list of iterables as a frozenset.
|
|
2647
|
+
|
|
2648
|
+
EXAMPLES::
|
|
2649
|
+
|
|
2650
|
+
sage: from sage.combinat.multiset_partition_into_sets_ordered import _union_of_sets
|
|
2651
|
+
sage: L = ([1,2,3], Set([1,5,6]), [], range(5,8))
|
|
2652
|
+
sage: _union_of_sets(L)
|
|
2653
|
+
frozenset({1, 2, 3, 5, 6, 7})
|
|
2654
|
+
"""
|
|
2655
|
+
return reduce(lambda a, b: frozenset(a) | frozenset(b),
|
|
2656
|
+
list_of_sets, frozenset())
|
|
2657
|
+
|
|
2658
|
+
|
|
2659
|
+
def _concatenate(list_of_iters):
|
|
2660
|
+
"""
|
|
2661
|
+
Return the concatenation of a list of iterables as a tuple.
|
|
2662
|
+
|
|
2663
|
+
EXAMPLES::
|
|
2664
|
+
|
|
2665
|
+
sage: from sage.combinat.multiset_partition_into_sets_ordered import _concatenate
|
|
2666
|
+
sage: L = ([1,2,3], Set([4,5,6]), [], range(7,11))
|
|
2667
|
+
sage: _concatenate(L)
|
|
2668
|
+
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
|
|
2669
|
+
"""
|
|
2670
|
+
return tuple([val for block in list_of_iters for val in block])
|
|
2671
|
+
|
|
2672
|
+
|
|
2673
|
+
def _is_finite(constraints):
|
|
2674
|
+
"""
|
|
2675
|
+
Return ``True`` if the dictionary ``constraints`` corresponds to
|
|
2676
|
+
a finite collection of ordered multiset partitions into sets.
|
|
2677
|
+
|
|
2678
|
+
If either ``weight`` or ``size`` is among the constraints, then
|
|
2679
|
+
the constraints represent a finite collection of ordered multiset
|
|
2680
|
+
partitions into sets. If both are absent, one needs ``alphabet`` to be
|
|
2681
|
+
present (plus a bound on length or order) in order to have a
|
|
2682
|
+
finite collection of ordered multiset partitions into sets.
|
|
2683
|
+
|
|
2684
|
+
EXAMPLES::
|
|
2685
|
+
|
|
2686
|
+
sage: from sage.combinat.multiset_partition_into_sets_ordered import _is_finite
|
|
2687
|
+
sage: W = {"weight": {1:3, 2:3, 4:1}, "length": 5}
|
|
2688
|
+
sage: S = {"size": 44, "min_length": 5}
|
|
2689
|
+
sage: AO = {"alphabet": range(44), "max_order": 5}
|
|
2690
|
+
sage: all(_is_finite(constr) for constr in (W, S, AO))
|
|
2691
|
+
True
|
|
2692
|
+
sage: AL = {"alphabet": range(44), "min_order": 5}
|
|
2693
|
+
sage: _is_finite(AL)
|
|
2694
|
+
False
|
|
2695
|
+
"""
|
|
2696
|
+
if "weight" in constraints or "size" in constraints:
|
|
2697
|
+
return True
|
|
2698
|
+
elif "alphabet" in constraints:
|
|
2699
|
+
# Assume the alphabet is finite
|
|
2700
|
+
Bounds = set(["length", "max_length", "order", "max_order"])
|
|
2701
|
+
return Bounds.intersection(set(constraints)) != set()
|
|
2702
|
+
|
|
2703
|
+
|
|
2704
|
+
def _base_iterator(constraints):
|
|
2705
|
+
"""
|
|
2706
|
+
Return a base iterator for ordered multiset partitions into sets or ``None``.
|
|
2707
|
+
|
|
2708
|
+
If the keys within ``constraints`` dictionary correspond to a finite set
|
|
2709
|
+
of ordered multiset partitions into sets, return an iterator. Else,
|
|
2710
|
+
return ``None``.
|
|
2711
|
+
|
|
2712
|
+
OUTPUT:
|
|
2713
|
+
|
|
2714
|
+
Tuples of ``frozenset`` objects representing ordered multiset partitions
|
|
2715
|
+
into sets.
|
|
2716
|
+
|
|
2717
|
+
EXAMPLES:
|
|
2718
|
+
|
|
2719
|
+
If key ``weight`` is present, ignore all other constraints
|
|
2720
|
+
(passes to ``_iterator_weight``)::
|
|
2721
|
+
|
|
2722
|
+
sage: from sage.combinat.multiset_partition_into_sets_ordered import _base_iterator
|
|
2723
|
+
sage: OMP = OrderedMultisetPartitionIntoSets
|
|
2724
|
+
sage: constraints = {"weight": {1:3, 2:3, 4:1}, "length": 5}
|
|
2725
|
+
sage: it = _base_iterator(constraints)
|
|
2726
|
+
sage: sorted(OMP(next(it)) for _ in range(4)) # note the partitions of length 6 and 7
|
|
2727
|
+
[[{1}, {1}, {1}, {2}, {2}, {2}, {4}],
|
|
2728
|
+
[{1}, {1}, {1}, {2}, {2}, {2,4}],
|
|
2729
|
+
[{1}, {1}, {1,2}, {2}, {2}, {4}],
|
|
2730
|
+
[{1}, {1}, {1,2}, {2}, {2,4}]]
|
|
2731
|
+
|
|
2732
|
+
If key ``size`` is present, pass to ``_iterator_size``, which then
|
|
2733
|
+
takes into account whether or not keys ``length`` and ``alphabet``
|
|
2734
|
+
are among the constraints::
|
|
2735
|
+
|
|
2736
|
+
sage: constraints = {"size": 5}
|
|
2737
|
+
sage: it = _base_iterator(constraints)
|
|
2738
|
+
sage: [OMP(next(it)) for _ in range(8)]
|
|
2739
|
+
[[{5}], [{2,3}], [{1,4}], [{4}, {1}], [{1,3}, {1}],
|
|
2740
|
+
[{3}, {2}], [{1,2}, {2}], [{3}, {1}, {1}]]
|
|
2741
|
+
|
|
2742
|
+
sage: constraintsL = {"size": 6, "length":2}
|
|
2743
|
+
sage: it = _base_iterator(constraintsL)
|
|
2744
|
+
sage: [OMP(next(it)) for _ in range(8)]
|
|
2745
|
+
[[{5}, {1}], [{2,3}, {1}], [{1,4}, {1}], [{4}, {2}],
|
|
2746
|
+
[{1,3}, {2}], [{3}, {3}], [{3}, {1,2}], [{1,2}, {3}]]
|
|
2747
|
+
|
|
2748
|
+
sage: constraintsA = {"size": 6, "alphabet":frozenset([2, 3])}
|
|
2749
|
+
sage: it = _base_iterator(constraintsA)
|
|
2750
|
+
sage: list(it)
|
|
2751
|
+
[(frozenset({3}), frozenset({3})),
|
|
2752
|
+
(frozenset({2}), frozenset({2}), frozenset({2}))]
|
|
2753
|
+
|
|
2754
|
+
If key ``alphabet`` is present, the slice may still be infinite, in
|
|
2755
|
+
which case ``None`` is returned. Else, use to ``_iterator_order``::
|
|
2756
|
+
|
|
2757
|
+
sage: constraints = {"alphabet": frozenset([3, 4]), "min_length":2}
|
|
2758
|
+
sage: _base_iterator(constraints) is None
|
|
2759
|
+
True
|
|
2760
|
+
sage: constraints = {"alphabet": frozenset([3, 4]), "max_length":2}
|
|
2761
|
+
sage: it = _base_iterator(constraints)
|
|
2762
|
+
sage: list(map(OMP, it))
|
|
2763
|
+
[[], [{3}], [{4}], [{3,4}], [{3}, {3}], [{3}, {4}],
|
|
2764
|
+
[{4}, {3}], [{4}, {4}], [{3,4}, {3}], [{3,4}, {4}],
|
|
2765
|
+
[{3}, {3,4}], [{4}, {3,4}], [{3,4}, {3,4}]]
|
|
2766
|
+
"""
|
|
2767
|
+
if "weight" in constraints:
|
|
2768
|
+
return _iterator_weight(constraints["weight"])
|
|
2769
|
+
elif "size" in constraints:
|
|
2770
|
+
return _iterator_size(constraints["size"],
|
|
2771
|
+
constraints.get("length",None), constraints.get("alphabet",None))
|
|
2772
|
+
elif "alphabet" in constraints:
|
|
2773
|
+
A = constraints["alphabet"]
|
|
2774
|
+
# assumes `alphabet` is finite
|
|
2775
|
+
min_k = constraints.get("min_length", 0)
|
|
2776
|
+
max_k = constraints.get("max_length", infinity)
|
|
2777
|
+
min_ord = constraints.get("min_order", 0)
|
|
2778
|
+
max_ord = constraints.get("max_order", max_k * len(A))
|
|
2779
|
+
max_k = min(max_k, max_ord)
|
|
2780
|
+
if "length" in constraints:
|
|
2781
|
+
min_k = max_k = constraints["length"]
|
|
2782
|
+
min_ord = max(min_ord, min_k)
|
|
2783
|
+
max_ord = min(max_ord, len(A) * max_k)
|
|
2784
|
+
if "order" in constraints:
|
|
2785
|
+
min_ord = max_ord = constraints["order"]
|
|
2786
|
+
max_k = min(max_k, max_ord)
|
|
2787
|
+
if min_ord:
|
|
2788
|
+
min_k = max(1, min_k, min_ord // len(A))
|
|
2789
|
+
if infinity not in (max_k, max_ord):
|
|
2790
|
+
return chain(*(_iterator_order(A, ord, range(min_k, max_k + 1))
|
|
2791
|
+
for ord in range(min_ord, max_ord + 1)))
|
|
2792
|
+
# else
|
|
2793
|
+
return None
|
|
2794
|
+
|
|
2795
|
+
|
|
2796
|
+
def _iterator_weight(weight):
|
|
2797
|
+
"""
|
|
2798
|
+
An iterator for the ordered multiset partitions into sets with weight given by
|
|
2799
|
+
the dictionary (or weak composition) ``weight``.
|
|
2800
|
+
|
|
2801
|
+
The dictionary ``weight`` may contain values equal to `0`;
|
|
2802
|
+
the corresponding keys are ignored.
|
|
2803
|
+
|
|
2804
|
+
OUTPUT:
|
|
2805
|
+
|
|
2806
|
+
Tuples of ``frozenset`` objects representing ordered multiset partitions
|
|
2807
|
+
into sets.
|
|
2808
|
+
|
|
2809
|
+
EXAMPLES::
|
|
2810
|
+
|
|
2811
|
+
sage: from sage.combinat.multiset_partition_into_sets_ordered import _iterator_weight
|
|
2812
|
+
sage: weight = {1:2, 'b':1}
|
|
2813
|
+
sage: OMP = OrderedMultisetPartitionsIntoSets(weight)
|
|
2814
|
+
sage: l = list(_iterator_weight(weight))
|
|
2815
|
+
|
|
2816
|
+
sage: sorted(map(OMP, l), key=str) == sorted(map(OMP,
|
|
2817
|
+
....: [[{1}, {1}, {'b'}], [{1}, {1,'b'}], [{1}, {'b'}, {1}],
|
|
2818
|
+
....: [{1,'b'}, {1}], [{'b'}, {1}, {1}]]), key=str)
|
|
2819
|
+
True
|
|
2820
|
+
sage: OMP = OrderedMultisetPartitionsIntoSets({1:3, 3:1})
|
|
2821
|
+
sage: list(map(OMP, _iterator_weight([3,0,1])))
|
|
2822
|
+
[[{1}, {1}, {1}, {3}], [{1}, {1}, {1,3}], [{1}, {1}, {3}, {1}],
|
|
2823
|
+
[{1}, {1,3}, {1}], [{1}, {3}, {1}, {1}],
|
|
2824
|
+
[{1,3}, {1}, {1}], [{3}, {1}, {1}, {1}]]
|
|
2825
|
+
|
|
2826
|
+
TESTS::
|
|
2827
|
+
|
|
2828
|
+
sage: list(_iterator_weight([2,0,1]))
|
|
2829
|
+
[(frozenset({1}), frozenset({1}), frozenset({3})),
|
|
2830
|
+
(frozenset({1}), frozenset({1, 3})),
|
|
2831
|
+
(frozenset({1}), frozenset({3}), frozenset({1})),
|
|
2832
|
+
(frozenset({1, 3}), frozenset({1})),
|
|
2833
|
+
(frozenset({3}), frozenset({1}), frozenset({1}))]
|
|
2834
|
+
sage: list(_iterator_weight([]))
|
|
2835
|
+
[()]
|
|
2836
|
+
"""
|
|
2837
|
+
# "weight" should be a dict mapping keys to weights
|
|
2838
|
+
if isinstance(weight, (list, tuple)):
|
|
2839
|
+
weight = {k+1: val for k, val in enumerate(weight) if val}
|
|
2840
|
+
|
|
2841
|
+
# We first map the arbitrary keys to integers to combat unreliable
|
|
2842
|
+
# sorting behavior.
|
|
2843
|
+
keys = tuple(set(weight))
|
|
2844
|
+
multiset = []
|
|
2845
|
+
for i, key in enumerate(keys):
|
|
2846
|
+
multiset += [i] * weight[key]
|
|
2847
|
+
|
|
2848
|
+
# We build ordered multiset partitions into sets of `X` by
|
|
2849
|
+
# permutation + deconcatenation
|
|
2850
|
+
for alpha in Permutations_mset(multiset):
|
|
2851
|
+
co = _break_at_descents(alpha, weak=True)
|
|
2852
|
+
for A in OrderedMultisetPartitionIntoSets(co).finer(strong=True):
|
|
2853
|
+
B = tuple([frozenset([keys[i] for i in block]) for block in A])
|
|
2854
|
+
yield B
|
|
2855
|
+
|
|
2856
|
+
|
|
2857
|
+
def _iterator_size(size, length=None, alphabet=None):
|
|
2858
|
+
r"""
|
|
2859
|
+
An iterator for the ordered multiset partitions into sets of integer `n`.
|
|
2860
|
+
|
|
2861
|
+
The degree `n` part of ordered multiset partitions into sets contains all
|
|
2862
|
+
sequences of subsets of `\NN_+` whose total sum adds up to `n`.
|
|
2863
|
+
|
|
2864
|
+
If optional argument ``alphabet`` is given, it should be a ``Set`` object.
|
|
2865
|
+
Then only yield those `c` with all letters taken from ``alphabet``.
|
|
2866
|
+
|
|
2867
|
+
OUTPUT:
|
|
2868
|
+
|
|
2869
|
+
Tuples of ``frozenset`` objects representing ordered multiset partitions
|
|
2870
|
+
into sets.
|
|
2871
|
+
|
|
2872
|
+
TESTS::
|
|
2873
|
+
|
|
2874
|
+
sage: from sage.combinat.multiset_partition_into_sets_ordered import _iterator_size
|
|
2875
|
+
sage: OMP = OrderedMultisetPartitionsIntoSets(3)
|
|
2876
|
+
sage: list(map(OMP, _iterator_size(3)))
|
|
2877
|
+
[[{3}], [{1,2}], [{2}, {1}], [{1}, {2}], [{1}, {1}, {1}]]
|
|
2878
|
+
|
|
2879
|
+
sage: OMP = OrderedMultisetPartitionsIntoSets(5, alphabet=(1,3))
|
|
2880
|
+
sage: list(map(OMP, _iterator_size(5, alphabet={1,3})))
|
|
2881
|
+
[[{1,3}, {1}], [{3}, {1}, {1}], [{1}, {1,3}], [{1}, {3}, {1}],
|
|
2882
|
+
[{1}, {1}, {3}], [{1}, {1}, {1}, {1}, {1}]]
|
|
2883
|
+
|
|
2884
|
+
sage: list(_iterator_size(2))
|
|
2885
|
+
[(frozenset({2}),), (frozenset({1}), frozenset({1}))]
|
|
2886
|
+
"""
|
|
2887
|
+
# iteration scheme:
|
|
2888
|
+
# - start from an integer composition ``alpha``.
|
|
2889
|
+
# - for each ``a`` in ``alpha``, pick distinct integers that sum to ``a``
|
|
2890
|
+
if alphabet:
|
|
2891
|
+
min_p = min(alphabet)
|
|
2892
|
+
max_p = max(alphabet)
|
|
2893
|
+
for alpha in IntegerListsLex(size, length=length, min_part=1,
|
|
2894
|
+
max_part=min(size, sum(alphabet))):
|
|
2895
|
+
for p in product(*[IntegerListsLex(a, min_slope=1,
|
|
2896
|
+
min_part=min_p,
|
|
2897
|
+
max_part=min(a, max_p))
|
|
2898
|
+
for a in alpha]):
|
|
2899
|
+
if frozenset(_concatenate(p)).issubset(frozenset(alphabet)):
|
|
2900
|
+
yield tuple(frozenset(k) for k in p)
|
|
2901
|
+
else:
|
|
2902
|
+
for alpha in IntegerListsLex(size, length=length, min_part=1, max_part=size):
|
|
2903
|
+
for p in product(*[IntegerListsLex(a, min_slope=1,
|
|
2904
|
+
min_part=1) for a in alpha]):
|
|
2905
|
+
yield tuple(frozenset(k) for k in p)
|
|
2906
|
+
|
|
2907
|
+
|
|
2908
|
+
def _iterator_order(A, d, lengths=None):
|
|
2909
|
+
"""
|
|
2910
|
+
An iterator for the ordered multiset partitions into sets of order `d`
|
|
2911
|
+
over alphabet `A`.
|
|
2912
|
+
|
|
2913
|
+
If optional argument ``lengths`` is given, it should be a list of integers.
|
|
2914
|
+
Then only yield those ordered multiset partitions into sets with length
|
|
2915
|
+
in ``lengths``.
|
|
2916
|
+
|
|
2917
|
+
OUTPUT:
|
|
2918
|
+
|
|
2919
|
+
Tuples of ``frozenset`` objects representing ordered multiset partitions
|
|
2920
|
+
into sets.
|
|
2921
|
+
|
|
2922
|
+
TESTS::
|
|
2923
|
+
|
|
2924
|
+
sage: from sage.combinat.multiset_partition_into_sets_ordered import _iterator_order
|
|
2925
|
+
sage: OMP = OrderedMultisetPartitionsIntoSets([1,4], 3)
|
|
2926
|
+
sage: list(map(OMP, _iterator_order({1,4}, 3)))
|
|
2927
|
+
[[{1,4}, {1}], [{1,4}, {4}], [{1}, {1,4}], [{4}, {1,4}], [{1}, {1}, {1}],
|
|
2928
|
+
[{1}, {1}, {4}], [{1}, {4}, {1}], [{1}, {4}, {4}], [{4}, {1}, {1}],
|
|
2929
|
+
[{4}, {1}, {4}], [{4}, {4}, {1}], [{4}, {4}, {4}]]
|
|
2930
|
+
sage: list(map(OMP, _iterator_order([1,4], 3, [3])))
|
|
2931
|
+
[[{1}, {1}, {1}], [{1}, {1}, {4}], [{1}, {4}, {1}], [{1}, {4}, {4}],
|
|
2932
|
+
[{4}, {1}, {1}], [{4}, {1}, {4}], [{4}, {4}, {1}], [{4}, {4}, {4}]]
|
|
2933
|
+
|
|
2934
|
+
sage: OMP = OrderedMultisetPartitionsIntoSets([1,2,4], 3)
|
|
2935
|
+
sage: list(map(OMP, _iterator_order([1,2,4], 3, [1,2])))[:10]
|
|
2936
|
+
[[{1,2,4}], [{1,2}, {1}], [{1,2}, {2}], [{1,2}, {4}], [{1,4}, {1}],
|
|
2937
|
+
[{1,4}, {2}], [{1,4}, {4}], [{2,4}, {1}], [{2,4}, {2}], [{2,4}, {4}]]
|
|
2938
|
+
|
|
2939
|
+
sage: list(_iterator_order([1,4], 3, [1]))
|
|
2940
|
+
[]
|
|
2941
|
+
sage: list(_iterator_order([1,4], 3, [2]))
|
|
2942
|
+
[(frozenset({1, 4}), frozenset({1})), (frozenset({1, 4}), frozenset({4})),
|
|
2943
|
+
(frozenset({1}), frozenset({1, 4})), (frozenset({4}), frozenset({1, 4}))]
|
|
2944
|
+
sage: list(_iterator_order([1,4], 3, [4]))
|
|
2945
|
+
[]
|
|
2946
|
+
sage: list(_iterator_order([1,4], 0, [3]))
|
|
2947
|
+
[]
|
|
2948
|
+
sage: list(_iterator_order([1,4], 0, [0,3]))
|
|
2949
|
+
[()]
|
|
2950
|
+
sage: list(_iterator_order([1,4], 0))
|
|
2951
|
+
[()]
|
|
2952
|
+
"""
|
|
2953
|
+
A = frozenset(A)
|
|
2954
|
+
|
|
2955
|
+
# iteration scheme:
|
|
2956
|
+
# start from an integer composition ``alpha`` of ``d``.
|
|
2957
|
+
# for each ``a`` in ``alpha``, pick ``a`` letters from ``A``
|
|
2958
|
+
n = len(A)
|
|
2959
|
+
if not lengths:
|
|
2960
|
+
if d:
|
|
2961
|
+
lengths = range(max(1, d // n), d+1)
|
|
2962
|
+
else:
|
|
2963
|
+
lengths = (0,)
|
|
2964
|
+
|
|
2965
|
+
for k in lengths:
|
|
2966
|
+
if not k and not d:
|
|
2967
|
+
yield ()
|
|
2968
|
+
else:
|
|
2969
|
+
for alpha in IntegerListsLex(d, length=k, min_part=1, max_part=n):
|
|
2970
|
+
for co in product(*[Subsets_sk(A, a) for a in alpha]):
|
|
2971
|
+
yield tuple(frozenset(X) for X in co)
|
|
2972
|
+
|
|
2973
|
+
|
|
2974
|
+
def _descents(w) -> list:
|
|
2975
|
+
r"""
|
|
2976
|
+
Return descent positions in the word ``w``.
|
|
2977
|
+
|
|
2978
|
+
EXAMPLES::
|
|
2979
|
+
|
|
2980
|
+
sage: from sage.combinat.multiset_partition_into_sets_ordered import _descents
|
|
2981
|
+
sage: _descents([1, 2, 3, 2, 2, 1, 4, 3]) == [2, 4, 6]
|
|
2982
|
+
True
|
|
2983
|
+
sage: _descents([])
|
|
2984
|
+
[]
|
|
2985
|
+
"""
|
|
2986
|
+
return [j for j in range(len(w) - 1) if w[j] > w[j + 1]]
|
|
2987
|
+
|
|
2988
|
+
|
|
2989
|
+
def _break_at_descents(alpha, weak=True):
|
|
2990
|
+
r"""
|
|
2991
|
+
Return the deconcatenation of the composition ``alpha`` at its
|
|
2992
|
+
set of descents.
|
|
2993
|
+
|
|
2994
|
+
OUTPUT:
|
|
2995
|
+
|
|
2996
|
+
A list `[a_1, \ldots, a_r]` of nonempty lists whose concatenation
|
|
2997
|
+
is ``list(alpha)`` with the property that ``alpha[i] >= alpha[i+1]``
|
|
2998
|
+
if and only if positions `i` and `i+1` correspond to different
|
|
2999
|
+
lists. (Specifically, ``alpha[i]`` is the last letter of some
|
|
3000
|
+
`a_j` and ``alpha[i+1]`` is the first letter of `a_{j+1}`.)
|
|
3001
|
+
|
|
3002
|
+
If the optional argument ``weak`` is ``False``, then only make
|
|
3003
|
+
breaks when ``alpha[i] > alpha[i+1]``.
|
|
3004
|
+
|
|
3005
|
+
EXAMPLES::
|
|
3006
|
+
|
|
3007
|
+
sage: from sage.combinat.multiset_partition_into_sets_ordered import _break_at_descents
|
|
3008
|
+
sage: _break_at_descents([1, 2, 3, 2, 2, 1, 4, 3])
|
|
3009
|
+
[[1, 2, 3], [2], [2], [1, 4], [3]]
|
|
3010
|
+
sage: _break_at_descents([1, 2, 3, 2, 2, 1, 4, 3], weak=False)
|
|
3011
|
+
[[1, 2, 3], [2, 2], [1, 4], [3]]
|
|
3012
|
+
sage: _break_at_descents([])
|
|
3013
|
+
[]
|
|
3014
|
+
"""
|
|
3015
|
+
if not alpha:
|
|
3016
|
+
return []
|
|
3017
|
+
|
|
3018
|
+
Blocks = []
|
|
3019
|
+
block = [alpha[0]]
|
|
3020
|
+
for i in range(1,len(alpha)):
|
|
3021
|
+
if (alpha[i-1] > alpha[i]) or (alpha[i-1] == alpha[i] and weak):
|
|
3022
|
+
Blocks.append(block)
|
|
3023
|
+
block = [alpha[i]]
|
|
3024
|
+
else:
|
|
3025
|
+
block.append(alpha[i])
|
|
3026
|
+
if block:
|
|
3027
|
+
Blocks.append(block)
|
|
3028
|
+
return Blocks
|
|
3029
|
+
|
|
3030
|
+
|
|
3031
|
+
def _refine_block(S, strong=False):
|
|
3032
|
+
r"""
|
|
3033
|
+
Return the list of all possible refinements of a set `S`.
|
|
3034
|
+
|
|
3035
|
+
A refinement of `S` is a tuple of nonempty subsets whose union is `S`.
|
|
3036
|
+
|
|
3037
|
+
If optional argument ``strong`` is set to ``True``, then only those
|
|
3038
|
+
refinements that are deconcatenations of the list ``sorted(S)`` are returned.
|
|
3039
|
+
|
|
3040
|
+
(The subsets involved are stored as ``frozenset`` objects.)
|
|
3041
|
+
|
|
3042
|
+
EXAMPLES::
|
|
3043
|
+
|
|
3044
|
+
sage: from sage.combinat.multiset_partition_into_sets_ordered import _refine_block
|
|
3045
|
+
sage: _refine_block([1, 2], strong=True)
|
|
3046
|
+
[(frozenset({1}), frozenset({2})), (frozenset({1, 2}),)]
|
|
3047
|
+
|
|
3048
|
+
sage: [tuple(Set(x) for x in tupl) for tupl in _refine_block([1, 2, 3], strong=True)]
|
|
3049
|
+
[({1}, {2}, {3}), ({1}, {2, 3}), ({1, 2}, {3}), ({1, 2, 3},)]
|
|
3050
|
+
sage: [tuple(Set(x) for x in tupl) for tupl in _refine_block([1, 2, 3])]
|
|
3051
|
+
[({3}, {2}, {1}), ({2}, {3}, {1}), ({3}, {1}, {2}),
|
|
3052
|
+
({3}, {1, 2}), ({2}, {1}, {3}), ({2}, {1, 3}),
|
|
3053
|
+
({2, 3}, {1}), ({1}, {3}, {2}), ({1}, {2}, {3}),
|
|
3054
|
+
({1}, {2, 3}), ({1, 3}, {2}), ({1, 2}, {3}),
|
|
3055
|
+
({1, 2, 3},)]
|
|
3056
|
+
|
|
3057
|
+
TESTS::
|
|
3058
|
+
|
|
3059
|
+
sage: len(_refine_block([1, 2, 3, 4])) == 1 + binomial(4,1)*2 + binomial(4,2) + binomial(4,2)*factorial(3) + factorial(4)
|
|
3060
|
+
True
|
|
3061
|
+
sage: len(_refine_block([1, 2, 3, 4], strong=True)) == 2**3
|
|
3062
|
+
True
|
|
3063
|
+
sage: _refine_block([])
|
|
3064
|
+
Traceback (most recent call last):
|
|
3065
|
+
...
|
|
3066
|
+
ValueError: S (=[]) must be nonempty
|
|
3067
|
+
"""
|
|
3068
|
+
if not S:
|
|
3069
|
+
raise ValueError("S (=%s) must be nonempty" % S)
|
|
3070
|
+
|
|
3071
|
+
if all(s in ZZ for s in S):
|
|
3072
|
+
X = sorted(S)
|
|
3073
|
+
else:
|
|
3074
|
+
X = sorted(S, key=str)
|
|
3075
|
+
n = len(X)
|
|
3076
|
+
out = []
|
|
3077
|
+
if not strong:
|
|
3078
|
+
WordSet = IntegerListsLex(min_part=0, max_part=n-1, length=n)
|
|
3079
|
+
else:
|
|
3080
|
+
WordSet = IntegerListsLex(min_part=0, max_part=n-1, length=n, min_slope=0)
|
|
3081
|
+
|
|
3082
|
+
for w in WordSet:
|
|
3083
|
+
if _is_initial_segment(sorted(set(w))):
|
|
3084
|
+
a = [frozenset() for _ in range(max(w)+1)]
|
|
3085
|
+
for pos in range(n):
|
|
3086
|
+
a[w[pos]] = a[w[pos]].union({X[pos]})
|
|
3087
|
+
out.append(tuple(a))
|
|
3088
|
+
return out
|
|
3089
|
+
|
|
3090
|
+
|
|
3091
|
+
def _is_initial_segment(lst):
|
|
3092
|
+
r"""
|
|
3093
|
+
Return ``True`` if ``lst`` is an interval in `\ZZ` of the form `[0, 1, \ldots, n]`.
|
|
3094
|
+
|
|
3095
|
+
EXAMPLES::
|
|
3096
|
+
|
|
3097
|
+
sage: from sage.combinat.multiset_partition_into_sets_ordered import _is_initial_segment
|
|
3098
|
+
sage: _is_initial_segment([1, 2, 3])
|
|
3099
|
+
False
|
|
3100
|
+
sage: _is_initial_segment([0, 1, 2, 3])
|
|
3101
|
+
True
|
|
3102
|
+
sage: _is_initial_segment([0])
|
|
3103
|
+
True
|
|
3104
|
+
"""
|
|
3105
|
+
return list(range(max(lst)+1)) == lst
|
|
3106
|
+
|
|
3107
|
+
|
|
3108
|
+
def _split_block(S, k=2):
|
|
3109
|
+
"""
|
|
3110
|
+
Return the list of all possible splittings of a set `S` into `k` parts.
|
|
3111
|
+
|
|
3112
|
+
A splitting of `S` is a tuple of (possibly empty) subsets whose union is `S`.
|
|
3113
|
+
|
|
3114
|
+
(The subsets involved are stored as ``frozenset`` objects.)
|
|
3115
|
+
|
|
3116
|
+
EXAMPLES::
|
|
3117
|
+
|
|
3118
|
+
sage: from sage.combinat.multiset_partition_into_sets_ordered import _split_block
|
|
3119
|
+
sage: S = [1, 2, 3]
|
|
3120
|
+
sage: _split_block(S, 1)
|
|
3121
|
+
[(frozenset({1, 2, 3}),)]
|
|
3122
|
+
|
|
3123
|
+
sage: [tuple(Set(x) for x in tupl) for tupl in _split_block(S, 2)]
|
|
3124
|
+
[({}, {1, 2, 3}), ({3}, {1, 2}), ({2}, {1, 3}), ({2, 3}, {1}),
|
|
3125
|
+
({1}, {2, 3}), ({1, 3}, {2}), ({1, 2}, {3}), ({1, 2, 3}, {})]
|
|
3126
|
+
sage: [tuple(Set(x) for x in tupl) for tupl in _split_block({2, 4}, 3)]
|
|
3127
|
+
[({}, {}, {2, 4}), ({}, {4}, {2}), ({4}, {}, {2}),
|
|
3128
|
+
({}, {2}, {4}), ({}, {2, 4}, {}), ({4}, {2}, {}),
|
|
3129
|
+
({2}, {}, {4}), ({2}, {4}, {}), ({2, 4}, {}, {})]
|
|
3130
|
+
"""
|
|
3131
|
+
if all(s in ZZ for s in S):
|
|
3132
|
+
X = sorted(S)
|
|
3133
|
+
else:
|
|
3134
|
+
X = sorted(S, key=str)
|
|
3135
|
+
n = len(X)
|
|
3136
|
+
out = []
|
|
3137
|
+
for w in IntegerListsLex(min_part=0, max_part=k-1, length=n):
|
|
3138
|
+
a = [frozenset() for _ in range(k)]
|
|
3139
|
+
for pos in range(n):
|
|
3140
|
+
a[w[pos]] = a[w[pos]].union({X[pos]})
|
|
3141
|
+
out.append(tuple(a))
|
|
3142
|
+
return out
|
|
3143
|
+
|
|
3144
|
+
|
|
3145
|
+
def _to_minimaj_blocks(T):
|
|
3146
|
+
r"""
|
|
3147
|
+
Return a tuple of tuples, representing an ordered multiset partition into sets
|
|
3148
|
+
in the minimaj ordering on blocks
|
|
3149
|
+
|
|
3150
|
+
INPUT:
|
|
3151
|
+
|
|
3152
|
+
- ``T`` -- a sequence of row words corresponding to (skew-)tableaux
|
|
3153
|
+
|
|
3154
|
+
OUTPUT:
|
|
3155
|
+
|
|
3156
|
+
The minimaj bijection `\phi^{-1}` of [BCHOPSY2017]_ applied to ``T``.
|
|
3157
|
+
|
|
3158
|
+
EXAMPLES::
|
|
3159
|
+
|
|
3160
|
+
sage: from sage.combinat.multiset_partition_into_sets_ordered import _to_minimaj_blocks
|
|
3161
|
+
sage: co = OrderedMultisetPartitionsIntoSets(14).an_element(); co
|
|
3162
|
+
[{2,3}, {2,3}, {4}]
|
|
3163
|
+
sage: co.to_tableaux_words()
|
|
3164
|
+
[[3, 2], [], [3, 2, 4]]
|
|
3165
|
+
sage: _to_minimaj_blocks(co.to_tableaux_words()) == co.minimaj_blocks()
|
|
3166
|
+
True
|
|
3167
|
+
"""
|
|
3168
|
+
mu = [(i,) for i in T[-1]]
|
|
3169
|
+
breaks = [0] + _descents(T[-1]) + [len(mu)-1]
|
|
3170
|
+
T = [T[i][::-1] for i in range(len(T)-1)][::-1]
|
|
3171
|
+
for f in range(len(breaks)-1):
|
|
3172
|
+
for j in range(breaks[f],breaks[f+1]+1):
|
|
3173
|
+
mu[j] += tuple(i for i in T[f] if (mu[j][0] < i or j == breaks[f])
|
|
3174
|
+
and (j == breaks[f+1] or i <= mu[j+1][0]))
|
|
3175
|
+
return tuple(mu)
|
|
3176
|
+
|
|
3177
|
+
|
|
3178
|
+
###############
|
|
3179
|
+
|
|
3180
|
+
class MinimajCrystal(UniqueRepresentation, Parent):
|
|
3181
|
+
r"""
|
|
3182
|
+
Crystal of ordered multiset partitions into sets with `ell` letters from
|
|
3183
|
+
alphabet `\{1, 2, \ldots, n\}` divided into `k` blocks.
|
|
3184
|
+
|
|
3185
|
+
Elements are represented in the minimaj ordering of blocks as in
|
|
3186
|
+
Benkart et al. [BCHOPSY2017]_.
|
|
3187
|
+
|
|
3188
|
+
.. NOTE::
|
|
3189
|
+
|
|
3190
|
+
Elements are not stored internally as ordered multiset partitions
|
|
3191
|
+
into sets, but as certain (pairs of) words stemming from the minimaj
|
|
3192
|
+
bijection `\phi` of [BCHOPSY2017]_. See
|
|
3193
|
+
:class:`sage.combinat.multiset_partition_into_sets_ordered.MinimajCrystal.Element`
|
|
3194
|
+
for further details.
|
|
3195
|
+
|
|
3196
|
+
AUTHORS:
|
|
3197
|
+
|
|
3198
|
+
- Anne Schilling (2018): initial draft
|
|
3199
|
+
- Aaron Lauve (2018): changed to use ``Letters`` crystal for elements
|
|
3200
|
+
|
|
3201
|
+
EXAMPLES::
|
|
3202
|
+
|
|
3203
|
+
sage: list(crystals.Minimaj(2,3,2)) # needs sage.modules
|
|
3204
|
+
[((2, 1), (1,)), ((2,), (1, 2)), ((1,), (1, 2)), ((1, 2), (2,))]
|
|
3205
|
+
|
|
3206
|
+
sage: b = crystals.Minimaj(3, 5, 2).an_element(); b # needs sage.modules
|
|
3207
|
+
((2, 3, 1), (1, 2))
|
|
3208
|
+
sage: b.f(2) # needs sage.modules
|
|
3209
|
+
((2, 3, 1), (1, 3))
|
|
3210
|
+
sage: b.e(2) # needs sage.modules
|
|
3211
|
+
"""
|
|
3212
|
+
|
|
3213
|
+
def __init__(self, n, ell, k):
|
|
3214
|
+
"""
|
|
3215
|
+
Initialize ``self``.
|
|
3216
|
+
|
|
3217
|
+
TESTS::
|
|
3218
|
+
|
|
3219
|
+
sage: # needs sage.modules
|
|
3220
|
+
sage: B = crystals.Minimaj(2,3,2)
|
|
3221
|
+
sage: TestSuite(B).run()
|
|
3222
|
+
sage: B = crystals.Minimaj(3, 5, 2)
|
|
3223
|
+
sage: TestSuite(B).run()
|
|
3224
|
+
sage: list(crystals.Minimaj(2,6,3))
|
|
3225
|
+
[((1, 2), (2, 1), (1, 2))]
|
|
3226
|
+
sage: list(crystals.Minimaj(2,5,2)) # blocks too fat for alphabet
|
|
3227
|
+
[]
|
|
3228
|
+
sage: list(crystals.Minimaj(4,2,3)) # more blocks than letters
|
|
3229
|
+
Traceback (most recent call last):
|
|
3230
|
+
...
|
|
3231
|
+
ValueError: n (=4), ell (=2), and k (=3) must all be positive integers
|
|
3232
|
+
"""
|
|
3233
|
+
Parent.__init__(self, category=ClassicalCrystals())
|
|
3234
|
+
self.n = n
|
|
3235
|
+
self.ell = ell
|
|
3236
|
+
self.k = k
|
|
3237
|
+
if not all([n in ZZ, ell in ZZ, k in ZZ]):
|
|
3238
|
+
raise TypeError("n (=%s), ell (=%s), and k (=%s) must all be positive integers" % (n, ell, k))
|
|
3239
|
+
if not all([n > 0, ell >= k, k > 0]):
|
|
3240
|
+
raise ValueError("n (=%s), ell (=%s), and k (=%s) must all be positive integers" % (n, ell, k))
|
|
3241
|
+
self._cartan_type = CartanType(['A',n-1])
|
|
3242
|
+
B = Letters(['A', n-1])
|
|
3243
|
+
T = tensor([B]*ell)
|
|
3244
|
+
self._BT = (B, T)
|
|
3245
|
+
self._OMPs = OrderedMultisetPartitionsIntoSets(n, ell, length=k)
|
|
3246
|
+
self.module_generators = []
|
|
3247
|
+
for co in self._OMPs:
|
|
3248
|
+
t = co.to_tableaux_words()
|
|
3249
|
+
word = T(*[B(a) for a in _concatenate(t)])
|
|
3250
|
+
blocks = [len(h) for h in t]
|
|
3251
|
+
breaks = tuple([0]+running_total(blocks))
|
|
3252
|
+
mu = self.element_class(self, (word, breaks))
|
|
3253
|
+
self.module_generators.append(mu)
|
|
3254
|
+
|
|
3255
|
+
def _repr_(self):
|
|
3256
|
+
"""
|
|
3257
|
+
Return the string representation of ``self``.
|
|
3258
|
+
|
|
3259
|
+
EXAMPLES::
|
|
3260
|
+
|
|
3261
|
+
sage: B = crystals.Minimaj(3,4,2); B # needs sage.modules
|
|
3262
|
+
Minimaj Crystal of type A_2 of words of length 4 into 2 blocks
|
|
3263
|
+
"""
|
|
3264
|
+
return ("Minimaj Crystal of type A_%s of words of length %s into %s blocks"
|
|
3265
|
+
% (self.n-1, self.ell, self.k))
|
|
3266
|
+
|
|
3267
|
+
def _an_element_(self):
|
|
3268
|
+
"""
|
|
3269
|
+
Return a typical element of ``self``.
|
|
3270
|
+
|
|
3271
|
+
EXAMPLES::
|
|
3272
|
+
|
|
3273
|
+
sage: # needs sage.modules
|
|
3274
|
+
sage: B = crystals.Minimaj(4,5,3)
|
|
3275
|
+
sage: B.an_element()
|
|
3276
|
+
((2, 3, 1), (1,), (1,))
|
|
3277
|
+
sage: B = crystals.Minimaj(2,2,1)
|
|
3278
|
+
sage: B.an_element()
|
|
3279
|
+
((1, 2),)
|
|
3280
|
+
sage: B = crystals.Minimaj(1,2,1)
|
|
3281
|
+
sage: B.an_element()
|
|
3282
|
+
Traceback (most recent call last):
|
|
3283
|
+
...
|
|
3284
|
+
EmptySetError
|
|
3285
|
+
"""
|
|
3286
|
+
t = self._OMPs.an_element().to_tableaux_words()
|
|
3287
|
+
breaks = tuple([0]+running_total([len(h) for h in t]))
|
|
3288
|
+
B,T = self._BT
|
|
3289
|
+
return self.element_class(self, (T(*[B(a) for a in _concatenate(t)]), breaks))
|
|
3290
|
+
|
|
3291
|
+
def _element_constructor_(self, x):
|
|
3292
|
+
"""
|
|
3293
|
+
Build an element of Minimaj from an ordered multiset partition into sets.
|
|
3294
|
+
|
|
3295
|
+
EXAMPLES::
|
|
3296
|
+
|
|
3297
|
+
sage: # needs sage.modules
|
|
3298
|
+
sage: B1 = crystals.Minimaj(4,5,3); b = B1.an_element(); b
|
|
3299
|
+
((2, 3, 1), (1,), (1,))
|
|
3300
|
+
sage: B1._element_constructor_(list(b))
|
|
3301
|
+
((2, 3, 1), (1,), (1,))
|
|
3302
|
+
sage: B1._element_constructor_([[1,2,3], [2], [2]])
|
|
3303
|
+
((3, 1, 2), (2,), (2,))
|
|
3304
|
+
sage: B2 = crystals.Minimaj(5,5,3)
|
|
3305
|
+
sage: B2._element_constructor_(b)
|
|
3306
|
+
((2, 3, 1), (1,), (1,))
|
|
3307
|
+
"""
|
|
3308
|
+
# Allow ``x`` to be either of:
|
|
3309
|
+
# - an ordered multiset partition into sets in ``self._OMPs``;
|
|
3310
|
+
# - an element of another Minimaj crystal with
|
|
3311
|
+
# + same `ell` and `k`, and
|
|
3312
|
+
# + all letters smaller or equal to ``self._n``.
|
|
3313
|
+
x = list(x)
|
|
3314
|
+
if x in self:
|
|
3315
|
+
t = self._OMPs(x).to_tableaux_words()
|
|
3316
|
+
breaks = tuple([0]+running_total([len(h) for h in t]))
|
|
3317
|
+
B,T = self._BT
|
|
3318
|
+
return self.element_class(self, (T(*[B(a) for a in _concatenate(t)]), breaks))
|
|
3319
|
+
else:
|
|
3320
|
+
raise ValueError("cannot convert %s into an element of %s" % (x, self))
|
|
3321
|
+
|
|
3322
|
+
def __contains__(self, x):
|
|
3323
|
+
"""
|
|
3324
|
+
Return ``True`` if ``x`` is an element of ``self`` or an ordered
|
|
3325
|
+
multiset partition into sets.
|
|
3326
|
+
|
|
3327
|
+
EXAMPLES::
|
|
3328
|
+
|
|
3329
|
+
sage: # needs sage.modules
|
|
3330
|
+
sage: B1 = crystals.Minimaj(2,5,3); b1 = B1.an_element(); b1
|
|
3331
|
+
((1, 2), (2, 1), (1,))
|
|
3332
|
+
sage: B2 = crystals.Minimaj(5,5,3); b2 = B2.an_element(); b2
|
|
3333
|
+
((2, 3, 1), (1,), (1,))
|
|
3334
|
+
sage: b2a = B2(((1,2), (1,), (1,2))); b2a
|
|
3335
|
+
((2, 1), (1,), (1, 2))
|
|
3336
|
+
sage: b1 in B2
|
|
3337
|
+
True
|
|
3338
|
+
sage: b2 in B1
|
|
3339
|
+
False
|
|
3340
|
+
sage: b2a in B1
|
|
3341
|
+
True
|
|
3342
|
+
"""
|
|
3343
|
+
if isinstance(x, MinimajCrystal.Element):
|
|
3344
|
+
if x.parent() == self:
|
|
3345
|
+
return True
|
|
3346
|
+
else:
|
|
3347
|
+
return list(x) in self._OMPs
|
|
3348
|
+
else:
|
|
3349
|
+
return x in self._OMPs
|
|
3350
|
+
|
|
3351
|
+
def from_tableau(self, t):
|
|
3352
|
+
r"""
|
|
3353
|
+
Return the bijection `\phi^{-1}` of [BCHOPSY2017]_ applied to ``t``.
|
|
3354
|
+
|
|
3355
|
+
INPUT:
|
|
3356
|
+
|
|
3357
|
+
- ``t`` -- a sequence of column tableaux and a ribbon tableau
|
|
3358
|
+
|
|
3359
|
+
EXAMPLES::
|
|
3360
|
+
|
|
3361
|
+
sage: # needs sage.modules
|
|
3362
|
+
sage: B = crystals.Minimaj(3,6,3)
|
|
3363
|
+
sage: b = B.an_element(); b
|
|
3364
|
+
((3, 1, 2), (2, 1), (1,))
|
|
3365
|
+
sage: t = b.to_tableaux_words(); t
|
|
3366
|
+
[[1], [2, 1], [], [3, 2, 1]]
|
|
3367
|
+
sage: B.from_tableau(t)
|
|
3368
|
+
((3, 1, 2), (2, 1), (1,))
|
|
3369
|
+
sage: B.from_tableau(t) == b
|
|
3370
|
+
True
|
|
3371
|
+
|
|
3372
|
+
TESTS::
|
|
3373
|
+
|
|
3374
|
+
sage: # needs sage.modules
|
|
3375
|
+
sage: B = crystals.Minimaj(3,6,3)
|
|
3376
|
+
sage: all(mu == B.from_tableau(mu.to_tableaux_words()) for mu in B)
|
|
3377
|
+
True
|
|
3378
|
+
sage: t = B.an_element().to_tableaux_words()
|
|
3379
|
+
sage: B1 = crystals.Minimaj(3,6,2)
|
|
3380
|
+
sage: B1.from_tableau(t)
|
|
3381
|
+
Traceback (most recent call last):
|
|
3382
|
+
...
|
|
3383
|
+
ValueError: ((3, 1, 2), (2, 1), (1,)) is not an element of
|
|
3384
|
+
Minimaj Crystal of type A_2 of words of length 6 into 2 blocks
|
|
3385
|
+
"""
|
|
3386
|
+
mu = _to_minimaj_blocks(t)
|
|
3387
|
+
if mu in self:
|
|
3388
|
+
return self(mu)
|
|
3389
|
+
else:
|
|
3390
|
+
raise ValueError("%s is not an element of %s" % (mu, self))
|
|
3391
|
+
|
|
3392
|
+
def val(self, q='q'):
|
|
3393
|
+
r"""
|
|
3394
|
+
Return the `Val` polynomial corresponding to ``self``.
|
|
3395
|
+
|
|
3396
|
+
EXAMPLES:
|
|
3397
|
+
|
|
3398
|
+
Verifying Example 4.5 from [BCHOPSY2017]_::
|
|
3399
|
+
|
|
3400
|
+
sage: B = crystals.Minimaj(3, 4, 2) # for `Val_{4,1}^{(3)}` # needs sage.modules
|
|
3401
|
+
sage: B.val() # needs sage.modules
|
|
3402
|
+
(q^2+q+1)*s[2, 1, 1] + q*s[2, 2]
|
|
3403
|
+
"""
|
|
3404
|
+
H = [self._OMPs(list(b)) for b in self.highest_weight_vectors()]
|
|
3405
|
+
Sym = SymmetricFunctions(ZZ[q])
|
|
3406
|
+
q = Sym.base_ring().gens()[0]
|
|
3407
|
+
s = Sym.schur()
|
|
3408
|
+
return sum((q**(t.minimaj()) * s[sorted(t.weight().values(), reverse=True)]
|
|
3409
|
+
for t in H), Sym.zero())
|
|
3410
|
+
|
|
3411
|
+
class Element(ElementWrapper):
|
|
3412
|
+
r"""
|
|
3413
|
+
An element of a Minimaj crystal.
|
|
3414
|
+
|
|
3415
|
+
.. NOTE::
|
|
3416
|
+
|
|
3417
|
+
Minimaj elements `b` are stored internally as pairs
|
|
3418
|
+
``(w, breaks)``, where:
|
|
3419
|
+
|
|
3420
|
+
- ``w`` -- a word of length ``self.parent().ell`` over the
|
|
3421
|
+
letters `1` up to ``self.parent().n``;
|
|
3422
|
+
- ``breaks`` is a list of de-concatenation points to turn ``w``
|
|
3423
|
+
into a list of row words of (skew-)tableaux that represent
|
|
3424
|
+
`b` under the minimaj bijection `\phi` of [BCHOPSY2017]_.
|
|
3425
|
+
|
|
3426
|
+
The pair ``(w, breaks)`` may be recovered via ``b.value``.
|
|
3427
|
+
"""
|
|
3428
|
+
|
|
3429
|
+
def _repr_(self):
|
|
3430
|
+
"""
|
|
3431
|
+
Return the string representation of ``self``.
|
|
3432
|
+
|
|
3433
|
+
EXAMPLES::
|
|
3434
|
+
|
|
3435
|
+
sage: crystals.Minimaj(4,5,3).an_element() # needs sage.modules
|
|
3436
|
+
((2, 3, 1), (1,), (1,))
|
|
3437
|
+
"""
|
|
3438
|
+
return repr(self._minimaj_blocks_from_word_pair())
|
|
3439
|
+
|
|
3440
|
+
def __iter__(self):
|
|
3441
|
+
"""
|
|
3442
|
+
Iterate over ``self._minimaj_blocks_from_word_pair()``.
|
|
3443
|
+
|
|
3444
|
+
EXAMPLES::
|
|
3445
|
+
|
|
3446
|
+
sage: b = crystals.Minimaj(4,5,3).an_element(); b # needs sage.modules
|
|
3447
|
+
((2, 3, 1), (1,), (1,))
|
|
3448
|
+
sage: b.value # needs sage.modules
|
|
3449
|
+
([1, 3, 2, 1, 1], (0, 1, 2, 5))
|
|
3450
|
+
sage: list(b) # needs sage.modules
|
|
3451
|
+
[(2, 3, 1), (1,), (1,)]
|
|
3452
|
+
"""
|
|
3453
|
+
return self._minimaj_blocks_from_word_pair().__iter__()
|
|
3454
|
+
|
|
3455
|
+
def _latex_(self):
|
|
3456
|
+
r"""
|
|
3457
|
+
Return the latex representation of ``self``.
|
|
3458
|
+
|
|
3459
|
+
EXAMPLES::
|
|
3460
|
+
|
|
3461
|
+
sage: b = crystals.Minimaj(4,5,3).an_element(); b # needs sage.modules
|
|
3462
|
+
((2, 3, 1), (1,), (1,))
|
|
3463
|
+
sage: latex(b) # needs sage.modules
|
|
3464
|
+
\left(\left(2, 3, 1\right), \left(1\right), \left(1\right)\right)
|
|
3465
|
+
"""
|
|
3466
|
+
return latex(self._minimaj_blocks_from_word_pair())
|
|
3467
|
+
|
|
3468
|
+
def _minimaj_blocks_from_word_pair(self):
|
|
3469
|
+
"""
|
|
3470
|
+
Return the tuple of tuples (in the minimaj ordering on blocks of
|
|
3471
|
+
ordered multiset partitions into sets) corresponding to ``self``.
|
|
3472
|
+
|
|
3473
|
+
EXAMPLES::
|
|
3474
|
+
|
|
3475
|
+
sage: B = crystals.Minimaj(4,5,3) # needs sage.modules
|
|
3476
|
+
sage: b = B.an_element(); b.value # needs sage.modules
|
|
3477
|
+
([1, 3, 2, 1, 1], (0, 1, 2, 5))
|
|
3478
|
+
sage: b._minimaj_blocks_from_word_pair() # needs sage.modules
|
|
3479
|
+
((2, 3, 1), (1,), (1,))
|
|
3480
|
+
"""
|
|
3481
|
+
return _to_minimaj_blocks(self.to_tableaux_words())
|
|
3482
|
+
|
|
3483
|
+
def to_tableaux_words(self):
|
|
3484
|
+
r"""
|
|
3485
|
+
Return the image of the ordered multiset partition into sets ``self``
|
|
3486
|
+
under the minimaj bijection `\phi` of [BCHOPSY2017]_.
|
|
3487
|
+
|
|
3488
|
+
EXAMPLES::
|
|
3489
|
+
|
|
3490
|
+
sage: # needs sage.modules
|
|
3491
|
+
sage: B = crystals.Minimaj(4,5,3)
|
|
3492
|
+
sage: b = B.an_element(); b
|
|
3493
|
+
((2, 3, 1), (1,), (1,))
|
|
3494
|
+
sage: b.to_tableaux_words()
|
|
3495
|
+
[[1], [3], [2, 1, 1]]
|
|
3496
|
+
sage: b = B([[1,3,4], [3], [3]]); b
|
|
3497
|
+
((4, 1, 3), (3,), (3,))
|
|
3498
|
+
sage: b.to_tableaux_words()
|
|
3499
|
+
[[3, 1], [], [4, 3, 3]]
|
|
3500
|
+
"""
|
|
3501
|
+
w, breaks = self.value
|
|
3502
|
+
return [[ZZ(w[a].value) for a in range(breaks[j], breaks[j+1])]
|
|
3503
|
+
for j in range(len(breaks)-1)]
|
|
3504
|
+
|
|
3505
|
+
def e(self, i):
|
|
3506
|
+
r"""
|
|
3507
|
+
Return `e_i` on ``self``.
|
|
3508
|
+
|
|
3509
|
+
EXAMPLES::
|
|
3510
|
+
|
|
3511
|
+
sage: B = crystals.Minimaj(4,3,2) # needs sage.modules
|
|
3512
|
+
sage: b = B([[2,3], [3]]); b # needs sage.modules
|
|
3513
|
+
((2, 3), (3,))
|
|
3514
|
+
sage: [b.e(i) for i in range(1,4)] # needs sage.modules
|
|
3515
|
+
[((1, 3), (3,)), ((2,), (2, 3)), None]
|
|
3516
|
+
"""
|
|
3517
|
+
P = self.parent()
|
|
3518
|
+
w, breaks = self.value
|
|
3519
|
+
if w.e(i) is None:
|
|
3520
|
+
return None
|
|
3521
|
+
w = w.e(i)
|
|
3522
|
+
return P.element_class(P, (w, breaks))
|
|
3523
|
+
|
|
3524
|
+
def f(self, i):
|
|
3525
|
+
r"""
|
|
3526
|
+
Return `f_i` on ``self``.
|
|
3527
|
+
|
|
3528
|
+
EXAMPLES::
|
|
3529
|
+
|
|
3530
|
+
sage: B = crystals.Minimaj(4,3,2) # needs sage.modules
|
|
3531
|
+
sage: b = B([[2,3], [3]]); b # needs sage.modules
|
|
3532
|
+
((2, 3), (3,))
|
|
3533
|
+
sage: [b.f(i) for i in range(1,4)] # needs sage.modules
|
|
3534
|
+
[None, None, ((2, 3), (4,))]
|
|
3535
|
+
"""
|
|
3536
|
+
P = self.parent()
|
|
3537
|
+
w, breaks = self.value
|
|
3538
|
+
if w.f(i) is None:
|
|
3539
|
+
return None
|
|
3540
|
+
w = w.f(i)
|
|
3541
|
+
return P.element_class(P, (w, breaks))
|