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,1857 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-combinat
|
|
2
|
+
r"""
|
|
3
|
+
Combinatorics in Sage
|
|
4
|
+
|
|
5
|
+
This thematic tutorial is a translation by Hugh Thomas of the
|
|
6
|
+
combinatorics chapter written by Nicolas M. Thiéry in the book "Calcul
|
|
7
|
+
Mathématique avec Sage" [CMS2012]_. It covers mainly the treatment in
|
|
8
|
+
``Sage`` of the following combinatorial problems: enumeration (how
|
|
9
|
+
many elements are there in a set `S`?), listing (generate all the
|
|
10
|
+
elements of `S`, or iterate through them), and random selection
|
|
11
|
+
(choosing an element at random from a set `S` according to a given
|
|
12
|
+
distribution, for example the uniform distribution). These questions
|
|
13
|
+
arise naturally in the calculation of probabilities (what is the
|
|
14
|
+
probability in poker of obtaining a straight or a four-of-a-kind of
|
|
15
|
+
aces?), in statistical physics, and also in computer algebra (the number
|
|
16
|
+
of elements in a finite field), or in the analysis of
|
|
17
|
+
algorithms. Combinatorics covers a much wider domain (partial orders,
|
|
18
|
+
representation theory, …) for which we only give a few pointers
|
|
19
|
+
towards the possibilities offered by ``Sage``.
|
|
20
|
+
|
|
21
|
+
A characteristic of computational combinatorics is the profusion of
|
|
22
|
+
types of objects and sets that one wants to manipulate. It would be
|
|
23
|
+
impossible to describe them all or, a fortiori, to implement them all.
|
|
24
|
+
After some :ref:`section-examples`, this chapter illustrates the
|
|
25
|
+
underlying method: supplying the basic building blocks to describe
|
|
26
|
+
common combinatorial sets :ref:`section-bricks`, tools for combining
|
|
27
|
+
them to construct new examples :ref:`section-constructions`, and
|
|
28
|
+
generic algorithms for solving uniformly a large class of problems
|
|
29
|
+
:ref:`section-generic`.
|
|
30
|
+
|
|
31
|
+
This is a domain in which ``Sage`` has much more extensive capabilities
|
|
32
|
+
than most computer algebra systems, and it is rapidly expanding; at the
|
|
33
|
+
same time, it is still quite new, and has many unnecessary limitations
|
|
34
|
+
and incoherences.
|
|
35
|
+
|
|
36
|
+
.. _section-examples:
|
|
37
|
+
|
|
38
|
+
Initial examples
|
|
39
|
+
----------------
|
|
40
|
+
|
|
41
|
+
.. _section-examples-poker:
|
|
42
|
+
|
|
43
|
+
Poker and probability
|
|
44
|
+
~~~~~~~~~~~~~~~~~~~~~
|
|
45
|
+
|
|
46
|
+
We begin by solving a classic problem: enumerating certain combinations
|
|
47
|
+
of cards in the game of poker, in order to deduce their probability.
|
|
48
|
+
|
|
49
|
+
A card in a poker deck is characterized by a suit (hearts, diamonds,
|
|
50
|
+
spades, or clubs) and a value (2, 3, ..., 10, jack, queen, king, ace). The
|
|
51
|
+
game is played with a full deck, which consists of the Cartesian product
|
|
52
|
+
of the set of suits and the set of values:
|
|
53
|
+
|
|
54
|
+
.. MATH:: \operatorname{Cards} = \operatorname{Suits} \times
|
|
55
|
+
\operatorname{Values} = \{ (s, v) \mathrel| s\in
|
|
56
|
+
\operatorname{Suits} \text{ et } v \in \operatorname{Values} \}\,.
|
|
57
|
+
|
|
58
|
+
We construct these examples in ``Sage``::
|
|
59
|
+
|
|
60
|
+
sage: Suits = Set(["Hearts", "Diamonds", "Spades", "Clubs"])
|
|
61
|
+
sage: Values = Set([2, 3, 4, 5, 6, 7, 8, 9, 10,
|
|
62
|
+
....: "Jack", "Queen", "King", "Ace"])
|
|
63
|
+
sage: Cards = cartesian_product([Values, Suits])
|
|
64
|
+
|
|
65
|
+
There are `4` suits and `13` possible values, and
|
|
66
|
+
therefore `4\times 13=52` cards in the poker deck::
|
|
67
|
+
|
|
68
|
+
sage: Suits.cardinality()
|
|
69
|
+
4
|
|
70
|
+
sage: Values.cardinality()
|
|
71
|
+
13
|
|
72
|
+
sage: Cards.cardinality()
|
|
73
|
+
52
|
|
74
|
+
|
|
75
|
+
Draw a card at random::
|
|
76
|
+
|
|
77
|
+
sage: Cards.random_element() # random
|
|
78
|
+
(6, 'Clubs')
|
|
79
|
+
|
|
80
|
+
Now we can define a set of cards::
|
|
81
|
+
|
|
82
|
+
sage: Set([Cards.random_element(), Cards.random_element()]) # random
|
|
83
|
+
{(2, 'Hearts'), (4, 'Spades')}
|
|
84
|
+
|
|
85
|
+
This problem should eventually disappear: it is planned to change the
|
|
86
|
+
implementation of Cartesian products so that their elements are
|
|
87
|
+
immutable by default.
|
|
88
|
+
|
|
89
|
+
Returning to our main topic, we will be considering a simplified version
|
|
90
|
+
of poker, in which each player directly draws five cards, which form his
|
|
91
|
+
*hand*. The cards are all distinct and the order in which they are drawn
|
|
92
|
+
is irrelevant; a hand is therefore a subset of size `5` of the
|
|
93
|
+
set of cards. To draw a hand at random, we first construct the set of
|
|
94
|
+
all possible hands, and then we ask for a randomly chosen element::
|
|
95
|
+
|
|
96
|
+
sage: Hands = Subsets(Cards, 5)
|
|
97
|
+
sage: Hands.random_element() # random
|
|
98
|
+
{(4, 'Hearts', 4), (9, 'Diamonds'), (8, 'Spades'),
|
|
99
|
+
(9, 'Clubs'), (7, 'Hearts')}
|
|
100
|
+
|
|
101
|
+
The total number of hands is given by the number of subsets of size
|
|
102
|
+
`5` of a set of size `52`, which is given by the
|
|
103
|
+
binomial coefficient `\binom{52}{5}`::
|
|
104
|
+
|
|
105
|
+
sage: binomial(52,5)
|
|
106
|
+
2598960
|
|
107
|
+
|
|
108
|
+
One can also ignore the method of calculation, and
|
|
109
|
+
simply ask for the size of the set of hands::
|
|
110
|
+
|
|
111
|
+
sage: Hands.cardinality()
|
|
112
|
+
2598960
|
|
113
|
+
|
|
114
|
+
The strength of a poker hand depends on the particular combination of
|
|
115
|
+
cards present. One such combination is the *flush*; this is a hand all
|
|
116
|
+
of whose cards have the same suit. (In principle, straight flushes
|
|
117
|
+
should be excluded; this will be the goal of an exercise given below.)
|
|
118
|
+
Such a hand is therefore characterized by the choice of five values from
|
|
119
|
+
among the thirteen possibilities, and the choice of one of four suits.
|
|
120
|
+
We will construct the set of all flushes, so as to determine how many
|
|
121
|
+
there are::
|
|
122
|
+
|
|
123
|
+
sage: Flushes = cartesian_product([Subsets(Values, 5), Suits])
|
|
124
|
+
sage: Flushes.cardinality()
|
|
125
|
+
5148
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
The probability of obtaining a flush when drawing a hand at random is
|
|
129
|
+
therefore::
|
|
130
|
+
|
|
131
|
+
sage: Flushes.cardinality() / Hands.cardinality()
|
|
132
|
+
33/16660
|
|
133
|
+
|
|
134
|
+
or about two in a thousand::
|
|
135
|
+
|
|
136
|
+
sage: 1000.0 * Flushes.cardinality() / Hands.cardinality()
|
|
137
|
+
1.98079231692677
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
We will now attempt a little numerical simulation. The following
|
|
141
|
+
function tests whether a given hand is a flush or not::
|
|
142
|
+
|
|
143
|
+
sage: def is_flush(hand):
|
|
144
|
+
....: return len(set(suit for (val, suit) in hand)) == 1
|
|
145
|
+
|
|
146
|
+
We now draw 10000 hands at random, and count the number of flushes
|
|
147
|
+
obtained (this takes about 10 seconds)::
|
|
148
|
+
|
|
149
|
+
sage: n = 10000
|
|
150
|
+
sage: nflush = 0
|
|
151
|
+
sage: for i in range(n): # long time
|
|
152
|
+
....: hand = Hands.random_element()
|
|
153
|
+
....: if is_flush(hand):
|
|
154
|
+
....: nflush += 1
|
|
155
|
+
sage: n, nflush # random
|
|
156
|
+
(10000, 18)
|
|
157
|
+
|
|
158
|
+
.. topic:: Exercises
|
|
159
|
+
|
|
160
|
+
A hand containing four cards of the same value is called a *four
|
|
161
|
+
of a kind*. Construct the set of four of a kind hands (Hint: use
|
|
162
|
+
``Arrangements`` to choose a pair of distinct values at random,
|
|
163
|
+
then choose a suit for the first value). Calculate the number of
|
|
164
|
+
four of a kind hand, list them, and then determine the probability
|
|
165
|
+
of obtaining a four of a kind when drawing a hand at random.
|
|
166
|
+
|
|
167
|
+
A hand all of whose cards have the same suit, and whose values are
|
|
168
|
+
consecutive, is called a *straight flush* rather than a *flush*.
|
|
169
|
+
Count the number of straight flushes, and then deduce the correct
|
|
170
|
+
probability of obtaining a flush when drawing a hand at random.
|
|
171
|
+
|
|
172
|
+
Calculate the probability of each of the poker hands (see
|
|
173
|
+
:wikipedia:`Poker_hands`), and compare them with the results of
|
|
174
|
+
simulations.
|
|
175
|
+
|
|
176
|
+
.. _section-examples-catalan:
|
|
177
|
+
|
|
178
|
+
Enumeration of trees using generating functions
|
|
179
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
180
|
+
|
|
181
|
+
In this section, we discuss the example of complete binary trees, and
|
|
182
|
+
illustrate in this context many techniques of enumeration in which
|
|
183
|
+
formal power series play a natural role. These techniques are quite
|
|
184
|
+
general, and can be applied whenever the combinatorial objects in
|
|
185
|
+
question admit a recursive definition (grammar) (see
|
|
186
|
+
:ref:`section-generic-species` for an automated treatment). The
|
|
187
|
+
goal is not a formal presentation of these methods; the calculations are
|
|
188
|
+
rigorous, but most of the justifications will be skipped.
|
|
189
|
+
|
|
190
|
+
A *complete binary tree* is either a leaf `\mathrm{L}`, or a
|
|
191
|
+
node to which two complete binary trees are attached (see
|
|
192
|
+
:ref:`figure-examples-catalan-trees`).
|
|
193
|
+
|
|
194
|
+
.. _figure-examples-catalan-trees:
|
|
195
|
+
|
|
196
|
+
.. figure:: ../../media/complete-binary-trees-4.png
|
|
197
|
+
:scale: 150 %
|
|
198
|
+
|
|
199
|
+
Figure: The five complete binary trees with four leaves
|
|
200
|
+
|
|
201
|
+
.. _exo.enumeration.arbres:
|
|
202
|
+
|
|
203
|
+
.. topic:: Exercise: enumeration of binary trees
|
|
204
|
+
|
|
205
|
+
Find by hand all the complete binary trees with `n=1, 2, 3, 4, 5`
|
|
206
|
+
leaves (see :ref:`Exercise: complete binary tree iterator
|
|
207
|
+
<exo-iterators-catalan>` to find them using ``Sage``).
|
|
208
|
+
|
|
209
|
+
Our goal is to determine the number `c_n` of complete binary
|
|
210
|
+
trees with `n` leaves (in this section, except when explicitly
|
|
211
|
+
stated otherwise, “trees” always means complete binary trees). This is a
|
|
212
|
+
typical situation in which one is not only interested in a single set,
|
|
213
|
+
but in a family of sets, typically parameterized by `n\in \NN`.
|
|
214
|
+
|
|
215
|
+
According to the solution of :ref:`Exercise: enumeration of binary trees <exo.enumeration.arbres>`, the first terms are given by
|
|
216
|
+
`c_1,\dots,c_5=1,1,2,5,14`. The simple fact of knowing these few
|
|
217
|
+
numbers is already very valuable. In fact, this permits research in a
|
|
218
|
+
gold mine of information: the `Online Encyclopedia of Integer Sequences
|
|
219
|
+
<http://oeis.org/>`_, commonly called “Sloane”, the name of its principal
|
|
220
|
+
author, which contains more than 190000 sequences of integers::
|
|
221
|
+
|
|
222
|
+
sage: oeis([1,1,2,5,14]) # optional -- internet
|
|
223
|
+
0: A000108: Catalan numbers: ...
|
|
224
|
+
1: ...
|
|
225
|
+
2: ...
|
|
226
|
+
|
|
227
|
+
The result suggests that the trees are counted by one of the most famous
|
|
228
|
+
sequences, the Catalan numbers. Looking through the references supplied
|
|
229
|
+
by the Encyclopedia, we see that this is really the case: the few
|
|
230
|
+
numbers above form a digital fingerprint of our objects, which enable us
|
|
231
|
+
to find, in a few seconds, a precise result from within an abundant
|
|
232
|
+
literature.
|
|
233
|
+
|
|
234
|
+
Our next goal is to recover this result using ``Sage``. Let
|
|
235
|
+
`C_n` be the set of trees with `n` leaves, so that
|
|
236
|
+
`c_n=|C_n|`; by convention, we will define
|
|
237
|
+
`C_0=\emptyset` and `c_0=0`. The set of all trees is
|
|
238
|
+
then the disjoint union of the sets `C_n`:
|
|
239
|
+
|
|
240
|
+
.. MATH:: C=\biguplus_{n\in \mathbb N} C_n\,.
|
|
241
|
+
|
|
242
|
+
Having named the set `C` of all trees, we can translate the
|
|
243
|
+
recursive definition of trees into a set-theoretic equation:
|
|
244
|
+
|
|
245
|
+
.. MATH:: C \quad \approx \quad \{ \mathrm{L} \} \quad \uplus\quad C \times C\,.
|
|
246
|
+
|
|
247
|
+
In words: a tree `t` (which is by definition in `C`) is either a
|
|
248
|
+
leaf (so in `\{\mathrm{L}\}`) or a node to which two trees
|
|
249
|
+
`t_1` and `t_2` have been attached, and which we can
|
|
250
|
+
therefore identify with the pair `(t_1,t_2)` (in the Cartesian
|
|
251
|
+
product `C\times C`).
|
|
252
|
+
|
|
253
|
+
The founding idea of algebraic combinatorics, introduced by Euler in
|
|
254
|
+
a letter to Goldbach of 1751 to treat a similar problem , is to
|
|
255
|
+
manipulate all the numbers `c_n` simultaneously, by encoding them
|
|
256
|
+
as coefficients in a formal power series, called the *generating
|
|
257
|
+
function* of the `c_n`’s:
|
|
258
|
+
|
|
259
|
+
.. MATH:: C(z) = \sum_{n\in \mathbb N} c_n z^n\,,
|
|
260
|
+
|
|
261
|
+
where `z` is a formal variable (which means that we do not
|
|
262
|
+
have to worry about questions of convergence). The beauty of this idea
|
|
263
|
+
is that set-theoretic operations `(A\uplus B`,
|
|
264
|
+
`A\times B)` translate naturally into algebraic operations on
|
|
265
|
+
the corresponding series (`A(z)+B(z)`,
|
|
266
|
+
`A(z)\cdot B(z)`), in such a way that the set-theoretic equation
|
|
267
|
+
satisfied by `C` can be translated directly into an algebraic
|
|
268
|
+
equation satisfied by `C(z)`:
|
|
269
|
+
|
|
270
|
+
.. MATH:: C(z) = z + C(z) \cdot C(z)\,.
|
|
271
|
+
|
|
272
|
+
Now we can solve this equation with ``Sage``. In order to do so, we
|
|
273
|
+
introduce two variables, `C` and `z`, and we define the
|
|
274
|
+
equation::
|
|
275
|
+
|
|
276
|
+
sage: C, z = var('C,z') # needs sage.symbolic
|
|
277
|
+
sage: sys = [ C == z + C*C ] # needs sage.symbolic
|
|
278
|
+
|
|
279
|
+
There are two solutions, which happen to have closed forms::
|
|
280
|
+
|
|
281
|
+
sage: sol = solve(sys, C, solution_dict=True); sol # needs sage.symbolic
|
|
282
|
+
[{C: -1/2*sqrt(-4*z + 1) + 1/2}, {C: 1/2*sqrt(-4*z + 1) + 1/2}]
|
|
283
|
+
sage: s0 = sol[0][C]; s1 = sol[1][C] # needs sage.symbolic
|
|
284
|
+
|
|
285
|
+
and whose Taylor series begin as follows::
|
|
286
|
+
|
|
287
|
+
sage: s0.series(z, 6) # needs sage.symbolic
|
|
288
|
+
1*z + 1*z^2 + 2*z^3 + 5*z^4 + 14*z^5 + Order(z^6)
|
|
289
|
+
sage: s1.series(z, 6) # needs sage.symbolic
|
|
290
|
+
1 + (-1)*z + (-1)*z^2 + (-2)*z^3 + (-5)*z^4 + (-14)*z^5
|
|
291
|
+
+ Order(z^6)
|
|
292
|
+
|
|
293
|
+
The second solution is clearly aberrant, while the first one gives the
|
|
294
|
+
expected coefficients. Therefore, we set::
|
|
295
|
+
|
|
296
|
+
sage: C = s0 # needs sage.symbolic
|
|
297
|
+
|
|
298
|
+
We can now calculate the next terms::
|
|
299
|
+
|
|
300
|
+
sage: C.series(z, 11) # needs sage.symbolic
|
|
301
|
+
1*z + 1*z^2 + 2*z^3 + 5*z^4 + 14*z^5 + 42*z^6 +
|
|
302
|
+
132*z^7 + 429*z^8 + 1430*z^9 + 4862*z^10 + Order(z^11)
|
|
303
|
+
|
|
304
|
+
or calculate, more or less instantaneously, the 100-th coefficient::
|
|
305
|
+
|
|
306
|
+
sage: C.series(z, 101).coefficient(z,100) # needs sage.symbolic
|
|
307
|
+
227508830794229349661819540395688853956041682601541047340
|
|
308
|
+
|
|
309
|
+
It is unfortunate to have to recalculate everything if at some point we
|
|
310
|
+
wanted the 101-st coefficient. Lazy power series (see
|
|
311
|
+
:mod:`sage.rings.lazy_series_ring`) come into their own here, in that
|
|
312
|
+
one can define them from a system of equations without solving it, and,
|
|
313
|
+
in particular, without needing a closed form for the answer. We begin by
|
|
314
|
+
defining the ring of lazy power series::
|
|
315
|
+
|
|
316
|
+
sage: L.<z> = LazyPowerSeriesRing(QQ)
|
|
317
|
+
|
|
318
|
+
Then we create a “free” power series, which we name, and which we then
|
|
319
|
+
define by a recursive equation::
|
|
320
|
+
|
|
321
|
+
sage: C = L.undefined(valuation=1)
|
|
322
|
+
sage: C.define(z + C * C)
|
|
323
|
+
|
|
324
|
+
::
|
|
325
|
+
|
|
326
|
+
sage: [C.coefficient(i) for i in range(11)]
|
|
327
|
+
[0, 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862]
|
|
328
|
+
|
|
329
|
+
At any point, one can ask for any coefficient without having to redefine
|
|
330
|
+
`C`::
|
|
331
|
+
|
|
332
|
+
sage: C.coefficient(100)
|
|
333
|
+
227508830794229349661819540395688853956041682601541047340
|
|
334
|
+
sage: C.coefficient(200)
|
|
335
|
+
129013158064429114001222907669676675134349530552728882499810851598901419013348319045534580850847735528275750122188940
|
|
336
|
+
|
|
337
|
+
We now return to the closed form of `C(z)`::
|
|
338
|
+
|
|
339
|
+
sage: z = var('z') # needs sage.symbolic
|
|
340
|
+
sage: C = s0; C # needs sage.symbolic
|
|
341
|
+
-1/2*sqrt(-4*z + 1) + 1/2
|
|
342
|
+
|
|
343
|
+
The `n`-th coefficient in the Taylor series for `C(z)`
|
|
344
|
+
being given by `\frac{1}{n!} C(z)^{(n)}(0)`, we look at the
|
|
345
|
+
successive derivatives `C(z)^{(n)}(z)`::
|
|
346
|
+
|
|
347
|
+
sage: derivative(C, z, 1) # needs sage.symbolic
|
|
348
|
+
1/sqrt(-4*z + 1)
|
|
349
|
+
sage: derivative(C, z, 2) # needs sage.symbolic
|
|
350
|
+
2/(-4*z + 1)^(3/2)
|
|
351
|
+
sage: derivative(C, z, 3) # needs sage.symbolic
|
|
352
|
+
12/(-4*z + 1)^(5/2)
|
|
353
|
+
|
|
354
|
+
This suggests the existence of a simple explicit formula, which we will
|
|
355
|
+
now seek. The following small function returns `d_n=n! \, c_n`::
|
|
356
|
+
|
|
357
|
+
sage: def d(n): return derivative(s0, n).subs(z=0)
|
|
358
|
+
|
|
359
|
+
Taking successive quotients::
|
|
360
|
+
|
|
361
|
+
sage: [ (d(n+1) / d(n)) for n in range(1,17) ] # needs sage.symbolic
|
|
362
|
+
[2, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, 54, 58, 62]
|
|
363
|
+
|
|
364
|
+
we observe that `d_n` satisfies the recurrence relation
|
|
365
|
+
`d_{n+1}=(4n-2)d_n`, from which we deduce that `c_n`
|
|
366
|
+
satisfies the recurrence relation
|
|
367
|
+
`c_{n+1}=\frac{(4n-2)}{n+1}c_n`. Simplifying, we find that
|
|
368
|
+
`c_n` is the `(n-1)`-th Catalan number:
|
|
369
|
+
|
|
370
|
+
.. MATH:: c_n = \operatorname{Catalan}(n-1) = \frac {1}{n} \binom{2(n-1)}{n-1}\,.
|
|
371
|
+
|
|
372
|
+
We check this::
|
|
373
|
+
|
|
374
|
+
sage: n = var('n') # needs sage.symbolic
|
|
375
|
+
sage: c = 1/n*binomial(2*(n-1),n-1) # needs sage.symbolic
|
|
376
|
+
sage: [c.subs(n=k) for k in range(1, 11)] # needs sage.symbolic
|
|
377
|
+
[1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862]
|
|
378
|
+
sage: [catalan_number(k-1) for k in range(1, 11)]
|
|
379
|
+
[1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862]
|
|
380
|
+
|
|
381
|
+
We can now calculate coefficients much further; here we calculate
|
|
382
|
+
`c_{100000}` which has more than `60000` digits::
|
|
383
|
+
|
|
384
|
+
sage: cc = c(n=100000) # needs sage.symbolic
|
|
385
|
+
|
|
386
|
+
This takes a couple of seconds::
|
|
387
|
+
|
|
388
|
+
sage: %time cc = c(100000) # not tested # needs sage.symbolic
|
|
389
|
+
CPU times: user 2.34 s, sys: 0.00 s, total: 2.34 s
|
|
390
|
+
Wall time: 2.34 s
|
|
391
|
+
sage: ZZ(cc).ndigits() # needs sage.symbolic
|
|
392
|
+
60198
|
|
393
|
+
|
|
394
|
+
The methods which we have used generalize to all recursively defined
|
|
395
|
+
objects: the system of set-theoretic equations can be translated into a
|
|
396
|
+
system of equations on the generating function, which enables the
|
|
397
|
+
recursive calculation of its coefficients. If the set-theoretic
|
|
398
|
+
equations are simple enough (for example, if they only involve Cartesian
|
|
399
|
+
products and disjoint unions), the equation for `C(z)` is
|
|
400
|
+
algebraic. This equation has, in general, no closed-form solution.
|
|
401
|
+
However, using *confinement*, one can deduce a *linear* differential
|
|
402
|
+
equation which `C(z)` satisfies. This differential equation, in
|
|
403
|
+
turn, can be translated into a recurrence relation of fixed length on
|
|
404
|
+
its coefficients `c_n`. In this case, the series is called
|
|
405
|
+
*D-finite*. After the initial calculation of this recurrence relation,
|
|
406
|
+
the calculation of coefficients is very fast. All these steps are purely
|
|
407
|
+
algorithmic, and it is planned to port into ``Sage`` the implementations
|
|
408
|
+
which exist in ``Maple`` (the ``gfun`` and ``combstruct`` packages) or
|
|
409
|
+
``MuPAD-Combinat`` (the ``decomposableObjects`` library).
|
|
410
|
+
|
|
411
|
+
For the moment, we illustrate this general procedure in the case of
|
|
412
|
+
complete binary trees. The generating function `C(z)` is a
|
|
413
|
+
solution to an algebraic equation `P(z,C(z)) = 0`, where
|
|
414
|
+
`P=P(x,y)` is a polynomial with coefficients in `\QQ`.
|
|
415
|
+
In the present case, `P=y^2-y+x`. We formally differentiate this
|
|
416
|
+
equation with respect to `z`::
|
|
417
|
+
|
|
418
|
+
sage: # needs sage.symbolic
|
|
419
|
+
sage: x, y, z = var('x, y, z')
|
|
420
|
+
sage: P = function('P')(x, y)
|
|
421
|
+
sage: C = function('C')(z)
|
|
422
|
+
sage: equation = P(x=z, y=C) == 0
|
|
423
|
+
sage: diff(equation, z)
|
|
424
|
+
diff(C(z), z)*D[1](P)(z, C(z)) + D[0](P)(z, C(z)) == 0
|
|
425
|
+
|
|
426
|
+
or, in a more readable format,
|
|
427
|
+
|
|
428
|
+
.. MATH:: \frac{d C(z)}{d z} \frac{\partial P}{\partial y} (z, C(z)) + \frac{\partial P}{\partial x}(z,C(z)) = 0
|
|
429
|
+
|
|
430
|
+
From this we deduce:
|
|
431
|
+
|
|
432
|
+
.. MATH:: \frac{d C(z)}{d z} = - \frac{\frac{\partial P}{\partial x}}{\frac{\partial P}{\partial y}}(z, C(z))\,.
|
|
433
|
+
|
|
434
|
+
In the case of complete binary trees, this gives::
|
|
435
|
+
|
|
436
|
+
sage: P = y^2 - y + x # needs sage.symbolic
|
|
437
|
+
sage: Px = diff(P, x); Py = diff(P, y) # needs sage.symbolic
|
|
438
|
+
sage: - Px / Py # needs sage.symbolic
|
|
439
|
+
-1/(2*y - 1)
|
|
440
|
+
|
|
441
|
+
Recall that `P(z, C(z))=0`. Thus, we can calculate this fraction
|
|
442
|
+
mod `P` and, in this way, express the derivative of
|
|
443
|
+
`C(z)` as a *polynomial in* `C(z)` *with coefficients in*
|
|
444
|
+
`\QQ(z)`. In order to achieve this, we construct the quotient
|
|
445
|
+
ring `R= \QQ(x)[y]/ (P)`::
|
|
446
|
+
|
|
447
|
+
sage: Qx = QQ['x'].fraction_field()
|
|
448
|
+
sage: Qxy = Qx['y']
|
|
449
|
+
sage: R = Qxy.quo(P); R # needs sage.symbolic
|
|
450
|
+
Univariate Quotient Polynomial Ring in ybar
|
|
451
|
+
over Fraction Field of Univariate Polynomial Ring in x
|
|
452
|
+
over Rational Field with modulus y^2 - y + x
|
|
453
|
+
|
|
454
|
+
Note: ``ybar`` is the name of the variable `y` in the quotient ring.
|
|
455
|
+
|
|
456
|
+
.. TODO:: add link to some tutorial on quotient rings
|
|
457
|
+
|
|
458
|
+
We continue the calculation of this fraction in `R`::
|
|
459
|
+
|
|
460
|
+
sage: fraction = - R(Px) / R(Py); fraction # needs sage.symbolic
|
|
461
|
+
(1/2/(x - 1/4))*ybar - 1/4/(x - 1/4)
|
|
462
|
+
|
|
463
|
+
.. NOTE::
|
|
464
|
+
|
|
465
|
+
The following variant does not work yet::
|
|
466
|
+
|
|
467
|
+
sage: fraction = R( - Px / Py ); fraction # todo: not implemented
|
|
468
|
+
Traceback (most recent call last):
|
|
469
|
+
...
|
|
470
|
+
TypeError: denominator must be a unit
|
|
471
|
+
|
|
472
|
+
We lift the result to `\QQ(x)[y]` and then substitute
|
|
473
|
+
`z` and `C(z)` to obtain an expression for
|
|
474
|
+
`\frac{d}{dz}C(z)`::
|
|
475
|
+
|
|
476
|
+
sage: fraction = fraction.lift(); fraction # needs sage.symbolic
|
|
477
|
+
(1/2/(x - 1/4))*y - 1/4/(x - 1/4)
|
|
478
|
+
sage: fraction(x=z, y=C) # needs sage.symbolic
|
|
479
|
+
2*C(z)/(4*z - 1) - 1/(4*z - 1)
|
|
480
|
+
|
|
481
|
+
or, more legibly,
|
|
482
|
+
|
|
483
|
+
.. MATH:: \frac{\partial C(z)}{\partial z} = \frac{1}{1-4z} -\frac{2}{1-4z}C(z)\,.
|
|
484
|
+
|
|
485
|
+
In this simple case, we can directly deduce from this expression a
|
|
486
|
+
linear differential equation with coefficients in `\QQ[z]`::
|
|
487
|
+
|
|
488
|
+
sage: # needs sage.symbolic
|
|
489
|
+
sage: equadiff = diff(C,z) == fraction(x=z, y=C)
|
|
490
|
+
sage: equadiff
|
|
491
|
+
diff(C(z), z) == 2*C(z)/(4*z - 1) - 1/(4*z - 1)
|
|
492
|
+
sage: equadiff = equadiff.simplify_rational()
|
|
493
|
+
sage: equadiff = equadiff * equadiff.rhs().denominator()
|
|
494
|
+
sage: equadiff = equadiff - equadiff.rhs()
|
|
495
|
+
sage: equadiff
|
|
496
|
+
(4*z - 1)*diff(C(z), z) - 2*C(z) + 1 == 0
|
|
497
|
+
|
|
498
|
+
or, more legibly,
|
|
499
|
+
|
|
500
|
+
.. MATH:: (1-4z) \frac{\partial C(z)}{\partial z} + 2 C(z) - 1 = 0\,.
|
|
501
|
+
|
|
502
|
+
It is trivial to verify this equation on the closed form::
|
|
503
|
+
|
|
504
|
+
sage: Cf = sage.symbolic.function_factory.function('C') # needs sage.symbolic
|
|
505
|
+
sage: equadiff.substitute_function(Cf, s0.function(z)) # needs sage.symbolic
|
|
506
|
+
(4*z - 1)/sqrt(-4*z + 1) + sqrt(-4*z + 1) == 0
|
|
507
|
+
sage: bool(equadiff.substitute_function(Cf, s0.function(z))) # needs sage.symbolic
|
|
508
|
+
True
|
|
509
|
+
|
|
510
|
+
.. On veut non seulement remplacer les occurrences de C(z), mais
|
|
511
|
+
.. aussi de C tout court (par exemple dans D[0](C)). Y-a-t'il mieux
|
|
512
|
+
.. pour retrouver C à partir de C(z)?
|
|
513
|
+
.. Cf. also:
|
|
514
|
+
.. http://ask.sagemath.org/question/541/substitute-expression-instead-of-formal-function
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
In the general case, one continues to calculate successive derivatives
|
|
518
|
+
of `C(z)`. These derivatives are *confined* in the quotient ring
|
|
519
|
+
`\QQ(z)[C]/(P)` which is of finite dimension `\deg P`
|
|
520
|
+
over `\QQ(z)`. Therefore, one will eventually find a linear
|
|
521
|
+
relation among the first `\deg P` derivatives of `C(z)`.
|
|
522
|
+
Putting it over a single denominator, we obtain a linear
|
|
523
|
+
differential equation of degree `\leq \deg P` with coefficients
|
|
524
|
+
in `\QQ[z]`. By extracting the coefficient of `z^n` in
|
|
525
|
+
the differential equation, we obtain the desired recurrence relation on
|
|
526
|
+
the coefficients; in this case we recover the relation we had already
|
|
527
|
+
found, based on the closed form:
|
|
528
|
+
|
|
529
|
+
.. MATH:: c_{n+1}=\frac{(4n-2)}{n+1}c_n
|
|
530
|
+
|
|
531
|
+
After fixing the correct initial conditions, it becomes possible to
|
|
532
|
+
calculate the coefficients of `C(z)` recursively::
|
|
533
|
+
|
|
534
|
+
sage: def C(n): return 1 if n <= 1 else (4*n-6)/n * C(n-1)
|
|
535
|
+
sage: [ C(i) for i in range(10) ]
|
|
536
|
+
[1, 1, 1, 2, 5, 14, 42, 132, 429, 1430]
|
|
537
|
+
|
|
538
|
+
If `n` is too large for the explicit calculation of
|
|
539
|
+
`c_n`, a sequence asymptotically equivalent to the sequence of
|
|
540
|
+
coefficients `c_n` may be sought. Here again, there are generic
|
|
541
|
+
techniques. The central tool is complex analysis, specifically, the
|
|
542
|
+
study of the generating function around its singularities. In the
|
|
543
|
+
present instance, the singularity is at `z_0=1/4` and one would
|
|
544
|
+
obtain `c_n \sim \frac{4^n}{n^{3/2}\sqrt{\pi}}`.
|
|
545
|
+
|
|
546
|
+
Summary
|
|
547
|
+
^^^^^^^
|
|
548
|
+
|
|
549
|
+
We see here a general phenomenon of computer algebra: the best *data
|
|
550
|
+
structure* to describe a complicated mathematical object (a real number,
|
|
551
|
+
a sequence, a formal power series, a function, a set) is often an
|
|
552
|
+
equation defining the object (or a system of equations, typically with
|
|
553
|
+
some initial conditions). Attempting to find a closed-form
|
|
554
|
+
solution to this equation is not necessarily of interest: on the one
|
|
555
|
+
hand, such a closed form rarely exists (e.g., the problem of
|
|
556
|
+
solving a polynomial by radicals), and on the other hand, the equation,
|
|
557
|
+
in itself, contains all the necessary information to calculate
|
|
558
|
+
algorithmically the properties of the object under consideration (e.g.,
|
|
559
|
+
a numerical approximation, the initial terms or elements, an asymptotic
|
|
560
|
+
equivalent), or to calculate with the object itself (e.g., performing
|
|
561
|
+
arithmetic on power series). Therefore, instead of solving the equation,
|
|
562
|
+
we look for the equation describing the object which is best suited to
|
|
563
|
+
the problem we want to solve.
|
|
564
|
+
|
|
565
|
+
As we saw in our example, confinement (for example, in a finite
|
|
566
|
+
dimensional vector space) is a fundamental tool for studying such
|
|
567
|
+
equations. This notion of confinement is widely applicable in
|
|
568
|
+
elimination techniques (linear algebra, Gröbner bases, and their
|
|
569
|
+
algebro-differential generalizations). The same tool is central in
|
|
570
|
+
algorithms for automatic summation and automatic verification of
|
|
571
|
+
identities (Gosper’s algorithm, Zeilberger’s algorithm, and their
|
|
572
|
+
generalizations; see also :ref:`Exercise: alternating sign matrices
|
|
573
|
+
<exercise-alternating-sign-matrices>`).
|
|
574
|
+
|
|
575
|
+
.. TODO:: add link to some tutorial on summation
|
|
576
|
+
|
|
577
|
+
All these techniques and their many generalizations are at the heart of
|
|
578
|
+
very active topics of research: automatic combinatorics and analytic
|
|
579
|
+
combinatorics, with major applications in the analysis of algorithms. It is
|
|
580
|
+
likely, and desirable, that they will be progressively implemented in
|
|
581
|
+
``Sage``.
|
|
582
|
+
|
|
583
|
+
.. _section-bricks:
|
|
584
|
+
|
|
585
|
+
Common enumerated sets
|
|
586
|
+
----------------------
|
|
587
|
+
|
|
588
|
+
First example: the subsets of a set
|
|
589
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
590
|
+
|
|
591
|
+
Fix a set `E` of size `n` and consider the subsets of
|
|
592
|
+
`E` of size `k`. We know that these subsets are counted
|
|
593
|
+
by the binomial coefficients `\binom n k`. We can therefore
|
|
594
|
+
calculate the number of subsets of size `k=2` of
|
|
595
|
+
`E=\{1,2,3,4\}` with the function ``binomial``::
|
|
596
|
+
|
|
597
|
+
sage: binomial(4, 2)
|
|
598
|
+
6
|
|
599
|
+
|
|
600
|
+
Alternatively, we can *construct* the set `\mathcal P_2(E)` of
|
|
601
|
+
all the subsets of size `2` of `E`, then ask its
|
|
602
|
+
cardinality::
|
|
603
|
+
|
|
604
|
+
sage: S = Subsets([1,2,3,4], 2)
|
|
605
|
+
sage: S.cardinality()
|
|
606
|
+
6
|
|
607
|
+
|
|
608
|
+
Once ``S`` has been constructed, we can also obtain the list of its
|
|
609
|
+
elements::
|
|
610
|
+
|
|
611
|
+
sage: S.list()
|
|
612
|
+
[{1, 2}, {1, 3}, {1, 4}, {2, 3}, {2, 4}, {3, 4}]
|
|
613
|
+
|
|
614
|
+
or select an element at random::
|
|
615
|
+
|
|
616
|
+
sage: S.random_element() # random
|
|
617
|
+
{1, 4}
|
|
618
|
+
|
|
619
|
+
More precisely, the object ``S`` models the set
|
|
620
|
+
`\mathcal P_2(E)` equipped with a fixed order (here,
|
|
621
|
+
lexicographic order). It is therefore possible to ask for its
|
|
622
|
+
`5`-th element, keeping in mind that, as with ``Python`` lists, the first
|
|
623
|
+
element is numbered zero::
|
|
624
|
+
|
|
625
|
+
sage: S.unrank(4)
|
|
626
|
+
{2, 4}
|
|
627
|
+
|
|
628
|
+
As a shortcut, in this setting, one can also use the notation::
|
|
629
|
+
|
|
630
|
+
sage: S[4]
|
|
631
|
+
{2, 4}
|
|
632
|
+
|
|
633
|
+
but this should be used with care because some sets have a
|
|
634
|
+
natural indexing other than by `(0, 1, \dots)`.
|
|
635
|
+
|
|
636
|
+
Conversely, one can calculate the position of an object in this order::
|
|
637
|
+
|
|
638
|
+
sage: s = S([2,4]); s
|
|
639
|
+
{2, 4}
|
|
640
|
+
sage: S.rank(s)
|
|
641
|
+
4
|
|
642
|
+
|
|
643
|
+
Note that ``S`` is *not* the list of its elements. One can, for example,
|
|
644
|
+
model the set `\mathcal P(\mathcal P(\mathcal P(E)))` and
|
|
645
|
+
calculate its cardinality (`2^{2^{2^4}}`)::
|
|
646
|
+
|
|
647
|
+
sage: E = Set([1,2,3,4])
|
|
648
|
+
sage: S = Subsets(Subsets(Subsets(E))); S
|
|
649
|
+
Subsets of Subsets of Subsets of {1, 2, 3, 4}
|
|
650
|
+
sage: n = S.cardinality(); n
|
|
651
|
+
2003529930406846464979072351560255750447825475569751419265016973...
|
|
652
|
+
|
|
653
|
+
which is roughly `2\cdot 10^{19728}`::
|
|
654
|
+
|
|
655
|
+
sage: n.ndigits()
|
|
656
|
+
19729
|
|
657
|
+
|
|
658
|
+
or ask for its `237102124`-th element::
|
|
659
|
+
|
|
660
|
+
sage: S.unrank(237102123) # random print output
|
|
661
|
+
{{{2, 4}, {1, 4}, {}, {1, 3, 4}, {1, 2, 4}, {4}, {2, 3}, {1, 3}, {2}},
|
|
662
|
+
{{1, 3}, {2, 4}, {1, 2, 4}, {}, {3, 4}}}
|
|
663
|
+
|
|
664
|
+
It would be physically impossible to construct explicitly all the
|
|
665
|
+
elements of `S`, as there are many more of them than there are
|
|
666
|
+
particles in the universe (estimated at `10^{82}`).
|
|
667
|
+
|
|
668
|
+
Remark: it would be natural in ``Python`` to use ``len(S)`` to ask for the
|
|
669
|
+
cardinality of ``S``. This is not possible because ``Python`` requires that the
|
|
670
|
+
result of ``len`` be an integer of type ``int``; this could cause
|
|
671
|
+
overflows, and would not permit the return of {Infinity} for infinite
|
|
672
|
+
sets::
|
|
673
|
+
|
|
674
|
+
sage: len(S)
|
|
675
|
+
Traceback (most recent call last):
|
|
676
|
+
...
|
|
677
|
+
OverflowError: cannot fit 'int' into an index-sized integer
|
|
678
|
+
|
|
679
|
+
Partitions of integers
|
|
680
|
+
~~~~~~~~~~~~~~~~~~~~~~
|
|
681
|
+
|
|
682
|
+
We now consider another classic problem: given a positive integer
|
|
683
|
+
`n`, in how many ways can it be written in the form of a sum
|
|
684
|
+
`n=i_1+i_2+\dots+i_\ell`, where `i_1,\dots,i_\ell` are
|
|
685
|
+
positive integers? There are two cases to distinguish:
|
|
686
|
+
|
|
687
|
+
- the order of the elements in the sum is not important, in which case
|
|
688
|
+
we call `(i_1,\dots,i_\ell)` a *partition* of `n`;
|
|
689
|
+
|
|
690
|
+
- the order of the elements in the sum is important, in which case we
|
|
691
|
+
call `(i_1,\dots,i_\ell)` a *composition* of `n`.
|
|
692
|
+
|
|
693
|
+
We will begin with the partitions of `n=5`; as before, we begin
|
|
694
|
+
by constructing the set of these partitions::
|
|
695
|
+
|
|
696
|
+
sage: P5 = Partitions(5); P5
|
|
697
|
+
Partitions of the integer 5
|
|
698
|
+
|
|
699
|
+
then we ask for its cardinality::
|
|
700
|
+
|
|
701
|
+
sage: P5.cardinality()
|
|
702
|
+
7
|
|
703
|
+
|
|
704
|
+
We look at these `7` partitions; the order being irrelevant, the
|
|
705
|
+
entries are ordered, by convention, in decreasing order.
|
|
706
|
+
|
|
707
|
+
::
|
|
708
|
+
|
|
709
|
+
sage: P5.list()
|
|
710
|
+
[[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1], [2, 1, 1, 1],
|
|
711
|
+
[1, 1, 1, 1, 1]]
|
|
712
|
+
|
|
713
|
+
The calculation of the number of partitions uses the Rademacher
|
|
714
|
+
formula (:wikipedia:`Partition_(number_theory)`), implemented in ``C``
|
|
715
|
+
and highly optimized, which makes it very fast::
|
|
716
|
+
|
|
717
|
+
sage: Partitions(100000).cardinality()
|
|
718
|
+
27493510569775696512677516320986352688173429315980054758203125984302147328114964173055050741660736621590157844774296248940493063070200461792764493033510116079342457190155718943509725312466108452006369558934464248716828789832182345009262853831404597021307130674510624419227311238999702284408609370935531629697851569569892196108480158600569421098519
|
|
719
|
+
|
|
720
|
+
Partitions of integers are combinatorial objects naturally equipped with
|
|
721
|
+
many operations. They are therefore returned as objects that are
|
|
722
|
+
richer than simple lists::
|
|
723
|
+
|
|
724
|
+
sage: P7 = Partitions(7)
|
|
725
|
+
sage: p = P7.unrank(5); p
|
|
726
|
+
[4, 2, 1]
|
|
727
|
+
sage: type(p)
|
|
728
|
+
<class 'sage.combinat.partition.Partitions_n_with_category.element_class'>
|
|
729
|
+
|
|
730
|
+
For example, they can be represented graphically by a Ferrers diagram::
|
|
731
|
+
|
|
732
|
+
sage: print(p.ferrers_diagram())
|
|
733
|
+
****
|
|
734
|
+
**
|
|
735
|
+
*
|
|
736
|
+
|
|
737
|
+
We leave it to the user to explore by introspection the available
|
|
738
|
+
operations.
|
|
739
|
+
|
|
740
|
+
Note that we can also construct a partition directly by::
|
|
741
|
+
|
|
742
|
+
sage: Partition([4,2,1])
|
|
743
|
+
[4, 2, 1]
|
|
744
|
+
|
|
745
|
+
or::
|
|
746
|
+
|
|
747
|
+
sage: P7([4,2,1])
|
|
748
|
+
[4, 2, 1]
|
|
749
|
+
|
|
750
|
+
If one wants to restrict the possible values of the parts
|
|
751
|
+
`i_1,\dots,i_\ell` of the partition as, for example, when giving
|
|
752
|
+
change, one can use ``WeightedIntegerVectors``. For example, the
|
|
753
|
+
following calculation::
|
|
754
|
+
|
|
755
|
+
sage: WeightedIntegerVectors(8, [2,3,5]).list()
|
|
756
|
+
[[0, 1, 1], [1, 2, 0], [4, 0, 0]]
|
|
757
|
+
|
|
758
|
+
shows that to make 8 dollars using 2, 3, and 5 dollar bills, one can
|
|
759
|
+
use a 3 and a 5 dollar bill, or a 2 and two 3 dollar bills, or four 2
|
|
760
|
+
dollar bills.
|
|
761
|
+
|
|
762
|
+
Compositions of integers are manipulated the same way::
|
|
763
|
+
|
|
764
|
+
sage: C5 = Compositions(5); C5
|
|
765
|
+
Compositions of 5
|
|
766
|
+
sage: C5.cardinality()
|
|
767
|
+
16
|
|
768
|
+
sage: C5.list()
|
|
769
|
+
[[1, 1, 1, 1, 1], [1, 1, 1, 2], [1, 1, 2, 1], [1, 1, 3],
|
|
770
|
+
[1, 2, 1, 1], [1, 2, 2], [1, 3, 1], [1, 4], [2, 1, 1, 1],
|
|
771
|
+
[2, 1, 2], [2, 2, 1], [2, 3], [3, 1, 1], [3, 2], [4, 1], [5]]
|
|
772
|
+
|
|
773
|
+
The number `16` above seems significant and suggests the existence of a
|
|
774
|
+
formula. We look at the number of compositions of `n` ranging
|
|
775
|
+
from `0` to `9`::
|
|
776
|
+
|
|
777
|
+
sage: [Compositions(n).cardinality() for n in range(10)]
|
|
778
|
+
[1, 1, 2, 4, 8, 16, 32, 64, 128, 256]
|
|
779
|
+
|
|
780
|
+
Similarly, if we consider the number of compositions of `5` by
|
|
781
|
+
length, we find a line of Pascal’s triangle::
|
|
782
|
+
|
|
783
|
+
sage: x = var('x') # needs sage.symbolic
|
|
784
|
+
sage: sum(x^len(c) for c in C5) # needs sage.symbolic
|
|
785
|
+
x^5 + 4*x^4 + 6*x^3 + 4*x^2 + x
|
|
786
|
+
|
|
787
|
+
The above example uses a functionality which we have not seen yet:
|
|
788
|
+
``C5`` being iterable, it can be used like a list in a ``for`` loop or
|
|
789
|
+
a comprehension (:ref:`section-bricks-iterators`).
|
|
790
|
+
|
|
791
|
+
Prove the formulas suggested by the above examples for the number of
|
|
792
|
+
compositions of `n` and the number of compositions of
|
|
793
|
+
`n` of length `k`; investigate by introspection
|
|
794
|
+
whether ``Sage`` uses these formulas for calculating cardinalities.
|
|
795
|
+
|
|
796
|
+
.. _section-bricks-divers:
|
|
797
|
+
|
|
798
|
+
Some other finite enumerated sets
|
|
799
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
800
|
+
|
|
801
|
+
Essentially, the principle is the same for all the finite sets with
|
|
802
|
+
which one wants to do combinatorics in ``Sage``; begin by constructing
|
|
803
|
+
an object which models this set, and then supply appropriate methods,
|
|
804
|
+
following a uniform interface [1]_. We now give a few more typical
|
|
805
|
+
examples.
|
|
806
|
+
|
|
807
|
+
Intervals of integers::
|
|
808
|
+
|
|
809
|
+
sage: C = IntegerRange(3, 21, 2); C
|
|
810
|
+
{3, 5, ..., 19}
|
|
811
|
+
sage: C.cardinality()
|
|
812
|
+
9
|
|
813
|
+
sage: C.list()
|
|
814
|
+
[3, 5, 7, 9, 11, 13, 15, 17, 19]
|
|
815
|
+
|
|
816
|
+
Permutations::
|
|
817
|
+
|
|
818
|
+
sage: C = Permutations(4); C
|
|
819
|
+
Standard permutations of 4
|
|
820
|
+
sage: C.cardinality()
|
|
821
|
+
24
|
|
822
|
+
sage: C.list()
|
|
823
|
+
[[1, 2, 3, 4], [1, 2, 4, 3], [1, 3, 2, 4], [1, 3, 4, 2],
|
|
824
|
+
[1, 4, 2, 3], [1, 4, 3, 2], [2, 1, 3, 4], [2, 1, 4, 3],
|
|
825
|
+
[2, 3, 1, 4], [2, 3, 4, 1], [2, 4, 1, 3], [2, 4, 3, 1],
|
|
826
|
+
[3, 1, 2, 4], [3, 1, 4, 2], [3, 2, 1, 4], [3, 2, 4, 1],
|
|
827
|
+
[3, 4, 1, 2], [3, 4, 2, 1], [4, 1, 2, 3], [4, 1, 3, 2],
|
|
828
|
+
[4, 2, 1, 3], [4, 2, 3, 1], [4, 3, 1, 2], [4, 3, 2, 1]]
|
|
829
|
+
|
|
830
|
+
Set partitions::
|
|
831
|
+
|
|
832
|
+
sage: C = SetPartitions(["a", "b", "c"])
|
|
833
|
+
sage: C # random print output
|
|
834
|
+
Set partitions of {'a', 'c', 'b'}
|
|
835
|
+
sage: C.cardinality()
|
|
836
|
+
5
|
|
837
|
+
sage: C.list()
|
|
838
|
+
[{{'a', 'b', 'c'}},
|
|
839
|
+
{{'a', 'b'}, {'c'}},
|
|
840
|
+
{{'a', 'c'}, {'b'}},
|
|
841
|
+
{{'a'}, {'b', 'c'}},
|
|
842
|
+
{{'a'}, {'b'}, {'c'}}]
|
|
843
|
+
|
|
844
|
+
Partial orders on a set of `8` elements, up to isomorphism::
|
|
845
|
+
|
|
846
|
+
sage: C = Posets(7); C
|
|
847
|
+
Posets containing 7 elements
|
|
848
|
+
sage: C.cardinality()
|
|
849
|
+
2045
|
|
850
|
+
|
|
851
|
+
::
|
|
852
|
+
|
|
853
|
+
sage: C.unrank(20).plot() # needs nauty sage.plot
|
|
854
|
+
Graphics object consisting of ... graphics primitives
|
|
855
|
+
|
|
856
|
+
.. image:: ../../media/a_poset.png
|
|
857
|
+
|
|
858
|
+
One can iterate through all graphs up to isomorphism. For example,
|
|
859
|
+
there are 34 simple graphs with 5 vertices::
|
|
860
|
+
|
|
861
|
+
sage: len(list(graphs(5))) # needs nauty
|
|
862
|
+
34
|
|
863
|
+
|
|
864
|
+
Here are those with at most `4` edges::
|
|
865
|
+
|
|
866
|
+
sage: up_to_four_edges = list(graphs(5, lambda G: G.size() <= 4))
|
|
867
|
+
sage: pretty_print(*up_to_four_edges)
|
|
868
|
+
|
|
869
|
+
.. image:: ../../media/graphs-5.png
|
|
870
|
+
|
|
871
|
+
However, the *set* ``C`` of these graphs is not yet available in
|
|
872
|
+
``Sage``; as a result, the following commands are not yet
|
|
873
|
+
implemented::
|
|
874
|
+
|
|
875
|
+
sage: # not implemented
|
|
876
|
+
sage: C = Graphs(5)
|
|
877
|
+
sage: C.cardinality()
|
|
878
|
+
34
|
|
879
|
+
sage: Graphs(19).cardinality()
|
|
880
|
+
24637809253125004524383007491432768
|
|
881
|
+
sage: Graphs(19).random_element()
|
|
882
|
+
Graph on 19 vertices
|
|
883
|
+
|
|
884
|
+
What we have seen so far also applies, in principle, to finite algebraic
|
|
885
|
+
structures like the dihedral groups::
|
|
886
|
+
|
|
887
|
+
sage: G = DihedralGroup(4); G
|
|
888
|
+
Dihedral group of order 8 as a permutation group
|
|
889
|
+
sage: G.cardinality()
|
|
890
|
+
8
|
|
891
|
+
sage: G.list()
|
|
892
|
+
[(), (1,3)(2,4), (1,4,3,2), (1,2,3,4), (2,4), (1,3), (1,4)(2,3), (1,2)(3,4)]
|
|
893
|
+
|
|
894
|
+
or the algebra of `2\times 2` matrices over the finite field
|
|
895
|
+
`\ZZ/2\ZZ`::
|
|
896
|
+
|
|
897
|
+
sage: C = MatrixSpace(GF(2), 2) # needs sage.modules sage.rings.finite_rings
|
|
898
|
+
sage: C.list() # needs sage.modules sage.rings.finite_rings
|
|
899
|
+
[
|
|
900
|
+
[0 0] [1 0] [0 1] [0 0] [0 0] [1 1] [1 0] [1 0] [0 1] [0 1]
|
|
901
|
+
[0 0], [0 0], [0 0], [1 0], [0 1], [0 0], [1 0], [0 1], [1 0], [0 1],
|
|
902
|
+
<BLANKLINE>
|
|
903
|
+
[0 0] [1 1] [1 1] [1 0] [0 1] [1 1]
|
|
904
|
+
[1 1], [1 0], [0 1], [1 1], [1 1], [1 1]
|
|
905
|
+
]
|
|
906
|
+
sage: C.cardinality() # needs sage.modules sage.rings.finite_rings
|
|
907
|
+
16
|
|
908
|
+
|
|
909
|
+
.. topic:: Exercise
|
|
910
|
+
|
|
911
|
+
List all the monomials of degree `5` in three variables (see
|
|
912
|
+
``IntegerVectors``). Manipulate the ordered set partitions
|
|
913
|
+
``OrderedSetPartitions`` and standard tableaux
|
|
914
|
+
(``StandardTableaux``).
|
|
915
|
+
|
|
916
|
+
.. _exercise-alternating-sign-matrices:
|
|
917
|
+
|
|
918
|
+
.. topic:: Exercise
|
|
919
|
+
|
|
920
|
+
List the alternating sign matrices of size `3`, `4`,
|
|
921
|
+
and `5` (``AlternatingSignMatrices``), and try to guess the
|
|
922
|
+
definition. The discovery and proof of the formula for the
|
|
923
|
+
enumeration of these matrices (see the method ``cardinality``),
|
|
924
|
+
motivated by calculations of determinants in physics, is quite a
|
|
925
|
+
story. In particular, the first proof, given by Zeilberger in 1992
|
|
926
|
+
was automatically produced by a computer program. It was 84 pages long,
|
|
927
|
+
and required nearly a hundred people to verify it.
|
|
928
|
+
|
|
929
|
+
.. topic:: Exercise
|
|
930
|
+
|
|
931
|
+
Calculate by hand the number of vectors in `(\ZZ/2\ZZ)^5`, and
|
|
932
|
+
the number of matrices in `GL_3(\ZZ/2\ZZ)` (that is to say,
|
|
933
|
+
the number of invertible `3\times 3` matrices with
|
|
934
|
+
coefficients in `\ZZ/2\ZZ`). Verify your answer with ``Sage``.
|
|
935
|
+
Generalize to `GL_n(\ZZ/q\ZZ)`.
|
|
936
|
+
|
|
937
|
+
.. _section-bricks-iterators:
|
|
938
|
+
|
|
939
|
+
Set comprehension and iterators
|
|
940
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
941
|
+
|
|
942
|
+
We will now show some of the possibilities offered by ``Python`` for
|
|
943
|
+
constructing (and iterating through) sets, with a notation that is
|
|
944
|
+
flexible and close to usual mathematical usage, and in particular the
|
|
945
|
+
benefits this yields in combinatorics.
|
|
946
|
+
|
|
947
|
+
We begin by constructing the finite set
|
|
948
|
+
`\{i^2\ \|\ i \in \{1,3,7\}\}`::
|
|
949
|
+
|
|
950
|
+
sage: [ i^2 for i in [1, 3, 7] ]
|
|
951
|
+
[1, 9, 49]
|
|
952
|
+
|
|
953
|
+
and then the same set, but with `i` running from `1` to
|
|
954
|
+
`9`::
|
|
955
|
+
|
|
956
|
+
sage: [ i^2 for i in range(1,10) ]
|
|
957
|
+
[1, 4, 9, 16, 25, 36, 49, 64, 81]
|
|
958
|
+
|
|
959
|
+
A construction of this form in ``Python`` is called *set comprehension*.
|
|
960
|
+
A clause can be added to keep only those elements with `i` prime::
|
|
961
|
+
|
|
962
|
+
sage: [ i^2 for i in range(1,10) if is_prime(i) ]
|
|
963
|
+
[4, 9, 25, 49]
|
|
964
|
+
|
|
965
|
+
Combining more than one set comprehension, it is possible to construct
|
|
966
|
+
the set `\{(i,j) \ | \ 1\leq j < i <5\}`::
|
|
967
|
+
|
|
968
|
+
sage: [ (i,j) for i in range(1,6) for j in range(1,i) ]
|
|
969
|
+
[(2, 1), (3, 1), (3, 2), (4, 1), (4, 2), (4, 3),
|
|
970
|
+
(5, 1), (5, 2), (5, 3), (5, 4)]
|
|
971
|
+
|
|
972
|
+
or to produce Pascal’s triangle::
|
|
973
|
+
|
|
974
|
+
sage: [[binomial(n,i) for i in range(n+1)] for n in range(10)]
|
|
975
|
+
[[1],
|
|
976
|
+
[1, 1],
|
|
977
|
+
[1, 2, 1],
|
|
978
|
+
[1, 3, 3, 1],
|
|
979
|
+
[1, 4, 6, 4, 1],
|
|
980
|
+
[1, 5, 10, 10, 5, 1],
|
|
981
|
+
[1, 6, 15, 20, 15, 6, 1],
|
|
982
|
+
[1, 7, 21, 35, 35, 21, 7, 1],
|
|
983
|
+
[1, 8, 28, 56, 70, 56, 28, 8, 1],
|
|
984
|
+
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]]
|
|
985
|
+
|
|
986
|
+
The execution of a set comprehension is accomplished in two steps; first
|
|
987
|
+
an *iterator* is constructed, and then a list is filled with the
|
|
988
|
+
elements successively produced by the iterator. Technically, an
|
|
989
|
+
*iterator* is an object with a method ``next`` which returns a new value
|
|
990
|
+
each time it is called, until it is exhausted. For example, the
|
|
991
|
+
following iterator ``it``::
|
|
992
|
+
|
|
993
|
+
sage: it = (binomial(3, i) for i in range(4))
|
|
994
|
+
|
|
995
|
+
returns successively the binomial coefficients `\binom 3 i` with
|
|
996
|
+
`i=0,1,2,3`::
|
|
997
|
+
|
|
998
|
+
sage: next(it)
|
|
999
|
+
1
|
|
1000
|
+
sage: next(it)
|
|
1001
|
+
3
|
|
1002
|
+
sage: next(it)
|
|
1003
|
+
3
|
|
1004
|
+
sage: next(it)
|
|
1005
|
+
1
|
|
1006
|
+
|
|
1007
|
+
When the iterator is finally exhausted, an exception is raised::
|
|
1008
|
+
|
|
1009
|
+
sage: next(it)
|
|
1010
|
+
Traceback (most recent call last):
|
|
1011
|
+
...
|
|
1012
|
+
StopIteration
|
|
1013
|
+
|
|
1014
|
+
More generally, an *iterable* is a ``Python`` object ``L`` (a list,
|
|
1015
|
+
a set, ...) over whose elements it is possible to iterate. Technically,
|
|
1016
|
+
the iterator is constructed by ``iter(L)``. In practice, the commands
|
|
1017
|
+
``iter`` and ``next`` are used very rarely, since ``for`` loops and list
|
|
1018
|
+
comprehensions provide a much pleasanter syntax::
|
|
1019
|
+
|
|
1020
|
+
sage: for s in Subsets(3):
|
|
1021
|
+
....: print(s)
|
|
1022
|
+
{}
|
|
1023
|
+
{1}
|
|
1024
|
+
{2}
|
|
1025
|
+
{3}
|
|
1026
|
+
{1, 2}
|
|
1027
|
+
{1, 3}
|
|
1028
|
+
{2, 3}
|
|
1029
|
+
{1, 2, 3}
|
|
1030
|
+
|
|
1031
|
+
::
|
|
1032
|
+
|
|
1033
|
+
sage: [ s.cardinality() for s in Subsets(3) ]
|
|
1034
|
+
[0, 1, 1, 1, 2, 2, 2, 3]
|
|
1035
|
+
|
|
1036
|
+
What is the point of an iterator? Consider the following example::
|
|
1037
|
+
|
|
1038
|
+
sage: sum( [ binomial(8, i) for i in range(9) ] )
|
|
1039
|
+
256
|
|
1040
|
+
|
|
1041
|
+
When it is executed, a list of `9` elements is constructed, and
|
|
1042
|
+
then it is passed as an argument to ``sum`` to add them up. If, on the
|
|
1043
|
+
other hand, the iterator is passed directly to ``sum`` (note the absence
|
|
1044
|
+
of square brackets)::
|
|
1045
|
+
|
|
1046
|
+
sage: sum( binomial(8, i) for i in range(9) )
|
|
1047
|
+
256
|
|
1048
|
+
|
|
1049
|
+
the function ``sum`` receives the iterator directly, and can
|
|
1050
|
+
short-circuit the construction of the intermediate list. If there are a
|
|
1051
|
+
large number of elements, this avoids allocating a large quantity of
|
|
1052
|
+
memory to fill a list which will be immediately destroyed.
|
|
1053
|
+
|
|
1054
|
+
Most functions that take a list of elements as input will also accept
|
|
1055
|
+
an iterator (or an iterable) instead. To begin with, one can obtain the
|
|
1056
|
+
list (or the tuple) of elements of an iterator as follows::
|
|
1057
|
+
|
|
1058
|
+
sage: list(binomial(8, i) for i in range(9))
|
|
1059
|
+
[1, 8, 28, 56, 70, 56, 28, 8, 1]
|
|
1060
|
+
sage: tuple(binomial(8, i) for i in range(9))
|
|
1061
|
+
(1, 8, 28, 56, 70, 56, 28, 8, 1)
|
|
1062
|
+
|
|
1063
|
+
We now consider the functions ``all`` and ``any`` which denote
|
|
1064
|
+
respectively the `n`-ary *and* and *or*::
|
|
1065
|
+
|
|
1066
|
+
sage: all([True, True, True, True])
|
|
1067
|
+
True
|
|
1068
|
+
sage: all([True, False, True, True])
|
|
1069
|
+
False
|
|
1070
|
+
sage: any([False, False, False, False])
|
|
1071
|
+
False
|
|
1072
|
+
sage: any([False, False, True, False])
|
|
1073
|
+
True
|
|
1074
|
+
|
|
1075
|
+
The following example verifies that all primes from `3` to
|
|
1076
|
+
`99` are odd::
|
|
1077
|
+
|
|
1078
|
+
sage: all( is_odd(p) for p in range(3,100) if is_prime(p) )
|
|
1079
|
+
True
|
|
1080
|
+
|
|
1081
|
+
A *Mersenne prime* is a prime of the form `2^p -1`. We verify
|
|
1082
|
+
that, for `p<1000`, if `2^p-1` is prime, then
|
|
1083
|
+
`p` is also prime::
|
|
1084
|
+
|
|
1085
|
+
sage: def mersenne(p): return 2^p -1
|
|
1086
|
+
sage: [ is_prime(p)
|
|
1087
|
+
....: for p in range(1000) if is_prime(mersenne(p)) ]
|
|
1088
|
+
[True, True, True, True, True, True, True, True, True, True,
|
|
1089
|
+
True, True, True, True]
|
|
1090
|
+
|
|
1091
|
+
Is the converse true?
|
|
1092
|
+
|
|
1093
|
+
.. topic:: Exercise
|
|
1094
|
+
|
|
1095
|
+
Try the two following commands and explain the considerable
|
|
1096
|
+
difference in the length of the calculations::
|
|
1097
|
+
|
|
1098
|
+
sage: all( is_prime(mersenne(p))
|
|
1099
|
+
....: for p in range(1000) if is_prime(p) )
|
|
1100
|
+
False
|
|
1101
|
+
sage: all( [ is_prime(mersenne(p))
|
|
1102
|
+
....: for p in range(1000) if is_prime(p)] )
|
|
1103
|
+
False
|
|
1104
|
+
|
|
1105
|
+
We now try to find the smallest counter-example. In order to do this, we
|
|
1106
|
+
use the ``Sage`` function ``exists``::
|
|
1107
|
+
|
|
1108
|
+
sage: exists( (p for p in range(1000) if is_prime(p)),
|
|
1109
|
+
....: lambda p: not is_prime(mersenne(p)) )
|
|
1110
|
+
(True, 11)
|
|
1111
|
+
|
|
1112
|
+
Alternatively, we could construct an iterator on the counter-examples::
|
|
1113
|
+
|
|
1114
|
+
sage: counter_examples = (p for p in range(1000)
|
|
1115
|
+
....: if is_prime(p) and not is_prime(mersenne(p)))
|
|
1116
|
+
sage: next(counter_examples)
|
|
1117
|
+
11
|
|
1118
|
+
sage: next(counter_examples)
|
|
1119
|
+
23
|
|
1120
|
+
|
|
1121
|
+
.. topic:: Exercise
|
|
1122
|
+
|
|
1123
|
+
What do the following commands do?
|
|
1124
|
+
|
|
1125
|
+
::
|
|
1126
|
+
|
|
1127
|
+
sage: cubes = [t**3 for t in range(-999,1000)]
|
|
1128
|
+
sage: exists([(x,y) for x in cubes for y in cubes], # long time (3s, 2012)
|
|
1129
|
+
....: lambda x_y: x_y[0] + x_y[1] == 218)
|
|
1130
|
+
(True, (-125, 343))
|
|
1131
|
+
sage: exists(((x,y) for x in cubes for y in cubes), # long time (2s, 2012)
|
|
1132
|
+
....: lambda x_y: x_y[0] + x_y[1] == 218)
|
|
1133
|
+
(True, (-125, 343))
|
|
1134
|
+
|
|
1135
|
+
Which of the last two is more economical in terms of time? In terms
|
|
1136
|
+
of memory? By how much?
|
|
1137
|
+
|
|
1138
|
+
.. topic:: Exercise
|
|
1139
|
+
|
|
1140
|
+
Try each of the following commands, and explain its result. If
|
|
1141
|
+
possible, hide the result first and try to guess it before
|
|
1142
|
+
launching the command.
|
|
1143
|
+
|
|
1144
|
+
.. TODO:: hide the results by default
|
|
1145
|
+
|
|
1146
|
+
.. warning:: it will be necessary to interrupt the execution of some of the commands
|
|
1147
|
+
|
|
1148
|
+
::
|
|
1149
|
+
|
|
1150
|
+
sage: x = var('x') # needs sage.symbolic
|
|
1151
|
+
sage: sum(x^len(s) for s in Subsets(8)) # needs sage.symbolic
|
|
1152
|
+
x^8 + 8*x^7 + 28*x^6 + 56*x^5 + 70*x^4 + 56*x^3 + 28*x^2 + 8*x + 1
|
|
1153
|
+
|
|
1154
|
+
::
|
|
1155
|
+
|
|
1156
|
+
sage: sum(x^p.length() for p in Permutations(3)) # needs sage.symbolic
|
|
1157
|
+
x^3 + 2*x^2 + 2*x + 1
|
|
1158
|
+
|
|
1159
|
+
::
|
|
1160
|
+
|
|
1161
|
+
sage: factor(sum(x^p.length() for p in Permutations(3))) # needs sage.symbolic
|
|
1162
|
+
(x^2 + x + 1)*(x + 1)
|
|
1163
|
+
|
|
1164
|
+
::
|
|
1165
|
+
|
|
1166
|
+
sage: P = Permutations(5)
|
|
1167
|
+
sage: all(p in P for p in P)
|
|
1168
|
+
True
|
|
1169
|
+
|
|
1170
|
+
::
|
|
1171
|
+
|
|
1172
|
+
sage: for p in GL(2, 2): print(p); print("")
|
|
1173
|
+
[1 0]
|
|
1174
|
+
[0 1]
|
|
1175
|
+
<BLANKLINE>
|
|
1176
|
+
[0 1]
|
|
1177
|
+
[1 0]
|
|
1178
|
+
<BLANKLINE>
|
|
1179
|
+
[0 1]
|
|
1180
|
+
[1 1]
|
|
1181
|
+
<BLANKLINE>
|
|
1182
|
+
[1 1]
|
|
1183
|
+
[0 1]
|
|
1184
|
+
<BLANKLINE>
|
|
1185
|
+
[1 1]
|
|
1186
|
+
[1 0]
|
|
1187
|
+
<BLANKLINE>
|
|
1188
|
+
[1 0]
|
|
1189
|
+
[1 1]
|
|
1190
|
+
<BLANKLINE>
|
|
1191
|
+
|
|
1192
|
+
::
|
|
1193
|
+
|
|
1194
|
+
sage: for p in Partitions(3): print(p) # not tested
|
|
1195
|
+
[3]
|
|
1196
|
+
[2, 1]
|
|
1197
|
+
[1, 1, 1]
|
|
1198
|
+
...
|
|
1199
|
+
|
|
1200
|
+
::
|
|
1201
|
+
|
|
1202
|
+
sage: for p in Partitions(): print(p) # not tested
|
|
1203
|
+
[]
|
|
1204
|
+
[1]
|
|
1205
|
+
[2]
|
|
1206
|
+
[1, 1]
|
|
1207
|
+
[3]
|
|
1208
|
+
...
|
|
1209
|
+
|
|
1210
|
+
::
|
|
1211
|
+
|
|
1212
|
+
sage: for p in Primes(): print(p) # not tested
|
|
1213
|
+
2
|
|
1214
|
+
3
|
|
1215
|
+
5
|
|
1216
|
+
7
|
|
1217
|
+
...
|
|
1218
|
+
|
|
1219
|
+
::
|
|
1220
|
+
|
|
1221
|
+
sage: exists( Primes(), lambda p: not is_prime(mersenne(p)) )
|
|
1222
|
+
(True, 11)
|
|
1223
|
+
|
|
1224
|
+
::
|
|
1225
|
+
|
|
1226
|
+
sage: counter_examples = (p for p in Primes()
|
|
1227
|
+
....: if not is_prime(mersenne(p)))
|
|
1228
|
+
sage: for p in counter_examples: print(p) # not tested
|
|
1229
|
+
11
|
|
1230
|
+
23
|
|
1231
|
+
29
|
|
1232
|
+
37
|
|
1233
|
+
41
|
|
1234
|
+
43
|
|
1235
|
+
47
|
|
1236
|
+
...
|
|
1237
|
+
|
|
1238
|
+
Operations on iterators
|
|
1239
|
+
^^^^^^^^^^^^^^^^^^^^^^^
|
|
1240
|
+
|
|
1241
|
+
``Python`` provides numerous tools for manipulating iterators; most of them
|
|
1242
|
+
are in the :mod:`itertools` library, which can be imported by::
|
|
1243
|
+
|
|
1244
|
+
sage: import itertools
|
|
1245
|
+
|
|
1246
|
+
We will demonstrate some applications, taking as a starting point the
|
|
1247
|
+
permutations of `3`::
|
|
1248
|
+
|
|
1249
|
+
sage: list(Permutations(3))
|
|
1250
|
+
[[1, 2, 3], [1, 3, 2], [2, 1, 3],
|
|
1251
|
+
[2, 3, 1], [3, 1, 2], [3, 2, 1]]
|
|
1252
|
+
|
|
1253
|
+
We can list the elements of a set by numbering them::
|
|
1254
|
+
|
|
1255
|
+
sage: list(enumerate(Permutations(3)))
|
|
1256
|
+
[(0, [1, 2, 3]), (1, [1, 3, 2]), (2, [2, 1, 3]),
|
|
1257
|
+
(3, [2, 3, 1]), (4, [3, 1, 2]), (5, [3, 2, 1])]
|
|
1258
|
+
|
|
1259
|
+
or select only the elements in positions 2, 3, and 4 (analogue of
|
|
1260
|
+
``l[1:4]``)::
|
|
1261
|
+
|
|
1262
|
+
sage: import itertools
|
|
1263
|
+
sage: list(itertools.islice(Permutations(3), int(1), int(4)))
|
|
1264
|
+
[[1, 3, 2], [2, 1, 3], [2, 3, 1]]
|
|
1265
|
+
|
|
1266
|
+
To apply a function to all the elements, one can do::
|
|
1267
|
+
|
|
1268
|
+
sage: [z.cycle_type() for z in Permutations(3)]
|
|
1269
|
+
[[1, 1, 1], [2, 1], [2, 1], [3], [3], [2, 1]]
|
|
1270
|
+
|
|
1271
|
+
and similarly to select the elements satisfying a certain condition::
|
|
1272
|
+
|
|
1273
|
+
sage: [z for z in Permutations(3) if z.has_pattern([1,2])]
|
|
1274
|
+
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2]]
|
|
1275
|
+
|
|
1276
|
+
Implementation of new iterators
|
|
1277
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
1278
|
+
|
|
1279
|
+
It is easy to construct new iterators, using the keyword ``yield``
|
|
1280
|
+
instead of ``return`` in a function::
|
|
1281
|
+
|
|
1282
|
+
sage: def f(n):
|
|
1283
|
+
....: for i in range(n):
|
|
1284
|
+
....: yield i
|
|
1285
|
+
|
|
1286
|
+
After the ``yield``, execution is not halted, but only suspended, ready
|
|
1287
|
+
to be continued from the same point. The result of the function is
|
|
1288
|
+
therefore an iterator over the successive values returned by ``yield``::
|
|
1289
|
+
|
|
1290
|
+
sage: g = f(4)
|
|
1291
|
+
sage: next(g)
|
|
1292
|
+
0
|
|
1293
|
+
sage: next(g)
|
|
1294
|
+
1
|
|
1295
|
+
sage: next(g)
|
|
1296
|
+
2
|
|
1297
|
+
sage: next(g)
|
|
1298
|
+
3
|
|
1299
|
+
|
|
1300
|
+
::
|
|
1301
|
+
|
|
1302
|
+
sage: next(g)
|
|
1303
|
+
Traceback (most recent call last):
|
|
1304
|
+
...
|
|
1305
|
+
StopIteration
|
|
1306
|
+
|
|
1307
|
+
The function could be used as follows::
|
|
1308
|
+
|
|
1309
|
+
sage: [ x for x in f(5) ]
|
|
1310
|
+
[0, 1, 2, 3, 4]
|
|
1311
|
+
|
|
1312
|
+
This model of computation, called *continuation*, is very useful in
|
|
1313
|
+
combinatorics, especially when combined with recursion. Here is how to
|
|
1314
|
+
generate all words of a given length on a given alphabet::
|
|
1315
|
+
|
|
1316
|
+
sage: def words(alphabet, l):
|
|
1317
|
+
....: if l == 0:
|
|
1318
|
+
....: yield []
|
|
1319
|
+
....: else:
|
|
1320
|
+
....: for word in words(alphabet, l-1):
|
|
1321
|
+
....: for l in alphabet:
|
|
1322
|
+
....: yield word + [l]
|
|
1323
|
+
sage: [ w for w in words(['a','b'], 3) ]
|
|
1324
|
+
[['a', 'a', 'a'], ['a', 'a', 'b'], ['a', 'b', 'a'],
|
|
1325
|
+
['a', 'b', 'b'], ['b', 'a', 'a'], ['b', 'a', 'b'],
|
|
1326
|
+
['b', 'b', 'a'], ['b', 'b', 'b']]
|
|
1327
|
+
|
|
1328
|
+
These words can then be counted by::
|
|
1329
|
+
|
|
1330
|
+
sage: sum(1 for w in words(['a','b','c','d'], 10))
|
|
1331
|
+
1048576
|
|
1332
|
+
|
|
1333
|
+
Counting the words one by one is clearly not an efficient method in this
|
|
1334
|
+
case, since the formula `n^\ell` is also available; note,
|
|
1335
|
+
though, that this is not the stupidest possible approach - it does, at
|
|
1336
|
+
least, avoid constructing the entire list in memory.
|
|
1337
|
+
|
|
1338
|
+
We now consider Dyck words, which are well-parenthesized words in the
|
|
1339
|
+
letters “`(`” and “`)`”. The function below generates
|
|
1340
|
+
all the Dyck words of a given length (where the length is the number of
|
|
1341
|
+
pairs of parentheses), using the recursive definition which says that a
|
|
1342
|
+
Dyck word is either empty or of the form `(w_1)w_2` where
|
|
1343
|
+
`w_1` and `w_2` are Dyck words::
|
|
1344
|
+
|
|
1345
|
+
sage: def dyck_words(l):
|
|
1346
|
+
....: if l==0:
|
|
1347
|
+
....: yield ''
|
|
1348
|
+
....: else:
|
|
1349
|
+
....: for k in range(l):
|
|
1350
|
+
....: for w1 in dyck_words(k):
|
|
1351
|
+
....: for w2 in dyck_words(l-k-1):
|
|
1352
|
+
....: yield '('+w1+')'+w2
|
|
1353
|
+
|
|
1354
|
+
Here are all the Dyck words of length `4`::
|
|
1355
|
+
|
|
1356
|
+
sage: list(dyck_words(4))
|
|
1357
|
+
['()()()()', '()()(())', '()(())()', '()(()())', '()((()))',
|
|
1358
|
+
'(())()()', '(())(())', '(()())()', '((()))()', '(()()())',
|
|
1359
|
+
'(()(()))', '((())())', '((()()))', '(((())))']
|
|
1360
|
+
|
|
1361
|
+
Counting them, we recover a well-known sequence::
|
|
1362
|
+
|
|
1363
|
+
sage: [ sum(1 for w in dyck_words(l)) for l in range(10) ]
|
|
1364
|
+
[1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862]
|
|
1365
|
+
|
|
1366
|
+
.. _exo-iterators-catalan:
|
|
1367
|
+
|
|
1368
|
+
.. topic:: Exercise: complete binary tree iterator
|
|
1369
|
+
|
|
1370
|
+
Construct an iterator on the set `C_n` of complete binary
|
|
1371
|
+
trees with `n` leaves
|
|
1372
|
+
(see :ref:`section-examples-catalan`).
|
|
1373
|
+
|
|
1374
|
+
Hint: ``Sage`` 4.8.2 does not yet have a native data structure to
|
|
1375
|
+
represent complete binary trees. One simple way to represent them is
|
|
1376
|
+
to define a formal variable ``Leaf`` for the leaves and a formal
|
|
1377
|
+
2-ary function ``Node``::
|
|
1378
|
+
|
|
1379
|
+
sage: var('Leaf') # needs sage.symbolic
|
|
1380
|
+
Leaf
|
|
1381
|
+
sage: function('Node', nargs=2) # needs sage.symbolic
|
|
1382
|
+
Node
|
|
1383
|
+
|
|
1384
|
+
The second tree in :ref:`figure-examples-catalan-trees`
|
|
1385
|
+
can be represented by the expression::
|
|
1386
|
+
|
|
1387
|
+
sage: tr = Node(Node(Leaf, Node(Leaf, Leaf)), Leaf) # needs sage.symbolic
|
|
1388
|
+
|
|
1389
|
+
.. _section-constructions:
|
|
1390
|
+
|
|
1391
|
+
Constructions
|
|
1392
|
+
-------------
|
|
1393
|
+
|
|
1394
|
+
We will now see how to construct new sets starting from these building
|
|
1395
|
+
blocks. In fact, we have already begun to do this with the construction
|
|
1396
|
+
of `\mathcal P(\mathcal P(\mathcal P(\{1,2,3,4\})))` in the
|
|
1397
|
+
previous section, and to construct the example of sets of cards in
|
|
1398
|
+
:ref:`section-examples`.
|
|
1399
|
+
|
|
1400
|
+
Consider a large Cartesian product::
|
|
1401
|
+
|
|
1402
|
+
sage: C = cartesian_product([Compositions(8), Permutations(20)]); C
|
|
1403
|
+
The Cartesian product of (Compositions of 8, Standard permutations of 20)
|
|
1404
|
+
sage: C.cardinality()
|
|
1405
|
+
311411457046609920000
|
|
1406
|
+
|
|
1407
|
+
Clearly, it is impractical to construct the list of all the elements of this
|
|
1408
|
+
Cartesian product! And, in the following example, `H` is equipped with the
|
|
1409
|
+
usual combinatorial operations and also its structure as a product group::
|
|
1410
|
+
|
|
1411
|
+
sage: G = DihedralGroup(4)
|
|
1412
|
+
sage: H = cartesian_product([G,G])
|
|
1413
|
+
sage: H in Groups()
|
|
1414
|
+
True
|
|
1415
|
+
sage: H.an_element()
|
|
1416
|
+
((1,3), (1,3))
|
|
1417
|
+
sage: t = H([G.gen(0), G.gen(0)])
|
|
1418
|
+
sage: t
|
|
1419
|
+
((1,2,3,4), (1,2,3,4))
|
|
1420
|
+
sage: t*t
|
|
1421
|
+
((1,3)(2,4), (1,3)(2,4))
|
|
1422
|
+
|
|
1423
|
+
We now construct the union of two existing disjoint sets::
|
|
1424
|
+
|
|
1425
|
+
sage: C = DisjointUnionEnumeratedSets(
|
|
1426
|
+
....: [ Compositions(4), Permutations(3)] )
|
|
1427
|
+
sage: C
|
|
1428
|
+
Disjoint union of Family (Compositions of 4,
|
|
1429
|
+
Standard permutations of 3)
|
|
1430
|
+
sage: C.cardinality()
|
|
1431
|
+
14
|
|
1432
|
+
sage: C.list()
|
|
1433
|
+
[[1, 1, 1, 1], [1, 1, 2], [1, 2, 1], [1, 3], [2, 1, 1], [2, 2],
|
|
1434
|
+
[3, 1], [4], [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1],
|
|
1435
|
+
[3, 1, 2], [3, 2, 1]]
|
|
1436
|
+
|
|
1437
|
+
It is also possible to take the union of more than two disjoint sets, or
|
|
1438
|
+
even an infinite number of them. We will now construct the set of all
|
|
1439
|
+
permutations, viewed as the union of the sets `P_n` of
|
|
1440
|
+
permutations of size `n`. We begin by constructing the infinite
|
|
1441
|
+
family `F=(P_n)_{n\in N}`::
|
|
1442
|
+
|
|
1443
|
+
sage: F = Family(NonNegativeIntegers(), Permutations); F
|
|
1444
|
+
Lazy family (<class 'sage.combinat.permutation.Permutations'>(i))_{i in Non negative integers}
|
|
1445
|
+
sage: F.keys()
|
|
1446
|
+
Non negative integers
|
|
1447
|
+
sage: F[1000]
|
|
1448
|
+
Standard permutations of 1000
|
|
1449
|
+
|
|
1450
|
+
Now we can construct the disjoint union `\bigcup_{n\in \NN}P_n`::
|
|
1451
|
+
|
|
1452
|
+
sage: U = DisjointUnionEnumeratedSets(F); U
|
|
1453
|
+
Disjoint union of
|
|
1454
|
+
Lazy family (<class 'sage.combinat.permutation.Permutations'>(i))_{i in Non negative integers}
|
|
1455
|
+
|
|
1456
|
+
It is an infinite set::
|
|
1457
|
+
|
|
1458
|
+
sage: U.cardinality()
|
|
1459
|
+
+Infinity
|
|
1460
|
+
|
|
1461
|
+
which doesn’t prohibit iteration through its elements, though it will be
|
|
1462
|
+
necessary to interrupt it at some point::
|
|
1463
|
+
|
|
1464
|
+
sage: for p in U: # not tested
|
|
1465
|
+
....: print(p)
|
|
1466
|
+
[]
|
|
1467
|
+
[1]
|
|
1468
|
+
[1, 2]
|
|
1469
|
+
[2, 1]
|
|
1470
|
+
[1, 2, 3]
|
|
1471
|
+
[1, 3, 2]
|
|
1472
|
+
[2, 1, 3]
|
|
1473
|
+
[2, 3, 1]
|
|
1474
|
+
[3, 1, 2]
|
|
1475
|
+
...
|
|
1476
|
+
|
|
1477
|
+
Note: the above set could also have been constructed directly with::
|
|
1478
|
+
|
|
1479
|
+
sage: U = Permutations(); U
|
|
1480
|
+
Standard permutations
|
|
1481
|
+
|
|
1482
|
+
Summary
|
|
1483
|
+
~~~~~~~
|
|
1484
|
+
|
|
1485
|
+
``Sage`` provides a library of common enumerated sets, which can be
|
|
1486
|
+
combined by standard constructions, giving a toolbox that is flexible
|
|
1487
|
+
(but which could still be expanded). It is also possible to add new
|
|
1488
|
+
building blocks to ``Sage`` with a few lines (see the code in
|
|
1489
|
+
``FiniteEnumeratedSets().example()``). This is made possible by the
|
|
1490
|
+
uniformity of the interfaces and the fact that ``Sage`` is based on an
|
|
1491
|
+
object-oriented language. Also, very large or even infinite sets can
|
|
1492
|
+
be manipulated thanks to lazy evaluation strategies (iterators, etc.).
|
|
1493
|
+
|
|
1494
|
+
There is no magic to any of this: under the hood, ``Sage`` applies the
|
|
1495
|
+
usual rules (for example, that the cardinality of `E\times E` is
|
|
1496
|
+
`|E|^2`); the added value comes from the capacity to manipulate
|
|
1497
|
+
complicated constructions. The situation is comparable to ``Sage``’s
|
|
1498
|
+
implementation of differential calculus: ``Sage`` applies the usual
|
|
1499
|
+
rules for differentiation of functions and their compositions, where
|
|
1500
|
+
the added value comes from the possibility of manipulating complicated
|
|
1501
|
+
formulas. In this sense, ``Sage`` implements a *calculus* of finite
|
|
1502
|
+
enumerated sets.
|
|
1503
|
+
|
|
1504
|
+
.. _section-generic:
|
|
1505
|
+
|
|
1506
|
+
Generic algorithms
|
|
1507
|
+
------------------
|
|
1508
|
+
|
|
1509
|
+
.. _section-generic-integerlistlex:
|
|
1510
|
+
|
|
1511
|
+
Lexicographic generation of lists of integers
|
|
1512
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
1513
|
+
|
|
1514
|
+
Among the classic enumerated sets, especially in algebraic
|
|
1515
|
+
combinatorics, a certain number are composed of lists of integers of
|
|
1516
|
+
fixed sum, such as partitions, compositions, or integer vectors. These
|
|
1517
|
+
examples can also have supplementary constraints added to them. Here are
|
|
1518
|
+
some examples. We start with the integer vectors with sum `10`
|
|
1519
|
+
and length `3`, with parts bounded below by `2`,
|
|
1520
|
+
`4` and `2` respectively::
|
|
1521
|
+
|
|
1522
|
+
sage: IntegerVectors(10, 3, min_part=2, max_part=5,
|
|
1523
|
+
....: inner=[2, 4, 2]).list()
|
|
1524
|
+
[[4, 4, 2], [3, 5, 2], [3, 4, 3], [2, 5, 3], [2, 4, 4]]
|
|
1525
|
+
|
|
1526
|
+
The compositions of `5` with each part at most `3`, and
|
|
1527
|
+
with length `2` or `3`::
|
|
1528
|
+
|
|
1529
|
+
sage: Compositions(5, max_part=3,
|
|
1530
|
+
....: min_length=2, max_length=3).list()
|
|
1531
|
+
[[3, 2], [3, 1, 1], [2, 3], [2, 2, 1], [2, 1, 2], [1, 3, 1],
|
|
1532
|
+
[1, 2, 2], [1, 1, 3]]
|
|
1533
|
+
|
|
1534
|
+
The strictly decreasing partitions of `5`::
|
|
1535
|
+
|
|
1536
|
+
sage: Partitions(5, max_slope=-1).list()
|
|
1537
|
+
[[5], [4, 1], [3, 2]]
|
|
1538
|
+
|
|
1539
|
+
These sets share the same underlying algorithmic structure, implemented
|
|
1540
|
+
in the more general (and slightly more cumbersome) class
|
|
1541
|
+
``IntegerListsLex``. This class models sets of vectors
|
|
1542
|
+
`(\ell_0,\dots,\ell_k)` of nonnegative integers, with
|
|
1543
|
+
constraints on the sum and the length, and bounds on the parts and on
|
|
1544
|
+
the consecutive differences between the parts. Here are some more
|
|
1545
|
+
examples::
|
|
1546
|
+
|
|
1547
|
+
sage: IntegerListsLex(10, length=3,
|
|
1548
|
+
....: min_part=2, max_part=5,
|
|
1549
|
+
....: floor=[2, 4, 2]).list()
|
|
1550
|
+
[[4, 4, 2], [3, 5, 2], [3, 4, 3], [2, 5, 3], [2, 4, 4]]
|
|
1551
|
+
|
|
1552
|
+
sage: IntegerListsLex(5, min_part=1, max_part=3,
|
|
1553
|
+
....: min_length=2, max_length=3).list()
|
|
1554
|
+
[[3, 2], [3, 1, 1], [2, 3], [2, 2, 1], [2, 1, 2],
|
|
1555
|
+
[1, 3, 1], [1, 2, 2], [1, 1, 3]]
|
|
1556
|
+
|
|
1557
|
+
sage: IntegerListsLex(5, min_part=1, max_slope=-1).list()
|
|
1558
|
+
[[5], [4, 1], [3, 2]]
|
|
1559
|
+
|
|
1560
|
+
sage: list(Compositions(5, max_length=2))
|
|
1561
|
+
[[5], [4, 1], [3, 2], [2, 3], [1, 4]]
|
|
1562
|
+
|
|
1563
|
+
sage: list(IntegerListsLex(5, max_length=2, min_part=1))
|
|
1564
|
+
[[5], [4, 1], [3, 2], [2, 3], [1, 4]]
|
|
1565
|
+
|
|
1566
|
+
The point of the model of ``IntegerListsLex`` is in the compromise
|
|
1567
|
+
between generality and efficiency. The main algorithm permits
|
|
1568
|
+
iteration through the elements of such a set `S` in reverse
|
|
1569
|
+
lexicographic order with a good complexity in most practical use
|
|
1570
|
+
cases. Roughly speaking, the time needed to iterate through all the
|
|
1571
|
+
elements of `S` is proportional to the number of elements, where the
|
|
1572
|
+
proportion factor is controlled by the length `l` of the longest
|
|
1573
|
+
element of `S`. In addition, the memory usage is also controlled by
|
|
1574
|
+
`l`, which is to say negligible in practice.
|
|
1575
|
+
|
|
1576
|
+
This algorithm is based on a very general principle for traversing a
|
|
1577
|
+
decision tree, called *branch and bound*: at the top level, we run
|
|
1578
|
+
through all the possible choices for `\ell_0`; for each of these
|
|
1579
|
+
choices, we run through all the possible choices for `\ell_1`,
|
|
1580
|
+
and so on. Mathematically speaking, we have put the structure of a
|
|
1581
|
+
prefix tree on the elements of `S`: a node of the tree at depth
|
|
1582
|
+
`k` corresponds to a prefix `\ell_0,\dots,\ell_k` of one
|
|
1583
|
+
(or more) elements of `S` (see :ref:`figure-prefix-tree-partitions`).
|
|
1584
|
+
|
|
1585
|
+
.. _figure-prefix-tree-partitions:
|
|
1586
|
+
|
|
1587
|
+
.. figure:: ../../media/prefix-tree-partitions-5.png
|
|
1588
|
+
:scale: 150%
|
|
1589
|
+
|
|
1590
|
+
Figure: The prefix tree of the partitions of 5.
|
|
1591
|
+
|
|
1592
|
+
The usual problem with this type of approach is to avoid bad decisions
|
|
1593
|
+
which lead to leaving the prefix tree and exploring dead branches;
|
|
1594
|
+
this is particularly problematic because the growth of the number of
|
|
1595
|
+
elements is usually exponential in the depth. It turns out that the
|
|
1596
|
+
constraints listed above are simple enough to be able to reasonably
|
|
1597
|
+
predict when a sequence `\ell_0,\dots,\ell_k` is a prefix of some
|
|
1598
|
+
element `S`. Hence, most dead branches can be pruned.
|
|
1599
|
+
|
|
1600
|
+
.. _section-generic-polytopes:
|
|
1601
|
+
|
|
1602
|
+
Integer points in polytopes
|
|
1603
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
1604
|
+
|
|
1605
|
+
Although the algorithm for iteration in ``IntegerListsLex`` is
|
|
1606
|
+
efficient, its counting algorithm is naive: it just iterates over all
|
|
1607
|
+
the elements.
|
|
1608
|
+
|
|
1609
|
+
There is an alternative approach to treating this problem: modelling the
|
|
1610
|
+
desired lists of integers as the set of integer points of a polytope,
|
|
1611
|
+
that is to say, the set of solutions with integer coordinates of a
|
|
1612
|
+
system of linear inequalities. This is a very general context in which
|
|
1613
|
+
there exist advanced counting algorithms (e.g. Barvinok), which are
|
|
1614
|
+
implemented in libraries like ``LattE``. Iteration does not pose a hard problem
|
|
1615
|
+
in principle. However, there are two limitations that justify the
|
|
1616
|
+
existence of ``IntegerListsLex``. The first is theoretical: lattice
|
|
1617
|
+
points in a polytope only allow modelling of problems of a fixed
|
|
1618
|
+
dimension (length). The second is practical: at the moment only the
|
|
1619
|
+
library ``PALP`` has a ``Sage`` interface, and though it offers multiple
|
|
1620
|
+
capabilities for the study of polytopes, in the present application it
|
|
1621
|
+
only produces a list of lattice points, without providing either an
|
|
1622
|
+
iterator or non-naive counting::
|
|
1623
|
+
|
|
1624
|
+
sage: A = random_matrix(ZZ, 6, 3, x=7)
|
|
1625
|
+
sage: L = LatticePolytope(A.rows())
|
|
1626
|
+
sage: L.points() # random # needs palp
|
|
1627
|
+
M(4, 1, 0),
|
|
1628
|
+
M(0, 3, 5),
|
|
1629
|
+
M(2, 2, 3),
|
|
1630
|
+
M(6, 1, 3),
|
|
1631
|
+
M(1, 3, 6),
|
|
1632
|
+
M(6, 2, 3),
|
|
1633
|
+
M(3, 2, 4),
|
|
1634
|
+
M(3, 2, 3),
|
|
1635
|
+
M(4, 2, 4),
|
|
1636
|
+
M(4, 2, 3),
|
|
1637
|
+
M(5, 2, 3)
|
|
1638
|
+
in 3-d lattice M
|
|
1639
|
+
sage: L.npoints() # random # needs palp
|
|
1640
|
+
11
|
|
1641
|
+
|
|
1642
|
+
This polytope can be visualized in 3D with ``L.plot3d()`` (see
|
|
1643
|
+
:ref:`figure-polytope`).
|
|
1644
|
+
|
|
1645
|
+
.. _figure-polytope:
|
|
1646
|
+
|
|
1647
|
+
.. figure:: ../../media/polytope.png
|
|
1648
|
+
:scale: 75%
|
|
1649
|
+
|
|
1650
|
+
Figure: The polytope `L` and its integer points, in cross-eyed stereographic perspective.
|
|
1651
|
+
|
|
1652
|
+
.. _section-generic-species:
|
|
1653
|
+
|
|
1654
|
+
Species, decomposable combinatorial classes
|
|
1655
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
1656
|
+
|
|
1657
|
+
In :ref:`section-examples-catalan`, we showed how to use the recursive
|
|
1658
|
+
definition of binary trees to count them efficiently using generating
|
|
1659
|
+
functions. The techniques we used there are very general, and apply
|
|
1660
|
+
whenever the sets involved can be defined recursively (depending on
|
|
1661
|
+
who you ask, such a set is called a *decomposable combinatorial class*
|
|
1662
|
+
or, roughly speaking, a *combinatorial species*). This includes all
|
|
1663
|
+
the types of trees, but also permutations, compositions, functional
|
|
1664
|
+
graphs, etc.
|
|
1665
|
+
|
|
1666
|
+
Here, we illustrate just a few examples using the ``Sage`` library on
|
|
1667
|
+
combinatorial species::
|
|
1668
|
+
|
|
1669
|
+
sage: from sage.combinat.species.library import *
|
|
1670
|
+
sage: o = var('o') # needs sage.symbolic
|
|
1671
|
+
|
|
1672
|
+
We begin by redefining the complete binary trees; to do so, we stipulate
|
|
1673
|
+
the recurrence relation directly on the sets::
|
|
1674
|
+
|
|
1675
|
+
sage: BT = CombinatorialSpecies(min=1)
|
|
1676
|
+
sage: Leaf = SingletonSpecies()
|
|
1677
|
+
sage: BT.define( Leaf + (BT*BT) )
|
|
1678
|
+
|
|
1679
|
+
Now we can construct the set of trees with five nodes, list them, count
|
|
1680
|
+
them...::
|
|
1681
|
+
|
|
1682
|
+
sage: BT5 = BT.isotypes([o]*5) # needs sage.symbolic
|
|
1683
|
+
sage: BT5.cardinality() # needs sage.symbolic
|
|
1684
|
+
14
|
|
1685
|
+
sage: BT5.list() # needs sage.symbolic
|
|
1686
|
+
[o*(o*(o*(o*o))), o*(o*((o*o)*o)), o*((o*o)*(o*o)),
|
|
1687
|
+
o*((o*(o*o))*o), o*(((o*o)*o)*o), (o*o)*(o*(o*o)),
|
|
1688
|
+
(o*o)*((o*o)*o), (o*(o*o))*(o*o), ((o*o)*o)*(o*o),
|
|
1689
|
+
(o*(o*(o*o)))*o, (o*((o*o)*o))*o, ((o*o)*(o*o))*o,
|
|
1690
|
+
((o*(o*o))*o)*o, (((o*o)*o)*o)*o]
|
|
1691
|
+
|
|
1692
|
+
The trees are constructed using a generic recursive structure; the
|
|
1693
|
+
display is therefore not wonderful. To do better, it would be necessary
|
|
1694
|
+
to provide ``Sage`` with a more specialized data structure with the
|
|
1695
|
+
desired display capabilities.
|
|
1696
|
+
|
|
1697
|
+
We recover the generating function for the Catalan numbers::
|
|
1698
|
+
|
|
1699
|
+
sage: g = BT.isotype_generating_series(); g
|
|
1700
|
+
z + z^2 + 2*z^3 + 5*z^4 + 14*z^5 + 42*z^6 + 132*z^7 + O(z^8)
|
|
1701
|
+
|
|
1702
|
+
which is returned in the form of a lazy power series::
|
|
1703
|
+
|
|
1704
|
+
sage: g[100]
|
|
1705
|
+
227508830794229349661819540395688853956041682601541047340
|
|
1706
|
+
|
|
1707
|
+
We finish with the Fibonacci words, which are binary words without two
|
|
1708
|
+
consecutive “`1`”s. They admit a natural recursive definition::
|
|
1709
|
+
|
|
1710
|
+
sage: Eps = EmptySetSpecies()
|
|
1711
|
+
sage: Z0 = SingletonSpecies()
|
|
1712
|
+
sage: Z1 = Eps*SingletonSpecies()
|
|
1713
|
+
sage: FW = CombinatorialSpecies()
|
|
1714
|
+
sage: FW.define(Eps + Z0*FW + Z1*Eps + Z1*Z0*FW)
|
|
1715
|
+
|
|
1716
|
+
The Fibonacci sequence is easily recognized here, hence the name::
|
|
1717
|
+
|
|
1718
|
+
sage: L = FW.isotype_generating_series()[:15]; L
|
|
1719
|
+
[1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]
|
|
1720
|
+
|
|
1721
|
+
::
|
|
1722
|
+
|
|
1723
|
+
sage: oeis(L) # optional -- internet
|
|
1724
|
+
0: A000045: Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1.
|
|
1725
|
+
1: ...
|
|
1726
|
+
2: ...
|
|
1727
|
+
|
|
1728
|
+
This is an immediate consequence of the recurrence relation. One can
|
|
1729
|
+
also generate immediately all the Fibonacci words of a given length,
|
|
1730
|
+
with the same limitations resulting from the generic display.
|
|
1731
|
+
|
|
1732
|
+
::
|
|
1733
|
+
|
|
1734
|
+
sage: FW3 = FW.isotypes([o]*3) # needs sage.symbolic
|
|
1735
|
+
sage: FW3.list() # needs sage.symbolic
|
|
1736
|
+
[o*(o*(o*{})), o*(o*(({}*o)*{})), o*((({}*o)*o)*{}),
|
|
1737
|
+
(({}*o)*o)*(o*{}), (({}*o)*o)*(({}*o)*{})]
|
|
1738
|
+
|
|
1739
|
+
.. _section-generic-isomorphism:
|
|
1740
|
+
|
|
1741
|
+
Graphs up to isomorphism
|
|
1742
|
+
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
1743
|
+
|
|
1744
|
+
We saw in :ref:`section-bricks-divers` that ``Sage`` could generate
|
|
1745
|
+
graphs and partial orders up to isomorphism. We will now describe the
|
|
1746
|
+
underlying algorithm, which is the same in both cases, and covers a
|
|
1747
|
+
substantially wider class of problems.
|
|
1748
|
+
|
|
1749
|
+
We begin by recalling some notions. A graph `G=(V,E)` is a set
|
|
1750
|
+
`V` of vertices and a set `E` of edges connecting these
|
|
1751
|
+
vertices; an edge is described by a pair `\{u,v\}` of distinct
|
|
1752
|
+
vertices of `V`. Such a graph is called labelled; its vertices
|
|
1753
|
+
are typically numbered by considering `V=\{1,2,3,4,5\}`.
|
|
1754
|
+
|
|
1755
|
+
In many problems, the labels on the vertices play no role. Typically a
|
|
1756
|
+
chemist wants to study all the possible molecules with a given
|
|
1757
|
+
composition, for example the alkanes with `n=8` atoms of carbon
|
|
1758
|
+
and `2n+2=18` atoms of hydrogen. He therefore wants to find all
|
|
1759
|
+
the graphs consisting of `8` vertices with `4` neighbours, and
|
|
1760
|
+
`18` vertices with a single neighbour. The different carbon atoms,
|
|
1761
|
+
however, are all considered to be identical, and the same for
|
|
1762
|
+
the hydrogen atoms. The problem of our chemist is not imaginary; this
|
|
1763
|
+
type of application is actually at the origin of an important part of
|
|
1764
|
+
the research in graph theory on isomorphism problems.
|
|
1765
|
+
|
|
1766
|
+
Working by hand on a small graph it is possible, as in the example of
|
|
1767
|
+
:ref:`section-bricks-divers`, to make a drawing, erase the labels, and
|
|
1768
|
+
“forget” the geometrical information about the location of the
|
|
1769
|
+
vertices in the plane. However, to represent a graph in a computer
|
|
1770
|
+
program, it is necessary to introduce labels on the vertices so as to
|
|
1771
|
+
be able to describe how the edges connect them together. To compensate
|
|
1772
|
+
for the extra information which we have introduced, we then say that
|
|
1773
|
+
two labelled graphs `g_1` and `g_2` are *isomorphic* if there is a
|
|
1774
|
+
bijection from the vertices of `g_1` to those of `g_2`, which maps
|
|
1775
|
+
bijectively the edges of `g_1` to those of `g_2`; an *unlabelled
|
|
1776
|
+
graph* is then an equivalence class of labelled graphs.
|
|
1777
|
+
|
|
1778
|
+
In general, testing if two labelled graphs are isomorphic is expensive.
|
|
1779
|
+
However, the number of graphs, even unlabelled, grows very
|
|
1780
|
+
rapidly. Nonetheless, it is possible to list unlabelled graphs very efficiently
|
|
1781
|
+
considering their number. For example, the program ``Nauty`` can list the
|
|
1782
|
+
`12005168` simple graphs with `10` vertices in
|
|
1783
|
+
`20` seconds.
|
|
1784
|
+
|
|
1785
|
+
As in :ref:`section-generic-integerlistlex`, the general principle
|
|
1786
|
+
of the algorithm is to organize the objects to be enumerated into a tree
|
|
1787
|
+
that one traverses.
|
|
1788
|
+
|
|
1789
|
+
For this, in each equivalence class of labelled graphs (that is to say,
|
|
1790
|
+
for each unlabelled graph) one fixes a convenient canonical
|
|
1791
|
+
representative. The following are the fundamental operations:
|
|
1792
|
+
|
|
1793
|
+
* Testing whether a labelled graph is canonical
|
|
1794
|
+
|
|
1795
|
+
* Calculating the canonical representative of a labelled graph
|
|
1796
|
+
|
|
1797
|
+
These unavoidable operations remain expensive; one therefore tries to
|
|
1798
|
+
minimize the number of calls to them.
|
|
1799
|
+
|
|
1800
|
+
The canonical representatives are chosen in such a way that, for each
|
|
1801
|
+
canonical labelled graph `G`, there is a canonical choice of an edge
|
|
1802
|
+
whose removal produces a canonical graph again, which is called the
|
|
1803
|
+
father of `G`. This property implies that it is possible to organize
|
|
1804
|
+
the set of canonical representatives as a tree: at the root, the graph
|
|
1805
|
+
with no edges; below it, its unique child, the graph with one edge;
|
|
1806
|
+
then the graphs with two edges, and so on. The set of children of a
|
|
1807
|
+
graph `G` can be constructed by *augmentation*, adding an edge in all
|
|
1808
|
+
the possible ways to `G`, and then selecting, from among those graphs,
|
|
1809
|
+
the ones that are still canonical [2]_. Recursively, one obtains all
|
|
1810
|
+
the canonical graphs.
|
|
1811
|
+
|
|
1812
|
+
.. figure:: ../../media/prefix-tree-graphs-4.png
|
|
1813
|
+
|
|
1814
|
+
Figure: The generation tree of simple graphs with `4` vertices.
|
|
1815
|
+
|
|
1816
|
+
In what sense is this algorithm generic? Consider for example planar
|
|
1817
|
+
graphs (graphs which can be drawn in the plane without edges crossing):
|
|
1818
|
+
by removing an edge from a planar graph, one obtains another planar
|
|
1819
|
+
graph; so planar graphs form a subtree of the previous tree. To generate
|
|
1820
|
+
them, exactly the same algorithm can be used,
|
|
1821
|
+
selecting only the children which are planar::
|
|
1822
|
+
|
|
1823
|
+
sage: [len(list(graphs(n, property=lambda G: G.is_planar()))) # needs planarity
|
|
1824
|
+
....: for n in range(7)]
|
|
1825
|
+
[1, 1, 2, 4, 11, 33, 142]
|
|
1826
|
+
|
|
1827
|
+
In a similar fashion, one can generate any family of graphs closed
|
|
1828
|
+
under deletion of an edge, and in particular any family characterized
|
|
1829
|
+
by a forbidden subgraph. This includes for example forests (graphs
|
|
1830
|
+
without cycles), bipartite graphs (graphs without odd cycles),
|
|
1831
|
+
etc. This can be applied to generate:
|
|
1832
|
+
|
|
1833
|
+
- partial orders, via the bijection with Hasse diagrams which are
|
|
1834
|
+
oriented graphs without cycles and without edges implied by the
|
|
1835
|
+
transitivity of the order relation;
|
|
1836
|
+
|
|
1837
|
+
- lattices (not implemented in ``Sage``), via the bijection with the
|
|
1838
|
+
meet semi-lattice obtained by deleting the maximal vertex; in this
|
|
1839
|
+
case an augmentation by vertices rather than by edges is used.
|
|
1840
|
+
|
|
1841
|
+
REFERENCES:
|
|
1842
|
+
|
|
1843
|
+
.. [CMS2012] Alexandre Casamayou, Nathann Cohen, Guillaume Connan, Thierry
|
|
1844
|
+
Dumont, Laurent Fousse, François Maltey, Matthias Meulien, Marc Mezzarobba,
|
|
1845
|
+
Clément Pernet, Nicolas M. Thiéry, Paul Zimmermann *Calcul Mathématique avec
|
|
1846
|
+
Sage* https://www.sagemath.org/sagebook/french.html
|
|
1847
|
+
|
|
1848
|
+
.. [1]
|
|
1849
|
+
Or at least that should be the case; there are still many corners to
|
|
1850
|
+
clean up.
|
|
1851
|
+
|
|
1852
|
+
.. [2]
|
|
1853
|
+
In practice, an efficient implementation would exploit the symmetries
|
|
1854
|
+
of `G`, i.e., its automorphism group, to reduce the number of
|
|
1855
|
+
children to explore, and to reduce the cost of each test of
|
|
1856
|
+
canonicity.
|
|
1857
|
+
"""
|