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,2026 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-combinat
|
|
2
|
+
r"""
|
|
3
|
+
Common words
|
|
4
|
+
|
|
5
|
+
AUTHORS:
|
|
6
|
+
|
|
7
|
+
- Franco Saliola (2008-12-17): merged into sage
|
|
8
|
+
- Sébastien Labbé (2008-12-17): merged into sage
|
|
9
|
+
- Arnaud Bergeron (2008-12-17): merged into sage
|
|
10
|
+
- Amy Glen (2008-12-17): merged into sage
|
|
11
|
+
- Sébastien Labbé (2009-12-19): Added S-adic words (:issue:`7543`)
|
|
12
|
+
|
|
13
|
+
USE:
|
|
14
|
+
|
|
15
|
+
To see a list of all word constructors, type ``words.`` and then press
|
|
16
|
+
the :kbd:`Tab` key. The documentation for each constructor includes
|
|
17
|
+
information about each word, which provides a useful reference.
|
|
18
|
+
|
|
19
|
+
REFERENCES:
|
|
20
|
+
|
|
21
|
+
.. [AC03] \B. Adamczewski, J. Cassaigne, On the transcendence of real
|
|
22
|
+
numbers with a regular expansion, J. Number Theory 103 (2003)
|
|
23
|
+
27--37.
|
|
24
|
+
|
|
25
|
+
.. [BmBGL07] \A. Blondin-Massé, S. Brlek, A. Glen, and S. Labbé. On the
|
|
26
|
+
critical exponent of generalized Thue-Morse words. *Discrete Math.
|
|
27
|
+
Theor. Comput. Sci.* 9 (1):293--304, 2007.
|
|
28
|
+
|
|
29
|
+
.. [BmBGL09] \A. Blondin-Massé, S. Brlek, A. Garon, and S. Labbé. Christoffel
|
|
30
|
+
and Fibonacci Tiles, DGCI 2009, Montreal, to appear in LNCS.
|
|
31
|
+
|
|
32
|
+
.. [Loth02] \M. Lothaire, Algebraic Combinatorics On Words, vol. 90 of
|
|
33
|
+
Encyclopedia of Mathematics and its Applications, Cambridge
|
|
34
|
+
University Press, U.K., 2002.
|
|
35
|
+
|
|
36
|
+
.. [Fogg] Pytheas Fogg,
|
|
37
|
+
https://www.lirmm.fr/arith/wiki/PytheasFogg/S-adiques.
|
|
38
|
+
|
|
39
|
+
EXAMPLES::
|
|
40
|
+
|
|
41
|
+
sage: t = words.ThueMorseWord(); t
|
|
42
|
+
word: 0110100110010110100101100110100110010110...
|
|
43
|
+
"""
|
|
44
|
+
# ****************************************************************************
|
|
45
|
+
# Copyright (C) 2008 Franco Saliola <saliola@gmail.com>,
|
|
46
|
+
# Sebastien Labbe <slabqc@gmail.com>,
|
|
47
|
+
# Arnaud Bergeron <abergeron@gmail.com>,
|
|
48
|
+
# Amy Glen <amy.glen@gmail.com>
|
|
49
|
+
#
|
|
50
|
+
# This program is free software: you can redistribute it and/or modify
|
|
51
|
+
# it under the terms of the GNU General Public License as published by
|
|
52
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
53
|
+
# (at your option) any later version.
|
|
54
|
+
# https://www.gnu.org/licenses/
|
|
55
|
+
# ****************************************************************************
|
|
56
|
+
|
|
57
|
+
from collections.abc import Iterable
|
|
58
|
+
from itertools import cycle, count
|
|
59
|
+
from random import randint
|
|
60
|
+
from sage.misc.cachefunc import cached_method
|
|
61
|
+
from sage.rings.integer_ring import ZZ
|
|
62
|
+
from sage.rings.infinity import Infinity
|
|
63
|
+
from sage.combinat.words.abstract_word import Word_class
|
|
64
|
+
from sage.combinat.words.word import FiniteWord_list
|
|
65
|
+
from sage.combinat.words.finite_word import FiniteWord_class, Factorization
|
|
66
|
+
from sage.combinat.words.words import FiniteWords, InfiniteWords
|
|
67
|
+
from sage.combinat.words.morphism import WordMorphism
|
|
68
|
+
from sage.arith.misc import gcd
|
|
69
|
+
from sage.misc.decorators import rename_keyword
|
|
70
|
+
from sage.misc.lazy_import import lazy_import
|
|
71
|
+
|
|
72
|
+
lazy_import('sage.rings.real_mpfr', 'RR')
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _build_tab(sym, tab, W):
|
|
76
|
+
r"""
|
|
77
|
+
Internal function building a coding table for the ``phi_inv_tab`` function.
|
|
78
|
+
|
|
79
|
+
TESTS::
|
|
80
|
+
|
|
81
|
+
sage: from sage.combinat.words.word_generators import _build_tab
|
|
82
|
+
sage: _build_tab(1, [], Words([1, 2]))
|
|
83
|
+
[1]
|
|
84
|
+
sage: _build_tab(1, [1], Words([1, 2]))
|
|
85
|
+
[1, 2]
|
|
86
|
+
sage: _build_tab(2, [1], Words([1, 2]))
|
|
87
|
+
[2, 2]
|
|
88
|
+
sage: _build_tab(2, [1, 2], Words([1, 2]))
|
|
89
|
+
[2, 2, 1]
|
|
90
|
+
sage: _build_tab(1, [2, 2], Words([1, 2]))
|
|
91
|
+
[1, 1, 2]
|
|
92
|
+
"""
|
|
93
|
+
c = W.alphabet().cardinality()
|
|
94
|
+
res = [sym]
|
|
95
|
+
if len(tab) == 0:
|
|
96
|
+
return res
|
|
97
|
+
if sym == 1:
|
|
98
|
+
res += tab
|
|
99
|
+
res[1] = (res[1] % c) + 1
|
|
100
|
+
return res
|
|
101
|
+
w = W([sym]).delta_inv(W, tab[0])
|
|
102
|
+
w = w[1:]
|
|
103
|
+
res.append((w[-1] % c) + 1)
|
|
104
|
+
for i in range(1, len(tab)):
|
|
105
|
+
w = w.delta_inv(W, tab[i])
|
|
106
|
+
res.append((w[-1] % c) + 1)
|
|
107
|
+
return res
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class LowerChristoffelWord(FiniteWord_list):
|
|
111
|
+
r"""
|
|
112
|
+
Return the lower Christoffel word of slope `p/q`, where `p` and
|
|
113
|
+
`q` are relatively prime nonnegative integers, over the given
|
|
114
|
+
two-letter alphabet.
|
|
115
|
+
|
|
116
|
+
The *Christoffel word of slope `p/q`* is obtained from the
|
|
117
|
+
Cayley graph of `\ZZ/(p+q)\ZZ` with generator `q` as
|
|
118
|
+
follows. If `u \rightarrow v` is an edge in the Cayley graph, then
|
|
119
|
+
`v = u + p \mod{p+q}`. Label the edge `u \rightarrow v` by
|
|
120
|
+
``alphabet[1]`` if `u < v` and ``alphabet[0]`` otherwise. The Christoffel
|
|
121
|
+
word is the word obtained by reading the edge labels along the
|
|
122
|
+
cycle beginning from 0.
|
|
123
|
+
|
|
124
|
+
EXAMPLES::
|
|
125
|
+
|
|
126
|
+
sage: words.LowerChristoffelWord(4,7)
|
|
127
|
+
word: 00100100101
|
|
128
|
+
|
|
129
|
+
::
|
|
130
|
+
|
|
131
|
+
sage: words.LowerChristoffelWord(4,7,alphabet='ab')
|
|
132
|
+
word: aabaabaabab
|
|
133
|
+
|
|
134
|
+
TESTS::
|
|
135
|
+
|
|
136
|
+
sage: words.LowerChristoffelWord(1,0)
|
|
137
|
+
word: 1
|
|
138
|
+
sage: words.LowerChristoffelWord(0,1,'xy')
|
|
139
|
+
word: x
|
|
140
|
+
sage: words.LowerChristoffelWord(1,1)
|
|
141
|
+
word: 01
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
def __init__(self, p, q, alphabet=(0, 1), algorithm='cf'):
|
|
145
|
+
r"""
|
|
146
|
+
INPUT:
|
|
147
|
+
|
|
148
|
+
- ``p`` -- integer coprime with `q`
|
|
149
|
+
- ``q`` -- integer coprime with `p`
|
|
150
|
+
- ``alphabet`` -- sequence of two elements (default: (0, 1))
|
|
151
|
+
- ``algorithm`` -- construction method (default: ``'cf'``).
|
|
152
|
+
It can be one of the following:
|
|
153
|
+
|
|
154
|
+
- ``'linear'`` -- linear algorithm in the length of the word
|
|
155
|
+
- ``'cf'`` -- fast method using continued fraction
|
|
156
|
+
|
|
157
|
+
TESTS::
|
|
158
|
+
|
|
159
|
+
sage: words.ChristoffelWord(9, 4, algorithm='linear')
|
|
160
|
+
word: 0110110110111
|
|
161
|
+
sage: words.ChristoffelWord(9, 4, algorithm='cf')
|
|
162
|
+
word: 0110110110111
|
|
163
|
+
sage: words.ChristoffelWord(4, 9, algorithm='linear')
|
|
164
|
+
word: 0001001001001
|
|
165
|
+
sage: words.ChristoffelWord(4, 9, algorithm='cf')
|
|
166
|
+
word: 0001001001001
|
|
167
|
+
|
|
168
|
+
::
|
|
169
|
+
|
|
170
|
+
sage: words.LowerChristoffelWord(4,8)
|
|
171
|
+
Traceback (most recent call last):
|
|
172
|
+
...
|
|
173
|
+
ValueError: 4 and 8 are not relatively prime
|
|
174
|
+
sage: words.LowerChristoffelWord(17, 39, 'xyz')
|
|
175
|
+
Traceback (most recent call last):
|
|
176
|
+
...
|
|
177
|
+
ValueError: alphabet must contain exactly two distinct elements
|
|
178
|
+
sage: w = words.LowerChristoffelWord(4,7)
|
|
179
|
+
sage: w2 = loads(dumps(w))
|
|
180
|
+
sage: w == w2
|
|
181
|
+
True
|
|
182
|
+
sage: type(w2)
|
|
183
|
+
<class 'sage.combinat.words.word_generators.LowerChristoffelWord'>
|
|
184
|
+
sage: _ = w2.standard_factorization() # hackish test for self.__p and self.__q
|
|
185
|
+
"""
|
|
186
|
+
if len(set(alphabet)) != 2:
|
|
187
|
+
raise ValueError("alphabet must contain exactly two distinct elements")
|
|
188
|
+
# Compute gcd of p, q; raise TypeError if not 1.
|
|
189
|
+
if gcd(p, q) != 1:
|
|
190
|
+
raise ValueError("%s and %s are not relatively prime" % (p, q))
|
|
191
|
+
# Compute the Christoffel word
|
|
192
|
+
if algorithm == 'linear':
|
|
193
|
+
w = []
|
|
194
|
+
u = 0
|
|
195
|
+
if (p, q) == (0, 1):
|
|
196
|
+
w = [alphabet[0]]
|
|
197
|
+
else:
|
|
198
|
+
for i in range(p + q):
|
|
199
|
+
v = (u + p) % (p + q)
|
|
200
|
+
new_letter = alphabet[0] if u < v else alphabet[1]
|
|
201
|
+
w.append(new_letter)
|
|
202
|
+
u = v
|
|
203
|
+
elif algorithm == 'cf':
|
|
204
|
+
if (p, q) == (0, 1):
|
|
205
|
+
w = [alphabet[0]]
|
|
206
|
+
elif (p, q) == (1, 0):
|
|
207
|
+
w = [alphabet[1]]
|
|
208
|
+
else:
|
|
209
|
+
from sage.rings.rational_field import QQ
|
|
210
|
+
cf = QQ((p, q)).continued_fraction_list()
|
|
211
|
+
u = [alphabet[0]]
|
|
212
|
+
v = [alphabet[1]]
|
|
213
|
+
# do not consider the first zero if p < q
|
|
214
|
+
start = 1 if p < q else 0
|
|
215
|
+
for i in range(start, len(cf)-1):
|
|
216
|
+
if i % 2 == 0:
|
|
217
|
+
u = u + v * cf[i]
|
|
218
|
+
else:
|
|
219
|
+
v = u * cf[i] + v
|
|
220
|
+
i = len(cf)-1
|
|
221
|
+
if i % 2 == 0:
|
|
222
|
+
u = u + v * (cf[i]-1)
|
|
223
|
+
else:
|
|
224
|
+
v = u * (cf[i]-1) + v
|
|
225
|
+
w = u + v
|
|
226
|
+
else:
|
|
227
|
+
raise ValueError('Unknown algorithm (=%s)' % algorithm)
|
|
228
|
+
super().__init__(FiniteWords(alphabet), w)
|
|
229
|
+
self.__p = p
|
|
230
|
+
self.__q = q
|
|
231
|
+
|
|
232
|
+
def markoff_number(self):
|
|
233
|
+
r"""
|
|
234
|
+
Return the Markoff number associated to the Christoffel word ``self``.
|
|
235
|
+
|
|
236
|
+
The *Markoff number* of a Christoffel word `w` is `trace(M(w))/3`,
|
|
237
|
+
where `M(w)` is the `2\times 2` matrix obtained by applying the
|
|
238
|
+
morphism:
|
|
239
|
+
0 -> matrix(2,[2,1,1,1])
|
|
240
|
+
1 -> matrix(2,[5,2,2,1])
|
|
241
|
+
|
|
242
|
+
EXAMPLES::
|
|
243
|
+
|
|
244
|
+
sage: w0 = words.LowerChristoffelWord(4,7)
|
|
245
|
+
sage: w1, w2 = w0.standard_factorization()
|
|
246
|
+
sage: (m0,m1,m2) = (w.markoff_number() for w in (w0,w1,w2)) # needs sage.modules
|
|
247
|
+
sage: (m0,m1,m2) # needs sage.modules
|
|
248
|
+
(294685, 13, 7561)
|
|
249
|
+
sage: m0**2 + m1**2 + m2**2 == 3*m0*m1*m2 # needs sage.modules
|
|
250
|
+
True
|
|
251
|
+
"""
|
|
252
|
+
from sage.matrix.constructor import matrix
|
|
253
|
+
eta = {0:matrix(2,[2,1,1,1]), 1:matrix(2,[5,2,2,1])}
|
|
254
|
+
M = matrix(2,[1,0,0,1])
|
|
255
|
+
for a in self:
|
|
256
|
+
M *= eta[a]
|
|
257
|
+
return M.trace()/3
|
|
258
|
+
|
|
259
|
+
def standard_factorization(self):
|
|
260
|
+
r"""
|
|
261
|
+
Return the standard factorization of the Christoffel word ``self``.
|
|
262
|
+
|
|
263
|
+
The *standard factorization* of a Christoffel word `w` is the
|
|
264
|
+
unique factorization of `w` into two Christoffel words.
|
|
265
|
+
|
|
266
|
+
EXAMPLES::
|
|
267
|
+
|
|
268
|
+
sage: w = words.LowerChristoffelWord(5,9)
|
|
269
|
+
sage: w
|
|
270
|
+
word: 00100100100101
|
|
271
|
+
sage: w1, w2 = w.standard_factorization()
|
|
272
|
+
sage: w1
|
|
273
|
+
word: 001
|
|
274
|
+
sage: w2
|
|
275
|
+
word: 00100100101
|
|
276
|
+
|
|
277
|
+
::
|
|
278
|
+
|
|
279
|
+
sage: w = words.LowerChristoffelWord(51,37)
|
|
280
|
+
sage: w1, w2 = w.standard_factorization()
|
|
281
|
+
sage: w1
|
|
282
|
+
word: 0101011010101101011
|
|
283
|
+
sage: w2
|
|
284
|
+
word: 0101011010101101011010101101010110101101...
|
|
285
|
+
sage: w1 * w2 == w
|
|
286
|
+
True
|
|
287
|
+
"""
|
|
288
|
+
p, q = self.__p, self.__q
|
|
289
|
+
index = 0
|
|
290
|
+
u = 0
|
|
291
|
+
for i in range(p + q):
|
|
292
|
+
v = (u+p) % (p+q)
|
|
293
|
+
if v == 1:
|
|
294
|
+
index = i
|
|
295
|
+
break
|
|
296
|
+
u = v
|
|
297
|
+
w1, w2 = self[:index+1], self[index+1:]
|
|
298
|
+
w10 = w1.number_of_letter_occurrences(0)
|
|
299
|
+
w11 = w1.number_of_letter_occurrences(1)
|
|
300
|
+
w20 = w2.number_of_letter_occurrences(0)
|
|
301
|
+
w21 = w2.number_of_letter_occurrences(1)
|
|
302
|
+
return Factorization([LowerChristoffelWord(w11,w10),
|
|
303
|
+
LowerChristoffelWord(w21,w20)])
|
|
304
|
+
|
|
305
|
+
def __reduce__(self):
|
|
306
|
+
r"""
|
|
307
|
+
EXAMPLES::
|
|
308
|
+
|
|
309
|
+
sage: from sage.combinat.words.word_generators import LowerChristoffelWord
|
|
310
|
+
sage: w = LowerChristoffelWord(5,7)
|
|
311
|
+
sage: w.__reduce__()
|
|
312
|
+
(<class 'sage.combinat.words.word_generators.LowerChristoffelWord'>, (5, 7, {0, 1}))
|
|
313
|
+
"""
|
|
314
|
+
return self.__class__, (self.__p, self.__q, self.parent().alphabet())
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
class WordGenerator:
|
|
318
|
+
r"""
|
|
319
|
+
Constructor of several famous words.
|
|
320
|
+
|
|
321
|
+
EXAMPLES::
|
|
322
|
+
|
|
323
|
+
sage: words.ThueMorseWord()
|
|
324
|
+
word: 0110100110010110100101100110100110010110...
|
|
325
|
+
|
|
326
|
+
::
|
|
327
|
+
|
|
328
|
+
sage: words.FibonacciWord()
|
|
329
|
+
word: 0100101001001010010100100101001001010010...
|
|
330
|
+
|
|
331
|
+
::
|
|
332
|
+
|
|
333
|
+
sage: words.ChristoffelWord(5, 8)
|
|
334
|
+
word: 0010010100101
|
|
335
|
+
|
|
336
|
+
::
|
|
337
|
+
|
|
338
|
+
sage: words.RandomWord(10, 4) # not tested random
|
|
339
|
+
word: 1311131221
|
|
340
|
+
|
|
341
|
+
::
|
|
342
|
+
|
|
343
|
+
sage: words.CodingOfRotationWord(alpha=0.618, beta=0.618)
|
|
344
|
+
word: 1010110101101101011010110110101101101011...
|
|
345
|
+
|
|
346
|
+
::
|
|
347
|
+
|
|
348
|
+
sage: tm = WordMorphism('a->ab,b->ba')
|
|
349
|
+
sage: fib = WordMorphism('a->ab,b->a')
|
|
350
|
+
sage: tmword = words.ThueMorseWord([0, 1])
|
|
351
|
+
sage: from itertools import repeat
|
|
352
|
+
sage: words.s_adic(tmword, repeat('a'), {0:tm, 1:fib})
|
|
353
|
+
word: abbaababbaabbaabbaababbaababbaabbaababba...
|
|
354
|
+
|
|
355
|
+
.. NOTE::
|
|
356
|
+
|
|
357
|
+
To see a list of all word constructors, type ``words.`` and then
|
|
358
|
+
hit the :kbd:`Tab` key. The documentation for each constructor
|
|
359
|
+
includes information about each word, which provides a useful
|
|
360
|
+
reference.
|
|
361
|
+
|
|
362
|
+
TESTS::
|
|
363
|
+
|
|
364
|
+
sage: from sage.combinat.words.word_generators import WordGenerator
|
|
365
|
+
sage: words2 = WordGenerator()
|
|
366
|
+
sage: type(loads(dumps(words2)))
|
|
367
|
+
<class 'sage.combinat.words.word_generators.WordGenerator'>
|
|
368
|
+
"""
|
|
369
|
+
def ThueMorseWord(self, alphabet=(0, 1), base=2):
|
|
370
|
+
r"""
|
|
371
|
+
Return the (Generalized) Thue-Morse word over the given alphabet.
|
|
372
|
+
|
|
373
|
+
There are several ways to define the Thue-Morse word `t`.
|
|
374
|
+
We use the following definition: `t[n]` is the sum modulo `m` of
|
|
375
|
+
the digits in the given base expansion of `n`.
|
|
376
|
+
|
|
377
|
+
See [BmBGL07]_, [Brlek89]_, and [MH38]_.
|
|
378
|
+
|
|
379
|
+
INPUT:
|
|
380
|
+
|
|
381
|
+
- ``alphabet`` -- (default: ``(0, 1)``) any container that is suitable
|
|
382
|
+
to build an instance of OrderedAlphabet (``list``, ``tuple``, ``str``, ...)
|
|
383
|
+
|
|
384
|
+
- ``base`` -- integer (default: 2); greater than or equal to 2
|
|
385
|
+
|
|
386
|
+
EXAMPLES:
|
|
387
|
+
|
|
388
|
+
Thue-Morse word::
|
|
389
|
+
|
|
390
|
+
sage: t = words.ThueMorseWord(); t
|
|
391
|
+
word: 0110100110010110100101100110100110010110...
|
|
392
|
+
|
|
393
|
+
Thue-Morse word on other alphabets::
|
|
394
|
+
|
|
395
|
+
sage: t = words.ThueMorseWord('ab'); t
|
|
396
|
+
word: abbabaabbaababbabaababbaabbabaabbaababba...
|
|
397
|
+
|
|
398
|
+
::
|
|
399
|
+
|
|
400
|
+
sage: t = words.ThueMorseWord(['L1', 'L2'])
|
|
401
|
+
sage: t[:8]
|
|
402
|
+
word: L1,L2,L2,L1,L2,L1,L1,L2
|
|
403
|
+
|
|
404
|
+
Generalized Thue Morse word::
|
|
405
|
+
|
|
406
|
+
sage: words.ThueMorseWord(alphabet=(0,1,2), base=2)
|
|
407
|
+
word: 0112122012202001122020012001011212202001...
|
|
408
|
+
sage: t = words.ThueMorseWord(alphabet=(0,1,2), base=5); t
|
|
409
|
+
word: 0120112012201200120112012120122012001201...
|
|
410
|
+
sage: t[100:130].critical_exponent()
|
|
411
|
+
10/3
|
|
412
|
+
|
|
413
|
+
TESTS::
|
|
414
|
+
|
|
415
|
+
sage: words.ThueMorseWord(alphabet='ab', base=1)
|
|
416
|
+
Traceback (most recent call last):
|
|
417
|
+
...
|
|
418
|
+
ValueError: base (=1) and len(alphabet) (=2) must be at least 2
|
|
419
|
+
|
|
420
|
+
REFERENCES:
|
|
421
|
+
|
|
422
|
+
.. [Brlek89] Brlek, S. 1989. «Enumeration of the factors in the Thue-Morse
|
|
423
|
+
word», *Discrete Appl. Math.*, vol. 24, p. 83--96.
|
|
424
|
+
|
|
425
|
+
.. [MH38] Morse, M., et G. A. Hedlund. 1938. «Symbolic dynamics»,
|
|
426
|
+
*American Journal of Mathematics*, vol. 60, p. 815--866.
|
|
427
|
+
"""
|
|
428
|
+
W = InfiniteWords(alphabet)
|
|
429
|
+
alphabet = W.alphabet()
|
|
430
|
+
m = alphabet.cardinality()
|
|
431
|
+
if base < 2 or m < 2:
|
|
432
|
+
raise ValueError("base (=%s) and len(alphabet) (=%s) must be at least 2" % (base, m))
|
|
433
|
+
from functools import partial
|
|
434
|
+
f = partial(self._ThueMorseWord_nth_digit, alphabet=alphabet, base=base)
|
|
435
|
+
return W(f, datatype='callable')
|
|
436
|
+
|
|
437
|
+
def _ThueMorseWord_nth_digit(self, n, alphabet=(0, 1), base=2):
|
|
438
|
+
r"""
|
|
439
|
+
Return the `n`-th letter of the (Generalized) Thue-Morse word.
|
|
440
|
+
|
|
441
|
+
The `n`-th digit of the Thue-Morse word can be defined as the number
|
|
442
|
+
of bits in the 2-complement representation of the position
|
|
443
|
+
modulo 2 which is what this function uses. The running time
|
|
444
|
+
is `O(\log n)` where `n` is the position desired.
|
|
445
|
+
|
|
446
|
+
The `n`-th digit of the Generalized Thue Morse word can be defined as
|
|
447
|
+
the sum of the digits of `n` written in the given base mod `m`,
|
|
448
|
+
where `m` is the length of the given alphabet.
|
|
449
|
+
|
|
450
|
+
INPUT:
|
|
451
|
+
|
|
452
|
+
- ``n`` -- integer; the position
|
|
453
|
+
- ``alphabet`` -- an alphabet (default: (0, 1)) of size at least 2
|
|
454
|
+
- ``base`` -- integer (default: 2) greater than or equal to 2
|
|
455
|
+
|
|
456
|
+
OUTPUT:
|
|
457
|
+
|
|
458
|
+
0 or 1 -- the digit at the position
|
|
459
|
+
letter -- the letter of alphabet at the position
|
|
460
|
+
|
|
461
|
+
TESTS::
|
|
462
|
+
|
|
463
|
+
sage: from sage.combinat.words.word_generators import WordGenerator
|
|
464
|
+
sage: WordGenerator()._ThueMorseWord_nth_digit(0)
|
|
465
|
+
0
|
|
466
|
+
sage: WordGenerator()._ThueMorseWord_nth_digit(3)
|
|
467
|
+
0
|
|
468
|
+
sage: WordGenerator()._ThueMorseWord_nth_digit(32)
|
|
469
|
+
1
|
|
470
|
+
sage: WordGenerator()._ThueMorseWord_nth_digit(6, 'abc', base = 7)
|
|
471
|
+
'a'
|
|
472
|
+
|
|
473
|
+
Negative input::
|
|
474
|
+
|
|
475
|
+
sage: words._ThueMorseWord_nth_digit(-7)
|
|
476
|
+
Traceback (most recent call last):
|
|
477
|
+
...
|
|
478
|
+
NotImplementedError: nth digit of Thue-Morse word is not implemented for negative value of n
|
|
479
|
+
"""
|
|
480
|
+
if n < 0:
|
|
481
|
+
raise NotImplementedError("nth digit of Thue-Morse word is not implemented for negative value of n")
|
|
482
|
+
m = len(alphabet)
|
|
483
|
+
if base == 2 and m == 2:
|
|
484
|
+
for tn in count():
|
|
485
|
+
if n == 0:
|
|
486
|
+
return alphabet[tn & 1]
|
|
487
|
+
n &= n - 1
|
|
488
|
+
elif base < 2 or m < 2:
|
|
489
|
+
raise ValueError("base (=%s) and len(alphabet) (=%s) must be at least 2" % (base, m))
|
|
490
|
+
else:
|
|
491
|
+
return alphabet[ZZ(sum(ZZ(n).digits(base=base))).mod(m)]
|
|
492
|
+
|
|
493
|
+
def FibonacciWord(self, alphabet=(0, 1), construction_method='recursive'):
|
|
494
|
+
r"""
|
|
495
|
+
Return the Fibonacci word on the given two-letter alphabet.
|
|
496
|
+
|
|
497
|
+
INPUT:
|
|
498
|
+
|
|
499
|
+
- ``alphabet`` -- any container of length two that is suitable to
|
|
500
|
+
build an instance of OrderedAlphabet (``list``, ``tuple``, ``str``, ...)
|
|
501
|
+
|
|
502
|
+
- ``construction_method`` -- can be any of the following:
|
|
503
|
+
"recursive", "fixed point", "function" (see below for definitions)
|
|
504
|
+
|
|
505
|
+
Recursive construction: the Fibonacci word is the limit of the
|
|
506
|
+
following sequence of words: `S_0 = 0`, `S_1 = 01`,
|
|
507
|
+
`S_n = S_{n-1} S_{n-2}` for `n \geq 2`.
|
|
508
|
+
|
|
509
|
+
Fixed point construction: the Fibonacci word is the fixed point of the
|
|
510
|
+
morphism: `0 \mapsto 01` and `1 \mapsto 0`. Hence, it can be constructed
|
|
511
|
+
by the following read-write process:
|
|
512
|
+
|
|
513
|
+
#. beginning at the first letter of `01`,
|
|
514
|
+
#. if the next letter is `0`, append `01` to the word;
|
|
515
|
+
#. if the next letter is `1`, append `1` to the word;
|
|
516
|
+
#. move to the next letter of the word.
|
|
517
|
+
|
|
518
|
+
Function: Over the alphabet `\{1, 2\}`, the `n`-th letter of the
|
|
519
|
+
Fibonacci word is
|
|
520
|
+
`\lfloor (n+2) \varphi \rfloor - \lfloor (n+1) \varphi \rfloor`
|
|
521
|
+
where `\varphi=(1+\sqrt{5})/2` is the golden ratio.
|
|
522
|
+
|
|
523
|
+
EXAMPLES::
|
|
524
|
+
|
|
525
|
+
sage: w = words.FibonacciWord(construction_method='recursive'); w
|
|
526
|
+
word: 0100101001001010010100100101001001010010...
|
|
527
|
+
|
|
528
|
+
::
|
|
529
|
+
|
|
530
|
+
sage: v = words.FibonacciWord(construction_method='recursive', alphabet='ab'); v
|
|
531
|
+
word: abaababaabaababaababaabaababaabaababaaba...
|
|
532
|
+
|
|
533
|
+
::
|
|
534
|
+
|
|
535
|
+
sage: u = words.FibonacciWord(construction_method="fixed point"); u
|
|
536
|
+
word: 0100101001001010010100100101001001010010...
|
|
537
|
+
|
|
538
|
+
::
|
|
539
|
+
|
|
540
|
+
sage: words.FibonacciWord(construction_method="fixed point", alphabet=[4, 1])
|
|
541
|
+
word: 4144141441441414414144144141441441414414...
|
|
542
|
+
|
|
543
|
+
::
|
|
544
|
+
|
|
545
|
+
sage: words.FibonacciWord([0,1], 'function') # needs sage.symbolic
|
|
546
|
+
word: 0100101001001010010100100101001001010010...
|
|
547
|
+
sage: words.FibonacciWord('ab', 'function') # needs sage.symbolic
|
|
548
|
+
word: abaababaabaababaababaabaababaabaababaaba...
|
|
549
|
+
|
|
550
|
+
TESTS::
|
|
551
|
+
|
|
552
|
+
sage: from math import floor, sqrt
|
|
553
|
+
sage: golden_ratio = (1 + sqrt(5))/2.0
|
|
554
|
+
sage: a = golden_ratio / (1 + 2*golden_ratio)
|
|
555
|
+
sage: wn = lambda n : int(floor(a*(n+2)) - floor(a*(n+1)))
|
|
556
|
+
sage: f = Words([0,1])(wn); f
|
|
557
|
+
word: 0100101001001010010100100101001001010010...
|
|
558
|
+
sage: f[:10000] == w[:10000]
|
|
559
|
+
True
|
|
560
|
+
sage: f[:10000] == u[:10000] #long time
|
|
561
|
+
True
|
|
562
|
+
sage: words.FibonacciWord("abc")
|
|
563
|
+
Traceback (most recent call last):
|
|
564
|
+
...
|
|
565
|
+
TypeError: alphabet does not contain two distinct elements
|
|
566
|
+
"""
|
|
567
|
+
W = InfiniteWords(alphabet)
|
|
568
|
+
alphabet = W.alphabet()
|
|
569
|
+
if alphabet.cardinality() != 2:
|
|
570
|
+
raise TypeError("alphabet does not contain two distinct elements")
|
|
571
|
+
a,b = alphabet
|
|
572
|
+
|
|
573
|
+
if construction_method == "recursive":
|
|
574
|
+
w = W(self._FibonacciWord_RecursiveConstructionIterator(alphabet),
|
|
575
|
+
datatype='iter')
|
|
576
|
+
return w
|
|
577
|
+
|
|
578
|
+
elif construction_method in ("fixed point", "fixed_point"):
|
|
579
|
+
d = {b:[a],a:[a,b]}
|
|
580
|
+
w = self.FixedPointOfMorphism(d, a)
|
|
581
|
+
return w
|
|
582
|
+
|
|
583
|
+
elif construction_method == "function":
|
|
584
|
+
from sage.functions.other import floor
|
|
585
|
+
from sage.misc.functional import sqrt
|
|
586
|
+
phi = (1 + sqrt(5))/2 # the golden ratio
|
|
587
|
+
f = lambda n:a if floor((n+2)*phi) - floor((n+1)*phi) == 2 else b
|
|
588
|
+
return W(f)
|
|
589
|
+
|
|
590
|
+
else:
|
|
591
|
+
raise NotImplementedError
|
|
592
|
+
|
|
593
|
+
def _FibonacciWord_RecursiveConstructionIterator(self, alphabet=(0, 1)):
|
|
594
|
+
r"""
|
|
595
|
+
Iterate over the symbols of the Fibonacci word, as defined by
|
|
596
|
+
the following recursive construction: the Fibonacci word is the
|
|
597
|
+
limit of the sequence `S_0 = 0`, `S_1 = 01`, `S_n = S_{n-1}
|
|
598
|
+
S_{n-2}` for `n \geq 2`.
|
|
599
|
+
|
|
600
|
+
TESTS::
|
|
601
|
+
|
|
602
|
+
sage: from sage.combinat.words.word_generators import WordGenerator
|
|
603
|
+
sage: from itertools import islice
|
|
604
|
+
sage: it = WordGenerator()._FibonacciWord_RecursiveConstructionIterator()
|
|
605
|
+
sage: list(islice(it,13r))
|
|
606
|
+
[0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1]
|
|
607
|
+
"""
|
|
608
|
+
Fib0 = [0]
|
|
609
|
+
Fib1 = [0,1]
|
|
610
|
+
n = 0
|
|
611
|
+
while True:
|
|
612
|
+
it = iter(Fib1[n:])
|
|
613
|
+
for i in it:
|
|
614
|
+
n += 1
|
|
615
|
+
yield alphabet[i]
|
|
616
|
+
Fib1, Fib0 = Fib1 + Fib0, Fib1
|
|
617
|
+
|
|
618
|
+
def FixedPointOfMorphism(self, morphism, first_letter):
|
|
619
|
+
r"""
|
|
620
|
+
Return the fixed point of the morphism beginning with
|
|
621
|
+
``first_letter``.
|
|
622
|
+
|
|
623
|
+
A *fixed point* of a morphism `\varphi` is a word `w` such that
|
|
624
|
+
`\varphi(w) = w`.
|
|
625
|
+
|
|
626
|
+
INPUT:
|
|
627
|
+
|
|
628
|
+
- ``morphism`` -- endomorphism prolongable on ``first_letter``. It
|
|
629
|
+
must be something that WordMorphism's constructor understands
|
|
630
|
+
(dict, str, ...).
|
|
631
|
+
|
|
632
|
+
- ``first_letter`` -- the first letter of the fixed point
|
|
633
|
+
|
|
634
|
+
OUTPUT: the fixed point of the morphism beginning with ``first_letter``
|
|
635
|
+
|
|
636
|
+
EXAMPLES::
|
|
637
|
+
|
|
638
|
+
sage: mu = {0:[0,1], 1:[1,0]}
|
|
639
|
+
sage: tm = words.FixedPointOfMorphism(mu,0); tm
|
|
640
|
+
word: 0110100110010110100101100110100110010110...
|
|
641
|
+
sage: TM = words.ThueMorseWord()
|
|
642
|
+
sage: tm[:1000] == TM[:1000] # needs sage.modules
|
|
643
|
+
True
|
|
644
|
+
|
|
645
|
+
::
|
|
646
|
+
|
|
647
|
+
sage: mu = {0:[0,1], 1:[0]}
|
|
648
|
+
sage: f = words.FixedPointOfMorphism(mu,0); f
|
|
649
|
+
word: 0100101001001010010100100101001001010010...
|
|
650
|
+
sage: F = words.FibonacciWord(); F
|
|
651
|
+
word: 0100101001001010010100100101001001010010...
|
|
652
|
+
sage: f[:1000] == F[:1000] # needs sage.modules
|
|
653
|
+
True
|
|
654
|
+
|
|
655
|
+
::
|
|
656
|
+
|
|
657
|
+
sage: fp = words.FixedPointOfMorphism('a->abc,b->,c->','a'); fp
|
|
658
|
+
word: abc
|
|
659
|
+
"""
|
|
660
|
+
return WordMorphism(morphism).fixed_point(letter=first_letter)
|
|
661
|
+
|
|
662
|
+
def CodingOfRotationWord(self, alpha, beta, x=0, alphabet=(0, 1)):
|
|
663
|
+
r"""
|
|
664
|
+
Return the infinite word obtained from the coding of rotation of
|
|
665
|
+
parameters `(\alpha,\beta, x)` over the given two-letter alphabet.
|
|
666
|
+
|
|
667
|
+
The *coding of rotation* corresponding to the parameters
|
|
668
|
+
`(\alpha,\beta, x)` is the symbolic sequence `u = (u_n)_{n\geq 0}`
|
|
669
|
+
defined over the binary alphabet `\{0, 1\}` by `u_n = 1` if
|
|
670
|
+
`x+n\alpha\in[0, \beta[` and `u_n = 0` otherwise. See [AC03]_.
|
|
671
|
+
|
|
672
|
+
EXAMPLES::
|
|
673
|
+
|
|
674
|
+
sage: alpha = 0.45
|
|
675
|
+
sage: beta = 0.48
|
|
676
|
+
sage: words.CodingOfRotationWord(0.45, 0.48)
|
|
677
|
+
word: 1101010101001010101011010101010010101010...
|
|
678
|
+
|
|
679
|
+
::
|
|
680
|
+
|
|
681
|
+
sage: words.CodingOfRotationWord(0.45, 0.48, alphabet='xy')
|
|
682
|
+
word: yyxyxyxyxyxxyxyxyxyxyyxyxyxyxyxxyxyxyxyx...
|
|
683
|
+
|
|
684
|
+
TESTS::
|
|
685
|
+
|
|
686
|
+
sage: words.CodingOfRotationWord(0.51,0.43,alphabet=[1,0,2])
|
|
687
|
+
Traceback (most recent call last):
|
|
688
|
+
...
|
|
689
|
+
TypeError: alphabet does not contain two distinct elements
|
|
690
|
+
"""
|
|
691
|
+
if len(set(alphabet)) != 2:
|
|
692
|
+
raise TypeError("alphabet does not contain two distinct elements")
|
|
693
|
+
from functools import partial
|
|
694
|
+
f = partial(self._CodingOfRotationWord_function,alpha=alpha,beta=beta,x=x,alphabet=alphabet)
|
|
695
|
+
w = InfiniteWords(alphabet)(f, datatype='callable')
|
|
696
|
+
return w
|
|
697
|
+
|
|
698
|
+
def _CodingOfRotationWord_function(self, n, alpha, beta, x=0, alphabet=(0, 1)):
|
|
699
|
+
r"""
|
|
700
|
+
Internal function that returns the symbol in position `n` of the
|
|
701
|
+
coding of rotation word corresponding to the parameters `\alpha`,
|
|
702
|
+
`\beta`, and `x`.
|
|
703
|
+
|
|
704
|
+
TESTS::
|
|
705
|
+
|
|
706
|
+
sage: alpha, beta = 0.45, 0.48
|
|
707
|
+
sage: words._CodingOfRotationWord_function(3, alpha, beta)
|
|
708
|
+
1
|
|
709
|
+
sage: words._CodingOfRotationWord_function(10, alpha, beta)
|
|
710
|
+
0
|
|
711
|
+
sage: words._CodingOfRotationWord_function(17, alpha, beta)
|
|
712
|
+
0
|
|
713
|
+
"""
|
|
714
|
+
hauteur = x + n * alpha
|
|
715
|
+
fracH = hauteur.frac()
|
|
716
|
+
if fracH < 0:
|
|
717
|
+
fracH += 1
|
|
718
|
+
if 0 <= fracH < beta:
|
|
719
|
+
return alphabet[1]
|
|
720
|
+
else:
|
|
721
|
+
return alphabet[0]
|
|
722
|
+
|
|
723
|
+
@rename_keyword(cf='slope')
|
|
724
|
+
def CharacteristicSturmianWord(self, slope, alphabet=(0, 1), bits=None):
|
|
725
|
+
r"""
|
|
726
|
+
Return the characteristic Sturmian word (also called standard
|
|
727
|
+
Sturmian word) of given slope.
|
|
728
|
+
|
|
729
|
+
Over a binary alphabet `\{a,b\}`, the characteristic Sturmian
|
|
730
|
+
word `c_\alpha` of irrational slope `\alpha` is the infinite word
|
|
731
|
+
satisfying `s_{\alpha,0} = ac_\alpha` and `s'_{\alpha,0} = bc_\alpha`,
|
|
732
|
+
where `s_{\alpha,0}` and `s'_{\alpha,0}` are respectively the lower
|
|
733
|
+
and upper mechanical words with slope `\alpha` and intercept `0`.
|
|
734
|
+
Equivalently, for irrational `\alpha`,
|
|
735
|
+
`c_\alpha = s_{\alpha,\alpha} = s'_{\alpha,\alpha}`.
|
|
736
|
+
|
|
737
|
+
Let `\alpha = [0, d_1 + 1, d_2, d_3, \ldots]` be the continued
|
|
738
|
+
fraction expansion of `\alpha`. It has been shown that the
|
|
739
|
+
characteristic Sturmian word of slope `\alpha` is also the limit of
|
|
740
|
+
the sequence: `s_0 = b, s_1 = a, \ldots, s_{n+1} = s_n^{d_n} s_{n-1}`
|
|
741
|
+
for `n > 0`.
|
|
742
|
+
|
|
743
|
+
See Section 2.1 of [Loth02]_ for more details.
|
|
744
|
+
|
|
745
|
+
INPUT:
|
|
746
|
+
|
|
747
|
+
- ``slope`` -- the slope of the word. It can be one of the following:
|
|
748
|
+
|
|
749
|
+
- real number in `]0, 1[`
|
|
750
|
+
|
|
751
|
+
- iterable over the continued fraction expansion of a real
|
|
752
|
+
number in `]0, 1[`
|
|
753
|
+
|
|
754
|
+
- ``alphabet`` -- any container of length two that is suitable to
|
|
755
|
+
build an instance of OrderedAlphabet (``list``, ``tuple``, ``str``,
|
|
756
|
+
...)
|
|
757
|
+
|
|
758
|
+
- ``bits`` -- integer (optional and considered only if ``slope`` is
|
|
759
|
+
a real number); the number of bits to consider when computing the
|
|
760
|
+
continued fraction
|
|
761
|
+
|
|
762
|
+
OUTPUT: word
|
|
763
|
+
|
|
764
|
+
ALGORITHM:
|
|
765
|
+
|
|
766
|
+
Let `[0, d_1 + 1, d_2, d_3, \ldots]` be the continued fraction
|
|
767
|
+
expansion of `\alpha`. Then, the characteristic Sturmian word of
|
|
768
|
+
slope `\alpha` is the limit of the sequence: `s_0 = b`, `s_1 = a`
|
|
769
|
+
and `s_{n+1} = s_n^{d_n} s_{n-1}` for `n > 0`.
|
|
770
|
+
|
|
771
|
+
EXAMPLES:
|
|
772
|
+
|
|
773
|
+
From real slope::
|
|
774
|
+
|
|
775
|
+
sage: words.CharacteristicSturmianWord(1/golden_ratio^2) # needs sage.symbolic
|
|
776
|
+
word: 0100101001001010010100100101001001010010...
|
|
777
|
+
sage: words.CharacteristicSturmianWord(4/5) # needs sage.rings.real_mpfr
|
|
778
|
+
word: 11110
|
|
779
|
+
sage: words.CharacteristicSturmianWord(5/14) # needs sage.rings.real_mpfr
|
|
780
|
+
word: 01001001001001
|
|
781
|
+
sage: words.CharacteristicSturmianWord(pi - 3) # needs sage.symbolic
|
|
782
|
+
word: 0000001000000100000010000001000000100000...
|
|
783
|
+
|
|
784
|
+
From an iterator of the continued fraction expansion of a real::
|
|
785
|
+
|
|
786
|
+
sage: def cf():
|
|
787
|
+
....: yield 0
|
|
788
|
+
....: yield 2
|
|
789
|
+
....: while True: yield 1
|
|
790
|
+
sage: F = words.CharacteristicSturmianWord(cf()); F # needs sage.rings.real_mpfr
|
|
791
|
+
word: 0100101001001010010100100101001001010010...
|
|
792
|
+
sage: Fib = words.FibonacciWord(); Fib
|
|
793
|
+
word: 0100101001001010010100100101001001010010...
|
|
794
|
+
sage: F[:10000] == Fib[:10000] # needs sage.rings.real_mpfr
|
|
795
|
+
True
|
|
796
|
+
|
|
797
|
+
The alphabet may be specified::
|
|
798
|
+
|
|
799
|
+
sage: words.CharacteristicSturmianWord(cf(), 'rs') # needs sage.rings.real_mpfr
|
|
800
|
+
word: rsrrsrsrrsrrsrsrrsrsrrsrrsrsrrsrrsrsrrsr...
|
|
801
|
+
|
|
802
|
+
The characteristic sturmian word of slope `(\sqrt{3}-1)/2`::
|
|
803
|
+
|
|
804
|
+
sage: words.CharacteristicSturmianWord((sqrt(3)-1)/2) # needs sage.symbolic
|
|
805
|
+
word: 0100100101001001001010010010010100100101...
|
|
806
|
+
|
|
807
|
+
The same word defined from the continued fraction expansion of
|
|
808
|
+
`(\sqrt{3}-1)/2`::
|
|
809
|
+
|
|
810
|
+
sage: from itertools import cycle, chain
|
|
811
|
+
sage: it = chain([0], cycle([2, 1]))
|
|
812
|
+
sage: words.CharacteristicSturmianWord(it) # needs sage.rings.real_mpfr
|
|
813
|
+
word: 0100100101001001001010010010010100100101...
|
|
814
|
+
|
|
815
|
+
The first terms of the standard sequence of the characteristic
|
|
816
|
+
sturmian word of slope `(\sqrt{3}-1)/2`::
|
|
817
|
+
|
|
818
|
+
sage: words.CharacteristicSturmianWord([0,2])
|
|
819
|
+
word: 01
|
|
820
|
+
sage: words.CharacteristicSturmianWord([0,2,1])
|
|
821
|
+
word: 010
|
|
822
|
+
sage: words.CharacteristicSturmianWord([0,2,1,2])
|
|
823
|
+
word: 01001001
|
|
824
|
+
sage: words.CharacteristicSturmianWord([0,2,1,2,1])
|
|
825
|
+
word: 01001001010
|
|
826
|
+
sage: words.CharacteristicSturmianWord([0,2,1,2,1,2])
|
|
827
|
+
word: 010010010100100100101001001001
|
|
828
|
+
sage: words.CharacteristicSturmianWord([0,2,1,2,1,2,1])
|
|
829
|
+
word: 0100100101001001001010010010010100100101...
|
|
830
|
+
|
|
831
|
+
TESTS::
|
|
832
|
+
|
|
833
|
+
sage: words.CharacteristicSturmianWord([1,1,1],'xyz')
|
|
834
|
+
Traceback (most recent call last):
|
|
835
|
+
...
|
|
836
|
+
TypeError: alphabet does not contain two distinct elements
|
|
837
|
+
|
|
838
|
+
::
|
|
839
|
+
|
|
840
|
+
sage: words.CharacteristicSturmianWord(5/4)
|
|
841
|
+
Traceback (most recent call last):
|
|
842
|
+
...
|
|
843
|
+
ValueError: the argument slope (=5/4) must be in ]0,1[
|
|
844
|
+
|
|
845
|
+
::
|
|
846
|
+
|
|
847
|
+
sage: words.CharacteristicSturmianWord(1/golden_ratio^2) # needs sage.symbolic
|
|
848
|
+
word: 0100101001001010010100100101001001010010...
|
|
849
|
+
sage: _.length() # needs sage.symbolic
|
|
850
|
+
+Infinity
|
|
851
|
+
|
|
852
|
+
::
|
|
853
|
+
|
|
854
|
+
sage: # needs sage.symbolic
|
|
855
|
+
sage: a = words.LowerMechanicalWord(1/pi)[1:]
|
|
856
|
+
sage: b = words.UpperMechanicalWord(1/pi)[1:]
|
|
857
|
+
sage: c = words.CharacteristicSturmianWord(1/pi)
|
|
858
|
+
sage: n = 500; a[:n] == b[:n] == c[:n]
|
|
859
|
+
True
|
|
860
|
+
|
|
861
|
+
::
|
|
862
|
+
|
|
863
|
+
sage: alpha = random()
|
|
864
|
+
sage: c = words.CharacteristicSturmianWord(alpha)
|
|
865
|
+
sage: l = words.LowerMechanicalWord(alpha)[1:]
|
|
866
|
+
sage: u = words.UpperMechanicalWord(alpha)[1:]
|
|
867
|
+
sage: i = 10000; j = i + 500; c[i:j] == l[i:j] == u[i:j]
|
|
868
|
+
True
|
|
869
|
+
|
|
870
|
+
::
|
|
871
|
+
|
|
872
|
+
sage: a, b = 207, 232
|
|
873
|
+
sage: u = words.ChristoffelWord(a, b)
|
|
874
|
+
sage: v = words.CharacteristicSturmianWord(a/(a+b))
|
|
875
|
+
sage: v.length()
|
|
876
|
+
439
|
|
877
|
+
sage: u[1:-1] == v[:-2]
|
|
878
|
+
True
|
|
879
|
+
"""
|
|
880
|
+
if len(set(alphabet)) != 2:
|
|
881
|
+
raise TypeError("alphabet does not contain two distinct elements")
|
|
882
|
+
|
|
883
|
+
if slope in RR:
|
|
884
|
+
if not 0 < slope < 1:
|
|
885
|
+
msg = "the argument slope (=%s) must be in ]0,1[" % slope
|
|
886
|
+
raise ValueError(msg)
|
|
887
|
+
from sage.rings.continued_fraction import continued_fraction
|
|
888
|
+
cf = continued_fraction(slope)
|
|
889
|
+
if cf.length() == Infinity:
|
|
890
|
+
parent = InfiniteWords(alphabet)
|
|
891
|
+
else:
|
|
892
|
+
parent = FiniteWords(alphabet)
|
|
893
|
+
cf = iter(cf)
|
|
894
|
+
elif isinstance(slope, Iterable):
|
|
895
|
+
cf = iter(slope)
|
|
896
|
+
parent = InfiniteWords(alphabet)
|
|
897
|
+
else:
|
|
898
|
+
raise TypeError("slope (=%s) must be a real number" % slope +
|
|
899
|
+
"or an iterable")
|
|
900
|
+
w = parent(self._CharacteristicSturmianWord_LetterIterator(cf,alphabet),
|
|
901
|
+
datatype='iter')
|
|
902
|
+
return w
|
|
903
|
+
|
|
904
|
+
def _CharacteristicSturmianWord_LetterIterator(self, cf, alphabet=(0, 1)):
|
|
905
|
+
r"""
|
|
906
|
+
Return an iterator over the symbols of the characteristic
|
|
907
|
+
Sturmian word of slope ``cf``.
|
|
908
|
+
|
|
909
|
+
INPUT:
|
|
910
|
+
|
|
911
|
+
- ``cf`` -- iterator, the continued fraction expansion of a real
|
|
912
|
+
number in `]0, 1[`
|
|
913
|
+
- ``alphabet`` -- the alphabet (default: ``(0,1)``) of
|
|
914
|
+
the output
|
|
915
|
+
|
|
916
|
+
OUTPUT: iterator of letters
|
|
917
|
+
|
|
918
|
+
ALGORITHM:
|
|
919
|
+
|
|
920
|
+
Let `[0, d_1 + 1, d_2, d_3, \ldots]` be the continued fraction
|
|
921
|
+
expansion of `\alpha`. Then, the characteristic Sturmian word of
|
|
922
|
+
slope `\alpha` is the limit of the sequence: `s_0 = 1`, `s_1 = 0`
|
|
923
|
+
and `s_{n+1} = s_n^{d_n} s_{n-1}` for `n > 0`.
|
|
924
|
+
|
|
925
|
+
EXAMPLES::
|
|
926
|
+
|
|
927
|
+
sage: continued_fraction(1/golden_ratio^2)[:8] # needs sage.symbolic
|
|
928
|
+
[0; 2, 1, 1, 1, 1, 2]
|
|
929
|
+
sage: cf = iter(_) # needs sage.symbolic
|
|
930
|
+
sage: Word(words._CharacteristicSturmianWord_LetterIterator(cf)) # needs sage.symbolic
|
|
931
|
+
word: 0100101001001010010100100101001010
|
|
932
|
+
|
|
933
|
+
::
|
|
934
|
+
|
|
935
|
+
sage: # needs sage.symbolic
|
|
936
|
+
sage: alpha = (sqrt(3)-1)/2
|
|
937
|
+
sage: continued_fraction(alpha)[:10]
|
|
938
|
+
[0; 2, 1, 2, 1, 2, 1, 2, 1, 2]
|
|
939
|
+
sage: cf = iter(_)
|
|
940
|
+
sage: Word(words._CharacteristicSturmianWord_LetterIterator(cf))
|
|
941
|
+
word: 0100100101001001001010010010010100100101...
|
|
942
|
+
"""
|
|
943
|
+
try:
|
|
944
|
+
if next(cf) != 0:
|
|
945
|
+
raise ValueError("the first term of the continued fraction expansion must be zero")
|
|
946
|
+
except StopIteration:
|
|
947
|
+
return
|
|
948
|
+
|
|
949
|
+
s0 = [1]
|
|
950
|
+
s1 = [0]
|
|
951
|
+
try:
|
|
952
|
+
e = next(cf)
|
|
953
|
+
except StopIteration:
|
|
954
|
+
return
|
|
955
|
+
|
|
956
|
+
if not e >= 1:
|
|
957
|
+
raise ValueError("the second term of the continued fraction expansion must be larger or equal to 1")
|
|
958
|
+
s1, s0 = s1*(e-1) + s0, s1
|
|
959
|
+
n = 0
|
|
960
|
+
while True:
|
|
961
|
+
try:
|
|
962
|
+
for i in s1[n:]:
|
|
963
|
+
n += 1
|
|
964
|
+
yield alphabet[i]
|
|
965
|
+
s1, s0 = s1*next(cf) + s0, s1
|
|
966
|
+
except StopIteration:
|
|
967
|
+
return
|
|
968
|
+
|
|
969
|
+
def KolakoskiWord(self, alphabet=(1, 2)):
|
|
970
|
+
r"""
|
|
971
|
+
Return the Kolakoski word over the given alphabet and
|
|
972
|
+
starting with the first letter of the alphabet.
|
|
973
|
+
|
|
974
|
+
Let `A = \{a,b\}` be an alphabet, where `a` and `b` are two
|
|
975
|
+
distinct positive integers. The Kolakoski word `K_{a,b}`
|
|
976
|
+
over `A` and starting with `a` is the unique infinite word `w`
|
|
977
|
+
such that `w = \Delta(w)`, where `\Delta(w)` is the word
|
|
978
|
+
encoding the runs of `w` (see ``delta()`` method on words for
|
|
979
|
+
more details).
|
|
980
|
+
|
|
981
|
+
Note that `K_{a,b} \neq K_{b,a}`. On the other hand, the
|
|
982
|
+
words `K_{a,b}` and `K_{b,a}` are the unique two words over `A`
|
|
983
|
+
that are fixed by `\Delta`.
|
|
984
|
+
|
|
985
|
+
Also note that the Kolakoski word is also known as the
|
|
986
|
+
Oldenburger word.
|
|
987
|
+
|
|
988
|
+
INPUT:
|
|
989
|
+
|
|
990
|
+
- ``alphabet`` -- (default: (1,2)) an iterable of two positive
|
|
991
|
+
integers
|
|
992
|
+
|
|
993
|
+
OUTPUT: infinite word
|
|
994
|
+
|
|
995
|
+
EXAMPLES:
|
|
996
|
+
|
|
997
|
+
The usual Kolakoski word::
|
|
998
|
+
|
|
999
|
+
sage: w = words.KolakoskiWord()
|
|
1000
|
+
sage: w
|
|
1001
|
+
word: 1221121221221121122121121221121121221221...
|
|
1002
|
+
sage: w.delta()
|
|
1003
|
+
word: 1221121221221121122121121221121121221221...
|
|
1004
|
+
|
|
1005
|
+
The other Kolakoski word on the same alphabet::
|
|
1006
|
+
|
|
1007
|
+
sage: w = words.KolakoskiWord(alphabet = (2,1))
|
|
1008
|
+
sage: w
|
|
1009
|
+
word: 2211212212211211221211212211211212212211...
|
|
1010
|
+
sage: w.delta()
|
|
1011
|
+
word: 2211212212211211221211212211211212212211...
|
|
1012
|
+
|
|
1013
|
+
It is naturally generalized to any two integers alphabet::
|
|
1014
|
+
|
|
1015
|
+
sage: w = words.KolakoskiWord(alphabet = (2,5))
|
|
1016
|
+
sage: w
|
|
1017
|
+
word: 2255222225555522552255225555522222555552...
|
|
1018
|
+
sage: w.delta()
|
|
1019
|
+
word: 2255222225555522552255225555522222555552...
|
|
1020
|
+
|
|
1021
|
+
TESTS::
|
|
1022
|
+
|
|
1023
|
+
sage: for i in range(1,10):
|
|
1024
|
+
....: for j in range(1,10):
|
|
1025
|
+
....: if i != j:
|
|
1026
|
+
....: w = words.KolakoskiWord(alphabet=(i,j))
|
|
1027
|
+
....: assert w[:50] == w.delta()[:50]
|
|
1028
|
+
|
|
1029
|
+
::
|
|
1030
|
+
|
|
1031
|
+
sage: words.KolakoskiWord((0, 2))
|
|
1032
|
+
Traceback (most recent call last):
|
|
1033
|
+
...
|
|
1034
|
+
ValueError: the alphabet (=(0, 2)) must consist of two distinct positive integers
|
|
1035
|
+
|
|
1036
|
+
REFERENCES:
|
|
1037
|
+
|
|
1038
|
+
.. [Kolakoski66] William Kolakoski, proposal 5304, American Mathematical Monthly
|
|
1039
|
+
72 (1965), 674; for a partial solution, see "Self Generating Runs,"
|
|
1040
|
+
by Necdet Üçoluk, Amer. Math. Mon. 73 (1966), 681-2.
|
|
1041
|
+
"""
|
|
1042
|
+
a, b = alphabet
|
|
1043
|
+
if a not in ZZ or a <= 0 or b not in ZZ or b <= 0 or a == b:
|
|
1044
|
+
msg = 'the alphabet (=%s) must consist of two distinct positive integers' % (alphabet,)
|
|
1045
|
+
raise ValueError(msg)
|
|
1046
|
+
return InfiniteWords(alphabet)(self._KolakoskiWord_iterator(a, b), datatype='iter')
|
|
1047
|
+
|
|
1048
|
+
def _KolakoskiWord_iterator(self, a=1, b=2):
|
|
1049
|
+
r"""
|
|
1050
|
+
Return an iterator over the Kolakoski word over ``{a,b}``
|
|
1051
|
+
and starting with ``a``.
|
|
1052
|
+
|
|
1053
|
+
Let `A = \{a,b\}` be an alphabet, where `a` and `b` are two
|
|
1054
|
+
distinct positive integers. The Kolakoski word `K_{a,b}`
|
|
1055
|
+
over `A` and starting with `a` is the unique infinite word `w`
|
|
1056
|
+
such that `w = \Delta(w)`, where `\Delta(w)` is the word
|
|
1057
|
+
encoding the runs of `w` (see ``delta()`` method on words for
|
|
1058
|
+
more details).
|
|
1059
|
+
|
|
1060
|
+
Note that `K_{a,b} \neq K_{b,a}`. On the other hand, the
|
|
1061
|
+
words `K_{a,b}` and `K_{b,a}` are the unique two words over `A`
|
|
1062
|
+
that are fixed by `\Delta`.
|
|
1063
|
+
|
|
1064
|
+
INPUT:
|
|
1065
|
+
|
|
1066
|
+
- ``a`` -- positive integer (default: 1); the first letter occurring
|
|
1067
|
+
in the returned Kolakoski word
|
|
1068
|
+
- ``b`` -- positive integer (default: 2); the second and last letter
|
|
1069
|
+
occurring in the returned Kolakoski word
|
|
1070
|
+
|
|
1071
|
+
OUTPUT: iterator
|
|
1072
|
+
|
|
1073
|
+
EXAMPLES:
|
|
1074
|
+
|
|
1075
|
+
The first ten letters of `K_{3,5}`::
|
|
1076
|
+
|
|
1077
|
+
sage: iter = words._KolakoskiWord_iterator(3, 5)
|
|
1078
|
+
sage: Word(iter)[:10]
|
|
1079
|
+
word: 3335553335
|
|
1080
|
+
|
|
1081
|
+
See ``words.KolakoskiWord()`` for more documentation.
|
|
1082
|
+
"""
|
|
1083
|
+
# First, we need to treat the basis case
|
|
1084
|
+
w = [a] * a
|
|
1085
|
+
for _ in range(a):
|
|
1086
|
+
yield a
|
|
1087
|
+
if a == 1:
|
|
1088
|
+
w.extend([b] * b)
|
|
1089
|
+
for _ in range(b):
|
|
1090
|
+
yield b
|
|
1091
|
+
w.pop(0)
|
|
1092
|
+
w.pop(0)
|
|
1093
|
+
# Letters swap function
|
|
1094
|
+
bar = lambda x: a if x == b else b
|
|
1095
|
+
current_letter = bar(w[-1])
|
|
1096
|
+
# Now we are ready to go in the recursive part
|
|
1097
|
+
while True:
|
|
1098
|
+
for _ in range(w[0]):
|
|
1099
|
+
yield current_letter
|
|
1100
|
+
w.append(current_letter)
|
|
1101
|
+
w.pop(0)
|
|
1102
|
+
current_letter = bar(current_letter)
|
|
1103
|
+
|
|
1104
|
+
def LowerMechanicalWord(self, alpha, rho=0, alphabet=None):
|
|
1105
|
+
r"""
|
|
1106
|
+
Return the lower mechanical word with slope `\alpha` and
|
|
1107
|
+
intercept `\rho`
|
|
1108
|
+
|
|
1109
|
+
The lower mechanical word `s_{\alpha,\rho}` with
|
|
1110
|
+
slope `\alpha` and intercept `\rho` is defined by
|
|
1111
|
+
`s_{\alpha,\rho}(n) = \lfloor\alpha(n+1) + \rho\rfloor -
|
|
1112
|
+
\lfloor\alpha n + \rho\rfloor`. [Loth02]_
|
|
1113
|
+
|
|
1114
|
+
INPUT:
|
|
1115
|
+
|
|
1116
|
+
- ``alpha`` -- real number such that `0 \leq\alpha\leq 1`
|
|
1117
|
+
|
|
1118
|
+
- ``rho`` -- real number (default: 0)
|
|
1119
|
+
|
|
1120
|
+
- ``alphabet`` -- iterable of two elements or ``None``
|
|
1121
|
+
(default: ``None``)
|
|
1122
|
+
|
|
1123
|
+
OUTPUT: infinite word
|
|
1124
|
+
|
|
1125
|
+
EXAMPLES::
|
|
1126
|
+
|
|
1127
|
+
sage: words.LowerMechanicalWord(1/golden_ratio^2) # needs sage.symbolic
|
|
1128
|
+
word: 0010010100100101001010010010100100101001...
|
|
1129
|
+
sage: words.LowerMechanicalWord(1/5) # needs sage.symbolic
|
|
1130
|
+
word: 0000100001000010000100001000010000100001...
|
|
1131
|
+
sage: words.LowerMechanicalWord(1/pi) # needs sage.symbolic
|
|
1132
|
+
word: 0001001001001001001001000100100100100100...
|
|
1133
|
+
|
|
1134
|
+
TESTS::
|
|
1135
|
+
|
|
1136
|
+
sage: m = words.LowerMechanicalWord(1/golden_ratio^2)[1:] # needs sage.symbolic
|
|
1137
|
+
sage: s = words.CharacteristicSturmianWord(1/golden_ratio^2) # needs sage.symbolic
|
|
1138
|
+
sage: m[:500] == s[:500] # needs sage.symbolic
|
|
1139
|
+
True
|
|
1140
|
+
|
|
1141
|
+
Check that this returns a word in an alphabet (:issue:`10054`)::
|
|
1142
|
+
|
|
1143
|
+
sage: words.UpperMechanicalWord(1/golden_ratio^2).parent() # needs sage.symbolic
|
|
1144
|
+
Infinite words over {0, 1}
|
|
1145
|
+
"""
|
|
1146
|
+
if not 0 <= alpha <= 1:
|
|
1147
|
+
raise ValueError("parameter alpha (=%s) must be in [0,1]" % alpha)
|
|
1148
|
+
|
|
1149
|
+
from sage.functions.other import floor
|
|
1150
|
+
from sage.combinat.words.alphabet import build_alphabet
|
|
1151
|
+
if alphabet is None or alphabet in ((0, 1), [0, 1]):
|
|
1152
|
+
alphabet = build_alphabet([0, 1])
|
|
1153
|
+
s = lambda n: floor(alpha*(n+1) + rho) - floor(alpha*n + rho)
|
|
1154
|
+
else:
|
|
1155
|
+
alphabet = build_alphabet(alphabet)
|
|
1156
|
+
card = alphabet.cardinality()
|
|
1157
|
+
if card != 2:
|
|
1158
|
+
raise TypeError("size of alphabet (=%s) must be two" % card)
|
|
1159
|
+
s = lambda n: alphabet[floor(alpha*(n+1) + rho) - floor(alpha*n + rho)]
|
|
1160
|
+
return InfiniteWords(alphabet)(s)
|
|
1161
|
+
|
|
1162
|
+
def UpperMechanicalWord(self, alpha, rho=0, alphabet=None):
|
|
1163
|
+
r"""
|
|
1164
|
+
Return the upper mechanical word with slope `\alpha` and
|
|
1165
|
+
intercept `\rho`
|
|
1166
|
+
|
|
1167
|
+
The upper mechanical word `s'_{\alpha,\rho}` with
|
|
1168
|
+
slope `\alpha` and intercept `\rho` is defined by
|
|
1169
|
+
`s'_{\alpha,\rho}(n) = \lceil\alpha(n+1) + \rho\rceil -
|
|
1170
|
+
\lceil\alpha n + \rho\rceil`. [Loth02]_
|
|
1171
|
+
|
|
1172
|
+
INPUT:
|
|
1173
|
+
|
|
1174
|
+
- ``alpha`` -- real number such that `0 \leq\alpha\leq 1`
|
|
1175
|
+
|
|
1176
|
+
- ``rho`` -- real number (default: 0)
|
|
1177
|
+
|
|
1178
|
+
- ``alphabet`` -- iterable of two elements or ``None``
|
|
1179
|
+
(default: ``None``)
|
|
1180
|
+
|
|
1181
|
+
OUTPUT: infinite word
|
|
1182
|
+
|
|
1183
|
+
EXAMPLES::
|
|
1184
|
+
|
|
1185
|
+
sage: words.UpperMechanicalWord(1/golden_ratio^2) # needs sage.symbolic
|
|
1186
|
+
word: 1010010100100101001010010010100100101001...
|
|
1187
|
+
sage: words.UpperMechanicalWord(1/5) # needs sage.symbolic
|
|
1188
|
+
word: 1000010000100001000010000100001000010000...
|
|
1189
|
+
sage: words.UpperMechanicalWord(1/pi) # needs sage.symbolic
|
|
1190
|
+
word: 1001001001001001001001000100100100100100...
|
|
1191
|
+
|
|
1192
|
+
TESTS::
|
|
1193
|
+
|
|
1194
|
+
sage: m = words.UpperMechanicalWord(1/golden_ratio^2)[1:] # needs sage.symbolic
|
|
1195
|
+
sage: s = words.CharacteristicSturmianWord(1/golden_ratio^2) # needs sage.symbolic
|
|
1196
|
+
sage: m[:500] == s[:500] # needs sage.symbolic
|
|
1197
|
+
True
|
|
1198
|
+
|
|
1199
|
+
Check that this returns a word in an alphabet (:issue:`10054`)::
|
|
1200
|
+
|
|
1201
|
+
sage: words.UpperMechanicalWord(1/golden_ratio^2).parent() # needs sage.symbolic
|
|
1202
|
+
Infinite words over {0, 1}
|
|
1203
|
+
"""
|
|
1204
|
+
if not 0 <= alpha <= 1:
|
|
1205
|
+
raise ValueError("parameter alpha (=%s) must be in [0,1]" % alpha)
|
|
1206
|
+
|
|
1207
|
+
from sage.functions.other import ceil
|
|
1208
|
+
from sage.combinat.words.alphabet import build_alphabet
|
|
1209
|
+
if alphabet is None or alphabet in ((0, 1), [0, 1]):
|
|
1210
|
+
alphabet = build_alphabet([0, 1])
|
|
1211
|
+
s = lambda n: ceil(alpha*(n+1) + rho) - ceil(alpha*n + rho)
|
|
1212
|
+
else:
|
|
1213
|
+
alphabet = build_alphabet(alphabet)
|
|
1214
|
+
card = alphabet.cardinality()
|
|
1215
|
+
if card != 2:
|
|
1216
|
+
raise TypeError("size of alphabet (=%s) must be two" % card)
|
|
1217
|
+
s = lambda n: alphabet[ceil(alpha*(n+1) + rho) - ceil(alpha*n + rho)]
|
|
1218
|
+
return InfiniteWords(alphabet)(s)
|
|
1219
|
+
|
|
1220
|
+
def StandardEpisturmianWord(self, directive_word):
|
|
1221
|
+
r"""
|
|
1222
|
+
Return the standard episturmian word (or epistandard word) directed by
|
|
1223
|
+
directive_word. Over a 2-letter alphabet, this function
|
|
1224
|
+
gives characteristic Sturmian words.
|
|
1225
|
+
|
|
1226
|
+
An infinite word `w` over a finite alphabet `A` is said to be
|
|
1227
|
+
*standard episturmian* (or *epistandard*) iff there exists an
|
|
1228
|
+
infinite word `x_1x_2x_3\cdots` over `A` (called the *directive
|
|
1229
|
+
word* of `w`) such that `w` is the limit as `n` goes to infinity of
|
|
1230
|
+
`Pal(x_1\cdots x_n)`, where `Pal` is the iterated palindromic closure
|
|
1231
|
+
function.
|
|
1232
|
+
|
|
1233
|
+
Note that an infinite word is *episturmian* if it has the same set
|
|
1234
|
+
of factors as some epistandard word.
|
|
1235
|
+
|
|
1236
|
+
See for instance [DJP2001]_, [JP2002]_, and [GJ2007]_.
|
|
1237
|
+
|
|
1238
|
+
INPUT:
|
|
1239
|
+
|
|
1240
|
+
- ``directive_word`` -- an infinite word or a period of a periodic
|
|
1241
|
+
infinite word
|
|
1242
|
+
|
|
1243
|
+
EXAMPLES::
|
|
1244
|
+
|
|
1245
|
+
sage: Fibonacci = words.StandardEpisturmianWord(Words('ab')('ab')); Fibonacci
|
|
1246
|
+
word: abaababaabaababaababaabaababaabaababaaba...
|
|
1247
|
+
sage: Tribonacci = words.StandardEpisturmianWord(Words('abc')('abc')); Tribonacci
|
|
1248
|
+
word: abacabaabacababacabaabacabacabaabacababa...
|
|
1249
|
+
sage: S = words.StandardEpisturmianWord(Words('abcd')('aabcabada')); S
|
|
1250
|
+
word: aabaacaabaaabaacaabaabaacaabaaabaacaabaa...
|
|
1251
|
+
sage: S = words.StandardEpisturmianWord(Fibonacci); S
|
|
1252
|
+
word: abaabaababaabaabaababaabaababaabaabaabab...
|
|
1253
|
+
sage: S[:25]
|
|
1254
|
+
word: abaabaababaabaabaababaaba
|
|
1255
|
+
sage: S = words.StandardEpisturmianWord(Tribonacci); S
|
|
1256
|
+
word: abaabacabaabaabacabaababaabacabaabaabaca...
|
|
1257
|
+
sage: words.StandardEpisturmianWord(123)
|
|
1258
|
+
Traceback (most recent call last):
|
|
1259
|
+
...
|
|
1260
|
+
TypeError: directive_word is not a word, so it cannot be used to build an episturmian word
|
|
1261
|
+
sage: words.StandardEpisturmianWord(Words('ab'))
|
|
1262
|
+
Traceback (most recent call last):
|
|
1263
|
+
...
|
|
1264
|
+
TypeError: directive_word is not a word, so it cannot be used to build an episturmian word
|
|
1265
|
+
"""
|
|
1266
|
+
if not isinstance(directive_word, Word_class):
|
|
1267
|
+
raise TypeError("directive_word is not a word, so it cannot be used to build an episturmian word")
|
|
1268
|
+
epistandard = directive_word.parent()(
|
|
1269
|
+
self._StandardEpisturmianWord_LetterIterator(directive_word),
|
|
1270
|
+
datatype='iter')
|
|
1271
|
+
return epistandard
|
|
1272
|
+
|
|
1273
|
+
def _StandardEpisturmianWord_LetterIterator(self, directive_word):
|
|
1274
|
+
r"""
|
|
1275
|
+
Internal iterating over the symbols of the standard episturmian
|
|
1276
|
+
word defined by the (directive) word directive_word.
|
|
1277
|
+
|
|
1278
|
+
An infinite word `w` over a finite alphabet `A` is standard episturmian
|
|
1279
|
+
(or epistandard) iff there exists an infinite word `x_1x_2x_3\ldots`
|
|
1280
|
+
over `A` (called the directive word of `w`) such that `w` is the limit
|
|
1281
|
+
as `n` goes to infinity of `Pal(x_1x_2\cdots x_n)`, where `Pal` is the
|
|
1282
|
+
iterated palindromic closure function.
|
|
1283
|
+
|
|
1284
|
+
INPUT:
|
|
1285
|
+
|
|
1286
|
+
- ``directive_word`` -- an infinite word or a finite word; if
|
|
1287
|
+
directive_word is finite, then it is repeated to give
|
|
1288
|
+
an infinite word
|
|
1289
|
+
|
|
1290
|
+
TESTS::
|
|
1291
|
+
|
|
1292
|
+
sage: import itertools
|
|
1293
|
+
sage: it = words._StandardEpisturmianWord_LetterIterator(Word('ab'))
|
|
1294
|
+
sage: list(itertools.islice(it, 13r))
|
|
1295
|
+
['a', 'b', 'a', 'a', 'b', 'a', 'b', 'a', 'a', 'b', 'a', 'a', 'b']
|
|
1296
|
+
"""
|
|
1297
|
+
if isinstance(directive_word, FiniteWord_class):
|
|
1298
|
+
d = cycle(directive_word)
|
|
1299
|
+
else:
|
|
1300
|
+
d = iter(directive_word)
|
|
1301
|
+
W = directive_word.parent()
|
|
1302
|
+
w = W(next(d))
|
|
1303
|
+
n = 0
|
|
1304
|
+
while True:
|
|
1305
|
+
for x in w[n:]:
|
|
1306
|
+
n += 1
|
|
1307
|
+
yield x
|
|
1308
|
+
w = W(w * W(next(d))).palindromic_closure()
|
|
1309
|
+
|
|
1310
|
+
def MinimalSmoothPrefix(self, n):
|
|
1311
|
+
r"""
|
|
1312
|
+
This function finds and returns the minimal smooth prefix of length
|
|
1313
|
+
``n``.
|
|
1314
|
+
|
|
1315
|
+
See [BMP2007]_ for a definition.
|
|
1316
|
+
|
|
1317
|
+
INPUT:
|
|
1318
|
+
|
|
1319
|
+
- ``n`` -- the desired length of the prefix
|
|
1320
|
+
|
|
1321
|
+
OUTPUT: word; the prefix
|
|
1322
|
+
|
|
1323
|
+
.. NOTE::
|
|
1324
|
+
|
|
1325
|
+
Be patient, this function can take a really long time if asked
|
|
1326
|
+
for a large prefix.
|
|
1327
|
+
|
|
1328
|
+
EXAMPLES::
|
|
1329
|
+
|
|
1330
|
+
sage: words.MinimalSmoothPrefix(10)
|
|
1331
|
+
word: 1212212112
|
|
1332
|
+
"""
|
|
1333
|
+
tab = []
|
|
1334
|
+
W = FiniteWords([1, 2])
|
|
1335
|
+
suff1 = W([1, 2, 2]).phi_inv()
|
|
1336
|
+
suff2 = W([2, 2]).phi_inv()
|
|
1337
|
+
w = [1]
|
|
1338
|
+
tab = _build_tab(1, tab, W)
|
|
1339
|
+
for k in range(1, n):
|
|
1340
|
+
if suff1._phi_inv_tab(tab) < suff2._phi_inv_tab(tab):
|
|
1341
|
+
w.append(1)
|
|
1342
|
+
tab = _build_tab(1, tab, W)
|
|
1343
|
+
else:
|
|
1344
|
+
w.append(2)
|
|
1345
|
+
tab = _build_tab(2, tab, W)
|
|
1346
|
+
return W(w)
|
|
1347
|
+
|
|
1348
|
+
def RandomWord(self, n, m=2, alphabet=None):
|
|
1349
|
+
r"""
|
|
1350
|
+
Return a random word of length `n` over the given `m`-letter
|
|
1351
|
+
alphabet.
|
|
1352
|
+
|
|
1353
|
+
INPUT:
|
|
1354
|
+
|
|
1355
|
+
- ``n`` -- integer; the length of the word
|
|
1356
|
+
- ``m`` -- integer (default: 2); the size of the output alphabet
|
|
1357
|
+
- ``alphabet`` -- (default: `\{0,1,...,m-1\}`) any container of
|
|
1358
|
+
length m that is suitable to build an instance of
|
|
1359
|
+
OrderedAlphabet (``list``, ``tuple``, ``str``, ...)
|
|
1360
|
+
|
|
1361
|
+
EXAMPLES::
|
|
1362
|
+
|
|
1363
|
+
sage: words.RandomWord(10) # random results
|
|
1364
|
+
word: 0110100101
|
|
1365
|
+
sage: words.RandomWord(10, 4) # random results
|
|
1366
|
+
word: 0322313320
|
|
1367
|
+
sage: words.RandomWord(100, 7) # random results
|
|
1368
|
+
word: 2630644023642516442650025611300034413310...
|
|
1369
|
+
sage: words.RandomWord(100, 7, range(-3,4)) # random results
|
|
1370
|
+
word: 1,3,-1,-1,3,2,2,0,1,-2,1,-1,-3,-2,2,0,3,0,-3,0,3,0,-2,-2,2,0,1,-3,2,-2,-2,2,0,2,1,-2,-3,-2,-1,0,...
|
|
1371
|
+
sage: words.RandomWord(100, 5, "abcde") # random results
|
|
1372
|
+
word: acebeaaccdbedbbbdeadeebbdeeebeaaacbadaac...
|
|
1373
|
+
sage: words.RandomWord(17, 5, "abcde") # random results
|
|
1374
|
+
word: dcacbbecbddebaadd
|
|
1375
|
+
|
|
1376
|
+
TESTS::
|
|
1377
|
+
|
|
1378
|
+
sage: words.RandomWord(2,3,"abcd")
|
|
1379
|
+
Traceback (most recent call last):
|
|
1380
|
+
...
|
|
1381
|
+
TypeError: alphabet does not contain 3 distinct elements
|
|
1382
|
+
"""
|
|
1383
|
+
if alphabet is None:
|
|
1384
|
+
alphabet = list(range(m))
|
|
1385
|
+
if len(set(alphabet)) != m:
|
|
1386
|
+
raise TypeError("alphabet does not contain %s distinct elements" % m)
|
|
1387
|
+
return FiniteWords(alphabet)([alphabet[randint(0,m-1)] for i in range(n)])
|
|
1388
|
+
|
|
1389
|
+
LowerChristoffelWord = LowerChristoffelWord
|
|
1390
|
+
|
|
1391
|
+
ChristoffelWord = LowerChristoffelWord
|
|
1392
|
+
|
|
1393
|
+
def UpperChristoffelWord(self, p, q, alphabet=(0, 1)):
|
|
1394
|
+
r"""
|
|
1395
|
+
Return the upper Christoffel word of slope `p/q`, where
|
|
1396
|
+
`p` and `q` are relatively prime nonnegative
|
|
1397
|
+
integers, over the given alphabet.
|
|
1398
|
+
|
|
1399
|
+
The *upper Christoffel word of slope `p/q`* is equal to the
|
|
1400
|
+
reversal of the lower Christoffel word of slope `p/q`.
|
|
1401
|
+
Equivalently, if `xuy` is the lower Christoffel word of
|
|
1402
|
+
slope `p/q`, where `x` and `y` are letters,
|
|
1403
|
+
then `yux` is the upper Christoffel word of slope
|
|
1404
|
+
`p/q` (because `u` is a palindrome).
|
|
1405
|
+
|
|
1406
|
+
INPUT:
|
|
1407
|
+
|
|
1408
|
+
- ``alphabet`` -- any container of length two that is suitable to build
|
|
1409
|
+
an instance of OrderedAlphabet (``list``, ``tuple``, ``str``, ...)
|
|
1410
|
+
|
|
1411
|
+
EXAMPLES::
|
|
1412
|
+
|
|
1413
|
+
sage: words.UpperChristoffelWord(1,0)
|
|
1414
|
+
word: 1
|
|
1415
|
+
|
|
1416
|
+
::
|
|
1417
|
+
|
|
1418
|
+
sage: words.UpperChristoffelWord(0,1)
|
|
1419
|
+
word: 0
|
|
1420
|
+
|
|
1421
|
+
::
|
|
1422
|
+
|
|
1423
|
+
sage: words.UpperChristoffelWord(1,1)
|
|
1424
|
+
word: 10
|
|
1425
|
+
|
|
1426
|
+
::
|
|
1427
|
+
|
|
1428
|
+
sage: words.UpperChristoffelWord(4,7)
|
|
1429
|
+
word: 10100100100
|
|
1430
|
+
|
|
1431
|
+
TESTS::
|
|
1432
|
+
|
|
1433
|
+
sage: words.UpperChristoffelWord(51,43,"abc")
|
|
1434
|
+
Traceback (most recent call last):
|
|
1435
|
+
...
|
|
1436
|
+
ValueError: alphabet must contain exactly two distinct elements
|
|
1437
|
+
"""
|
|
1438
|
+
w = words.LowerChristoffelWord(p, q, alphabet=alphabet).reversal()
|
|
1439
|
+
return w
|
|
1440
|
+
|
|
1441
|
+
@cached_method
|
|
1442
|
+
def _fibonacci_tile(self, n, q_0=None, q_1=3):
|
|
1443
|
+
r"""
|
|
1444
|
+
Return the word `q_n` defined by the recurrence below.
|
|
1445
|
+
|
|
1446
|
+
The sequence `(q_n)_{n\in\NN}` is defined by `q_0=\varepsilon`,
|
|
1447
|
+
`q_1=3` and
|
|
1448
|
+
|
|
1449
|
+
.. MATH::
|
|
1450
|
+
|
|
1451
|
+
q_n = \begin{cases}
|
|
1452
|
+
q_{n-1}q_{n-2} & \text{if} n\equiv 2 \mod 3, \\
|
|
1453
|
+
q_{n-1}\bar{q_{n-2}} & \text{if} n\equiv 0,1 \mod 3.
|
|
1454
|
+
\end{cases}
|
|
1455
|
+
|
|
1456
|
+
where the operator `\bar{\,}` exchanges the `1` and `3`.
|
|
1457
|
+
|
|
1458
|
+
INPUT:
|
|
1459
|
+
|
|
1460
|
+
- ``n`` -- nonnegative integer
|
|
1461
|
+
- ``q_0`` -- first initial value (default: ``None``); it can be
|
|
1462
|
+
``None``, 0, 1, 2 or 3
|
|
1463
|
+
- ``q_1`` -- second initial value (default: 3); it can be ``None``, 0,
|
|
1464
|
+
1, 2 or 3
|
|
1465
|
+
|
|
1466
|
+
EXAMPLES::
|
|
1467
|
+
|
|
1468
|
+
sage: for i in range(10): words._fibonacci_tile(i)
|
|
1469
|
+
word:
|
|
1470
|
+
word: 3
|
|
1471
|
+
word: 3
|
|
1472
|
+
word: 31
|
|
1473
|
+
word: 311
|
|
1474
|
+
word: 31131
|
|
1475
|
+
word: 31131133
|
|
1476
|
+
word: 3113113313313
|
|
1477
|
+
word: 311311331331331131133
|
|
1478
|
+
word: 3113113313313311311331331331131131
|
|
1479
|
+
|
|
1480
|
+
REFERENCES:
|
|
1481
|
+
|
|
1482
|
+
[BmBGL09]_
|
|
1483
|
+
"""
|
|
1484
|
+
from sage.combinat.words.morphism import WordMorphism
|
|
1485
|
+
W = FiniteWords([0,1,2,3])
|
|
1486
|
+
bar = WordMorphism({0:0,1:3,3:1,2:2},codomain=W)
|
|
1487
|
+
if n == 0:
|
|
1488
|
+
a = [] if q_0 is None else [q_0]
|
|
1489
|
+
return W(a)
|
|
1490
|
+
elif n == 1:
|
|
1491
|
+
b = [] if q_1 is None else [q_1]
|
|
1492
|
+
return W(b)
|
|
1493
|
+
elif n % 3 == 2:
|
|
1494
|
+
u = self._fibonacci_tile(n-1,q_0,q_1)
|
|
1495
|
+
v = self._fibonacci_tile(n-2,q_0,q_1)
|
|
1496
|
+
return u * v
|
|
1497
|
+
else:
|
|
1498
|
+
u = self._fibonacci_tile(n-1,q_0,q_1)
|
|
1499
|
+
v = bar(self._fibonacci_tile(n-2,q_0,q_1))
|
|
1500
|
+
return u * v
|
|
1501
|
+
|
|
1502
|
+
def fibonacci_tile(self, n):
|
|
1503
|
+
r"""
|
|
1504
|
+
Return the `n`-th Fibonacci Tile [BmBGL09]_.
|
|
1505
|
+
|
|
1506
|
+
EXAMPLES::
|
|
1507
|
+
|
|
1508
|
+
sage: for i in range(3): words.fibonacci_tile(i) # needs sage.modules
|
|
1509
|
+
Path: 3210
|
|
1510
|
+
Path: 323030101212
|
|
1511
|
+
Path: 3230301030323212323032321210121232121010...
|
|
1512
|
+
"""
|
|
1513
|
+
w = self._fibonacci_tile(3*n+1)
|
|
1514
|
+
w = w**4
|
|
1515
|
+
from sage.combinat.words.paths import WordPaths
|
|
1516
|
+
P = WordPaths([0,1,2,3])
|
|
1517
|
+
l = list(w.partial_sums(start=3,mod=4))
|
|
1518
|
+
return P(l)[:-1]
|
|
1519
|
+
|
|
1520
|
+
def dual_fibonacci_tile(self, n):
|
|
1521
|
+
r"""
|
|
1522
|
+
Return the `n`-th dual Fibonacci Tile [BmBGL09]_.
|
|
1523
|
+
|
|
1524
|
+
EXAMPLES::
|
|
1525
|
+
|
|
1526
|
+
sage: for i in range(4): words.dual_fibonacci_tile(i) # needs sage.modules
|
|
1527
|
+
Path: 3210
|
|
1528
|
+
Path: 32123032301030121012
|
|
1529
|
+
Path: 3212303230103230321232101232123032123210...
|
|
1530
|
+
Path: 3212303230103230321232101232123032123210...
|
|
1531
|
+
"""
|
|
1532
|
+
w = self._fibonacci_tile(3*n+1,3,3)
|
|
1533
|
+
w = w**4
|
|
1534
|
+
from sage.combinat.words.paths import WordPaths
|
|
1535
|
+
P = WordPaths([0,1,2,3])
|
|
1536
|
+
l = list(w.partial_sums(start=3,mod=4))
|
|
1537
|
+
return P(l)[:-1]
|
|
1538
|
+
|
|
1539
|
+
def _s_adic_iterator(self, sequence, letters):
|
|
1540
|
+
r"""
|
|
1541
|
+
Return the iterator over the `s`-adic infinite word obtained from a
|
|
1542
|
+
sequence of morphisms applied on letters where the hypothesis of
|
|
1543
|
+
nested prefixes is used.
|
|
1544
|
+
|
|
1545
|
+
DEFINITION (from [Fogg]_):
|
|
1546
|
+
|
|
1547
|
+
Let `w` be a infinite word over an alphabet `A = A_0`. A
|
|
1548
|
+
standard representation of `w` is obtained from a sequence of
|
|
1549
|
+
substitutions `\sigma_k : A_{k+1} \to A_k` and a sequence of letters
|
|
1550
|
+
`a_k \in A_k` such that:
|
|
1551
|
+
|
|
1552
|
+
.. MATH::
|
|
1553
|
+
|
|
1554
|
+
\lim_{k\to\infty} \sigma_0 \circ \sigma_1 \circ \cdots
|
|
1555
|
+
\sigma_k(a_k).
|
|
1556
|
+
|
|
1557
|
+
Given a set of substitutions `S`, we say that the representation is
|
|
1558
|
+
`S`-adic standard if the substitutions are chosen in `S`.
|
|
1559
|
+
|
|
1560
|
+
INPUT:
|
|
1561
|
+
|
|
1562
|
+
- ``sequence`` -- an iterable sequence of morphisms. It may be finite
|
|
1563
|
+
or infinite
|
|
1564
|
+
- ``letters`` -- an iterable sequence of letters. The image of the
|
|
1565
|
+
(i+1)-th letter under the (i+1)-th morphism must start with the i-th
|
|
1566
|
+
letter
|
|
1567
|
+
|
|
1568
|
+
OUTPUT: iterator of letters
|
|
1569
|
+
|
|
1570
|
+
EXAMPLES:
|
|
1571
|
+
|
|
1572
|
+
Let's define three morphisms and compute the first nested successive
|
|
1573
|
+
prefixes of the `s`-adic word::
|
|
1574
|
+
|
|
1575
|
+
sage: m1 = WordMorphism('e->gh,f->hg')
|
|
1576
|
+
sage: m2 = WordMorphism('c->ef,d->e')
|
|
1577
|
+
sage: m3 = WordMorphism('a->cd,b->dc')
|
|
1578
|
+
sage: Word(words._s_adic_iterator([m1],'e'))
|
|
1579
|
+
word: gh
|
|
1580
|
+
sage: Word(words._s_adic_iterator([m1,m2],'ec'))
|
|
1581
|
+
word: ghhg
|
|
1582
|
+
sage: Word(words._s_adic_iterator([m1,m2,m3],'eca'))
|
|
1583
|
+
word: ghhggh
|
|
1584
|
+
|
|
1585
|
+
If the letters don't satisfy the hypothesis of the algorithm, an
|
|
1586
|
+
error is raised::
|
|
1587
|
+
|
|
1588
|
+
sage: Word(words._s_adic_iterator([m1,m2,m3],'ecb'))
|
|
1589
|
+
Traceback (most recent call last):
|
|
1590
|
+
...
|
|
1591
|
+
ValueError: the hypothesis of the algorithm used is not satisfied; the image of the 3-th letter (=b) under the 3-th morphism (=a->cd, b->dc) should start with the 2-th letter (=c)
|
|
1592
|
+
|
|
1593
|
+
Two examples of infinite `s`-adic words::
|
|
1594
|
+
|
|
1595
|
+
sage: tm = WordMorphism('a->ab,b->ba')
|
|
1596
|
+
sage: fib = WordMorphism('a->ab,b->a')
|
|
1597
|
+
sage: from itertools import repeat
|
|
1598
|
+
sage: Word(words._s_adic_iterator(repeat(tm),repeat('a')))
|
|
1599
|
+
word: abbabaabbaababbabaababbaabbabaabbaababba...
|
|
1600
|
+
sage: Word(words._s_adic_iterator(repeat(fib),repeat('a')))
|
|
1601
|
+
word: abaababaabaababaababaabaababaabaababaaba...
|
|
1602
|
+
|
|
1603
|
+
A less trivial infinite `s`-adic word::
|
|
1604
|
+
|
|
1605
|
+
sage: D = {4:tm,5:fib}
|
|
1606
|
+
sage: tmword = words.ThueMorseWord([4,5])
|
|
1607
|
+
sage: it = (D[a] for a in tmword)
|
|
1608
|
+
sage: Word(words._s_adic_iterator(it, repeat('a')))
|
|
1609
|
+
word: abbaababbaabbaabbaababbaababbaabbaababba...
|
|
1610
|
+
|
|
1611
|
+
The morphism `\sigma: a \mapsto ba, b \mapsto b` cannot satisfy the
|
|
1612
|
+
hypothesis of the algorithm (nested prefixes)::
|
|
1613
|
+
|
|
1614
|
+
sage: sigma = WordMorphism('a->ba,b->b')
|
|
1615
|
+
sage: Word(words._s_adic_iterator(repeat(sigma),repeat('a')))
|
|
1616
|
+
Traceback (most recent call last):
|
|
1617
|
+
...
|
|
1618
|
+
ValueError: the hypothesis of the algorithm used is not satisfied; the image of the 2-th letter (=a) under the 2-th morphism (=a->ba, b->b) should start with the 1-th letter (=a)
|
|
1619
|
+
|
|
1620
|
+
AUTHORS:
|
|
1621
|
+
|
|
1622
|
+
- Sébastien Labbé (2009-12-18): initial version
|
|
1623
|
+
"""
|
|
1624
|
+
from itertools import tee
|
|
1625
|
+
sequence_it,sequence = tee(sequence)
|
|
1626
|
+
m = next(sequence_it)
|
|
1627
|
+
codomain = m.codomain()
|
|
1628
|
+
p = codomain.identity_morphism()
|
|
1629
|
+
letters_it,letters = tee(letters)
|
|
1630
|
+
precedent_letter = m(next(letters_it))[0]
|
|
1631
|
+
|
|
1632
|
+
yield precedent_letter
|
|
1633
|
+
for (i,(m,a)) in enumerate(zip(sequence, letters)):
|
|
1634
|
+
if not precedent_letter == m(a)[0]:
|
|
1635
|
+
raise ValueError("the hypothesis of the algorithm used is not satisfied; the image of the %s-th letter (=%s) under the %s-th morphism (=%s) should start with the %s-th letter (=%s)" % (i+1,a,i+1,m,i,precedent_letter))
|
|
1636
|
+
w = p(m(a)[1:])
|
|
1637
|
+
yield from w
|
|
1638
|
+
p = p * m
|
|
1639
|
+
precedent_letter = a
|
|
1640
|
+
|
|
1641
|
+
def s_adic(self, sequence, letters, morphisms=None):
|
|
1642
|
+
r"""
|
|
1643
|
+
Return the `s`-adic infinite word obtained from a sequence of
|
|
1644
|
+
morphisms applied on a letter.
|
|
1645
|
+
|
|
1646
|
+
DEFINITION (from [Fogg]_):
|
|
1647
|
+
|
|
1648
|
+
Let `w` be a infinite word over an alphabet `A = A_0`. A
|
|
1649
|
+
standard representation of `w` is obtained from a sequence of
|
|
1650
|
+
substitutions `\sigma_k : A_{k+1} \to A_k` and a sequence of letters
|
|
1651
|
+
`a_k \in A_k` such that:
|
|
1652
|
+
|
|
1653
|
+
.. MATH::
|
|
1654
|
+
|
|
1655
|
+
\lim_{k\to\infty} \sigma_0 \circ \sigma_1 \circ \cdots
|
|
1656
|
+
\sigma_k(a_k).
|
|
1657
|
+
|
|
1658
|
+
Given a set of substitutions `S`, we say that the representation is
|
|
1659
|
+
`S`-adic standard if the substitutions are chosen in `S`.
|
|
1660
|
+
|
|
1661
|
+
INPUT:
|
|
1662
|
+
|
|
1663
|
+
- ``sequence`` -- an iterable sequence of indices or of morphisms. It
|
|
1664
|
+
may be finite or infinite. If ``sequence`` is infinite, the image
|
|
1665
|
+
of the `(i+1)`-th letter under the `(i+1)`-th morphism must start
|
|
1666
|
+
with the `i`-th letter.
|
|
1667
|
+
|
|
1668
|
+
- ``letters`` -- a letter or a sequence of letters
|
|
1669
|
+
|
|
1670
|
+
- ``morphisms`` -- dict, list, callable or ``None`` (default:
|
|
1671
|
+
``None``) an object that maps indices to morphisms. If ``None``, then
|
|
1672
|
+
``sequence`` must consist of morphisms.
|
|
1673
|
+
|
|
1674
|
+
OUTPUT: a word
|
|
1675
|
+
|
|
1676
|
+
EXAMPLES:
|
|
1677
|
+
|
|
1678
|
+
Let us define three morphisms and compute the first nested successive
|
|
1679
|
+
prefixes of the `s`-adic word::
|
|
1680
|
+
|
|
1681
|
+
sage: m1 = WordMorphism('e->gh,f->hg')
|
|
1682
|
+
sage: m2 = WordMorphism('c->ef,d->e')
|
|
1683
|
+
sage: m3 = WordMorphism('a->cd,b->dc')
|
|
1684
|
+
sage: words.s_adic([m1],'e')
|
|
1685
|
+
word: gh
|
|
1686
|
+
sage: words.s_adic([m1,m2],'ec')
|
|
1687
|
+
word: ghhg
|
|
1688
|
+
sage: words.s_adic([m1,m2,m3],'eca')
|
|
1689
|
+
word: ghhggh
|
|
1690
|
+
|
|
1691
|
+
When the given sequence of morphism is finite, one may simply give
|
|
1692
|
+
the last letter, i.e. ``'a'``, instead of giving all of them,
|
|
1693
|
+
i.e. ``'eca'``::
|
|
1694
|
+
|
|
1695
|
+
sage: words.s_adic([m1,m2,m3],'a')
|
|
1696
|
+
word: ghhggh
|
|
1697
|
+
sage: words.s_adic([m1,m2,m3],'b')
|
|
1698
|
+
word: ghghhg
|
|
1699
|
+
|
|
1700
|
+
If the letters don't satisfy the hypothesis of the algorithm
|
|
1701
|
+
(nested prefixes), an error is raised::
|
|
1702
|
+
|
|
1703
|
+
sage: words.s_adic([m1,m2,m3],'ecb')
|
|
1704
|
+
Traceback (most recent call last):
|
|
1705
|
+
...
|
|
1706
|
+
ValueError: the hypothesis of the algorithm used is not satisfied; the image of the 3-th letter (=b) under the 3-th morphism (=a->cd, b->dc) should start with the 2-th letter (=c)
|
|
1707
|
+
|
|
1708
|
+
Let's define the Thue-Morse morphism and the Fibonacci morphism
|
|
1709
|
+
which will be used below to illustrate more examples and let's import
|
|
1710
|
+
the ``repeat`` tool from the ``itertools``::
|
|
1711
|
+
|
|
1712
|
+
sage: tm = WordMorphism('a->ab,b->ba')
|
|
1713
|
+
sage: fib = WordMorphism('a->ab,b->a')
|
|
1714
|
+
sage: from itertools import repeat
|
|
1715
|
+
|
|
1716
|
+
Two trivial examples of infinite `s`-adic words::
|
|
1717
|
+
|
|
1718
|
+
sage: words.s_adic(repeat(tm),repeat('a'))
|
|
1719
|
+
word: abbabaabbaababbabaababbaabbabaabbaababba...
|
|
1720
|
+
|
|
1721
|
+
::
|
|
1722
|
+
|
|
1723
|
+
sage: words.s_adic(repeat(fib),repeat('a'))
|
|
1724
|
+
word: abaababaabaababaababaabaababaabaababaaba...
|
|
1725
|
+
|
|
1726
|
+
A less trivial infinite `s`-adic word::
|
|
1727
|
+
|
|
1728
|
+
sage: D = {4:tm,5:fib}
|
|
1729
|
+
sage: tmword = words.ThueMorseWord([4,5])
|
|
1730
|
+
sage: it = (D[a] for a in tmword)
|
|
1731
|
+
sage: words.s_adic(it, repeat('a'))
|
|
1732
|
+
word: abbaababbaabbaabbaababbaababbaabbaababba...
|
|
1733
|
+
|
|
1734
|
+
The same thing using a sequence of indices::
|
|
1735
|
+
|
|
1736
|
+
sage: tmword = words.ThueMorseWord([0,1])
|
|
1737
|
+
sage: words.s_adic(tmword, repeat('a'), [tm,fib])
|
|
1738
|
+
word: abbaababbaabbaabbaababbaababbaabbaababba...
|
|
1739
|
+
|
|
1740
|
+
The correspondence of the indices may be given as a dict::
|
|
1741
|
+
|
|
1742
|
+
sage: words.s_adic(tmword, repeat('a'), {0:tm,1:fib})
|
|
1743
|
+
word: abbaababbaabbaabbaababbaababbaabbaababba...
|
|
1744
|
+
|
|
1745
|
+
because dict are more versatile for indices::
|
|
1746
|
+
|
|
1747
|
+
sage: tmwordTF = words.ThueMorseWord('TF')
|
|
1748
|
+
sage: words.s_adic(tmwordTF, repeat('a'), {'T':tm,'F':fib})
|
|
1749
|
+
word: abbaababbaabbaabbaababbaababbaabbaababba...
|
|
1750
|
+
|
|
1751
|
+
or by a callable::
|
|
1752
|
+
|
|
1753
|
+
sage: f = lambda n: tm if n == 0 else fib
|
|
1754
|
+
sage: words.s_adic(words.ThueMorseWord(), repeat('a'), f)
|
|
1755
|
+
word: abbaababbaabbaabbaababbaababbaabbaababba...
|
|
1756
|
+
|
|
1757
|
+
Random infinite `s`-adic words::
|
|
1758
|
+
|
|
1759
|
+
sage: from sage.misc.prandom import randint
|
|
1760
|
+
sage: def it():
|
|
1761
|
+
....: while True: yield randint(0,1)
|
|
1762
|
+
sage: words.s_adic(it(), repeat('a'), [tm,fib]) # random
|
|
1763
|
+
word: abbaabababbaababbaabbaababbaabababbaabba...
|
|
1764
|
+
sage: words.s_adic(it(), repeat('a'), [tm,fib]) # random
|
|
1765
|
+
word: abbaababbaabbaababbaababbaabbaababbaabba...
|
|
1766
|
+
sage: words.s_adic(it(), repeat('a'), [tm,fib]) # random
|
|
1767
|
+
word: abaaababaabaabaaababaabaaababaaababaabaa...
|
|
1768
|
+
|
|
1769
|
+
An example where the sequences cycle on two morphisms and two
|
|
1770
|
+
letters::
|
|
1771
|
+
|
|
1772
|
+
sage: G = WordMorphism('a->cd,b->dc')
|
|
1773
|
+
sage: H = WordMorphism('c->ab,d->ba')
|
|
1774
|
+
sage: from itertools import cycle
|
|
1775
|
+
sage: words.s_adic([G,H],'ac')
|
|
1776
|
+
word: cddc
|
|
1777
|
+
sage: words.s_adic(cycle([G,H]),cycle('ac'))
|
|
1778
|
+
word: cddcdccddccdcddcdccdcddccddcdccddccdcddc...
|
|
1779
|
+
|
|
1780
|
+
The morphism `\sigma: a\mapsto ba, b\mapsto b` can't satisfy the
|
|
1781
|
+
hypothesis of the nested prefixes, but one may compute arbitrarily
|
|
1782
|
+
long finite words having the limit `\sigma^\omega(a)`::
|
|
1783
|
+
|
|
1784
|
+
sage: sigma = WordMorphism('a->ba,b->b')
|
|
1785
|
+
sage: words.s_adic(repeat(sigma),repeat('a'))
|
|
1786
|
+
Traceback (most recent call last):
|
|
1787
|
+
...
|
|
1788
|
+
ValueError: the hypothesis of the algorithm used is not satisfied; the image of the 2-th letter (=a) under the 2-th morphism (=a->ba, b->b) should start with the 1-th letter (=a)
|
|
1789
|
+
sage: words.s_adic([sigma],'a')
|
|
1790
|
+
word: ba
|
|
1791
|
+
sage: words.s_adic([sigma,sigma],'a')
|
|
1792
|
+
word: bba
|
|
1793
|
+
sage: words.s_adic([sigma]*3,'a')
|
|
1794
|
+
word: bbba
|
|
1795
|
+
sage: words.s_adic([sigma]*4,'a')
|
|
1796
|
+
word: bbbba
|
|
1797
|
+
sage: words.s_adic([sigma]*5,'a')
|
|
1798
|
+
word: bbbbba
|
|
1799
|
+
sage: words.s_adic([sigma]*6,'a')
|
|
1800
|
+
word: bbbbbba
|
|
1801
|
+
sage: words.s_adic([sigma]*7,'a')
|
|
1802
|
+
word: bbbbbbba
|
|
1803
|
+
|
|
1804
|
+
The following examples illustrates an `S`-adic word defined over an
|
|
1805
|
+
infinite set `S` of morphisms `x_h`::
|
|
1806
|
+
|
|
1807
|
+
sage: x = lambda h:WordMorphism({1:[2],2:[3]+[1]*(h+1),3:[3]+[1]*h})
|
|
1808
|
+
sage: for h in [0,1,2,3]:
|
|
1809
|
+
....: print("{} {}".format(h, x(h)))
|
|
1810
|
+
0 1->2, 2->31, 3->3
|
|
1811
|
+
1 1->2, 2->311, 3->31
|
|
1812
|
+
2 1->2, 2->3111, 3->311
|
|
1813
|
+
3 1->2, 2->31111, 3->3111
|
|
1814
|
+
sage: w = Word(lambda n : valuation(n+1, 2) ); w
|
|
1815
|
+
word: 0102010301020104010201030102010501020103...
|
|
1816
|
+
sage: s = words.s_adic(w, repeat(3), x); s
|
|
1817
|
+
word: 3232232232322322322323223223232232232232...
|
|
1818
|
+
sage: prefixe = s[:10000]
|
|
1819
|
+
sage: list(map(prefixe.number_of_factors, range(15)))
|
|
1820
|
+
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
|
|
1821
|
+
sage: [_[i+1] - _[i] for i in range(len(_)-1)]
|
|
1822
|
+
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
|
1823
|
+
|
|
1824
|
+
TESTS::
|
|
1825
|
+
|
|
1826
|
+
sage: tm = WordMorphism('a->ab,b->ba')
|
|
1827
|
+
sage: fib = WordMorphism('a->ab,b->a')
|
|
1828
|
+
sage: w = words.s_adic([fib,tm,tm,fib,tm,fib]*3,'a')
|
|
1829
|
+
sage: w
|
|
1830
|
+
word: abaaabaababaabaaababaaababaaabaababaabaa...
|
|
1831
|
+
sage: w.length()
|
|
1832
|
+
32400
|
|
1833
|
+
sage: w.parent()
|
|
1834
|
+
Finite words over {'a', 'b'}
|
|
1835
|
+
sage: type(w)
|
|
1836
|
+
<class 'sage.combinat.words.word.FiniteWord_callable_with_caching'>
|
|
1837
|
+
|
|
1838
|
+
::
|
|
1839
|
+
|
|
1840
|
+
sage: words.s_adic([fib,tm,tm,fib,tm,fib],'aaaaaaa')
|
|
1841
|
+
word: abaaabaababaabaaababaaababaaabaababa
|
|
1842
|
+
|
|
1843
|
+
::
|
|
1844
|
+
|
|
1845
|
+
sage: words.s_adic([0,1,0,1,0,1,0,1],'a',[tm,fib])
|
|
1846
|
+
word: abbaabababbaabbaababbaababbaabababbaabba...
|
|
1847
|
+
|
|
1848
|
+
::
|
|
1849
|
+
|
|
1850
|
+
sage: words.s_adic([fib,fib],'bb')
|
|
1851
|
+
Traceback (most recent call last):
|
|
1852
|
+
...
|
|
1853
|
+
ValueError: the hypothesis of the algorithm used is not satisfied; the image of the 2-th letter (=b) under the 2-th morphism (=a->ab, b->a) should start with the 1-th letter (=b)
|
|
1854
|
+
|
|
1855
|
+
Test on different letters::
|
|
1856
|
+
|
|
1857
|
+
sage: tm = WordMorphism({0:[0,1], 1:[1,0]})
|
|
1858
|
+
sage: fib = WordMorphism({0:[0,1], 1:[0]})
|
|
1859
|
+
sage: f = lambda n: tm if n == 0 else fib
|
|
1860
|
+
sage: words.s_adic(words.ThueMorseWord(), repeat(0), f)
|
|
1861
|
+
word: 0110010110011001100101100101100110010110...
|
|
1862
|
+
|
|
1863
|
+
Testing the message error for the third argument::
|
|
1864
|
+
|
|
1865
|
+
sage: words.s_adic(words.ThueMorseWord(), repeat(0), 5)
|
|
1866
|
+
Traceback (most recent call last):
|
|
1867
|
+
...
|
|
1868
|
+
TypeError: morphisms (=5) must be None, callable or provide a __getitem__ method
|
|
1869
|
+
|
|
1870
|
+
AUTHORS:
|
|
1871
|
+
|
|
1872
|
+
- Sébastien Labbé (2009-12-18): initial version
|
|
1873
|
+
"""
|
|
1874
|
+
if morphisms is None:
|
|
1875
|
+
seq = sequence
|
|
1876
|
+
elif hasattr(morphisms, '__getitem__'):
|
|
1877
|
+
seq = (morphisms[i] for i in sequence)
|
|
1878
|
+
elif callable(morphisms):
|
|
1879
|
+
seq = (morphisms(i) for i in sequence)
|
|
1880
|
+
else:
|
|
1881
|
+
raise TypeError("morphisms (=%s) must be None, callable or provide a __getitem__ method" % morphisms)
|
|
1882
|
+
|
|
1883
|
+
from sage.combinat.words.word import FiniteWord_class
|
|
1884
|
+
if isinstance(sequence,(tuple,list,str,FiniteWord_class)) \
|
|
1885
|
+
and hasattr(letters, "__len__") and len(letters) == 1:
|
|
1886
|
+
from sage.misc.misc_c import prod
|
|
1887
|
+
return prod(seq)(letters)
|
|
1888
|
+
|
|
1889
|
+
from itertools import tee
|
|
1890
|
+
seq_it, seq = tee(seq)
|
|
1891
|
+
m = next(seq_it)
|
|
1892
|
+
W = m.codomain()
|
|
1893
|
+
|
|
1894
|
+
kwds = {}
|
|
1895
|
+
kwds['data'] = self._s_adic_iterator(seq, letters)
|
|
1896
|
+
kwds['datatype'] = 'iter'
|
|
1897
|
+
kwds['caching'] = True
|
|
1898
|
+
#kwds['check'] = False
|
|
1899
|
+
return W.shift()(**kwds)
|
|
1900
|
+
|
|
1901
|
+
def PalindromicDefectWord(self, k=1, alphabet='ab'):
|
|
1902
|
+
r"""
|
|
1903
|
+
Return the finite word `w = a b^k a b^{k-1} a a b^{k-1} a b^{k} a`.
|
|
1904
|
+
|
|
1905
|
+
As described by Brlek, Hamel, Nivat and Reutenauer in [BHNR2004]_, this
|
|
1906
|
+
finite word `w` is such that the infinite periodic word `w^{\omega}`
|
|
1907
|
+
has palindromic defect ``k``.
|
|
1908
|
+
|
|
1909
|
+
INPUT:
|
|
1910
|
+
|
|
1911
|
+
- ``k`` -- positive integer (default: 1)
|
|
1912
|
+
|
|
1913
|
+
- ``alphabet`` -- iterable of size two (default: ``'ab'``)
|
|
1914
|
+
|
|
1915
|
+
OUTPUT: finite word
|
|
1916
|
+
|
|
1917
|
+
EXAMPLES::
|
|
1918
|
+
|
|
1919
|
+
sage: words.PalindromicDefectWord(10)
|
|
1920
|
+
word: abbbbbbbbbbabbbbbbbbbaabbbbbbbbbabbbbbbb...
|
|
1921
|
+
|
|
1922
|
+
::
|
|
1923
|
+
|
|
1924
|
+
sage: w = words.PalindromicDefectWord(3)
|
|
1925
|
+
sage: w
|
|
1926
|
+
word: abbbabbaabbabbba
|
|
1927
|
+
sage: w.defect()
|
|
1928
|
+
0
|
|
1929
|
+
sage: (w^2).defect()
|
|
1930
|
+
3
|
|
1931
|
+
sage: (w^3).defect()
|
|
1932
|
+
3
|
|
1933
|
+
|
|
1934
|
+
On other alphabets::
|
|
1935
|
+
|
|
1936
|
+
sage: words.PalindromicDefectWord(3, alphabet='cd')
|
|
1937
|
+
word: cdddcddccddcdddc
|
|
1938
|
+
sage: words.PalindromicDefectWord(3, alphabet=['c', 3])
|
|
1939
|
+
word: c333c33cc33c333c
|
|
1940
|
+
|
|
1941
|
+
TESTS::
|
|
1942
|
+
|
|
1943
|
+
sage: k = 25
|
|
1944
|
+
sage: (words.PalindromicDefectWord(k)^2).defect()
|
|
1945
|
+
25
|
|
1946
|
+
|
|
1947
|
+
If k is negative or zero, then we get the same word::
|
|
1948
|
+
|
|
1949
|
+
sage: words.PalindromicDefectWord(0)
|
|
1950
|
+
word: aaaaaa
|
|
1951
|
+
sage: words.PalindromicDefectWord(-3)
|
|
1952
|
+
word: aaaaaa
|
|
1953
|
+
"""
|
|
1954
|
+
kk = k-1
|
|
1955
|
+
a, b = alphabet
|
|
1956
|
+
if not (isinstance(a, str) and isinstance(b, str)):
|
|
1957
|
+
a, b = (a,), (b,)
|
|
1958
|
+
w = a + b*k + a + b*kk + a + a + b*kk + a + b*k + a
|
|
1959
|
+
return FiniteWords(alphabet)(w)
|
|
1960
|
+
|
|
1961
|
+
def BaumSweetWord(self):
|
|
1962
|
+
r"""
|
|
1963
|
+
Return the Baum-Sweet Word.
|
|
1964
|
+
|
|
1965
|
+
The Baum-Sweet Sequence is an infinite word over the alphabet `\{0,1\}`
|
|
1966
|
+
defined by the following string substitution rules:
|
|
1967
|
+
|
|
1968
|
+
`00 \rightarrow 0000`
|
|
1969
|
+
|
|
1970
|
+
`01 \rightarrow 1001`
|
|
1971
|
+
|
|
1972
|
+
`10 \rightarrow 0100`
|
|
1973
|
+
|
|
1974
|
+
`11 \rightarrow 1101`
|
|
1975
|
+
|
|
1976
|
+
The substitution rule above can be considered as a morphism on the
|
|
1977
|
+
submonoid of `\{0,1\}` generated by `\{00,01,10,11\}` (which is a free
|
|
1978
|
+
monoid on these generators).
|
|
1979
|
+
|
|
1980
|
+
It is also defined as the concatenation of the terms from the Baum-Sweet
|
|
1981
|
+
Sequence:
|
|
1982
|
+
|
|
1983
|
+
.. MATH::
|
|
1984
|
+
|
|
1985
|
+
b_n = \begin{cases}
|
|
1986
|
+
0, & \text{if } n = 0 \\
|
|
1987
|
+
1, & \text{if } m \text{ is even} \\
|
|
1988
|
+
b_{\frac{m-1}{2}}, & \text{if } m \text{ is odd}
|
|
1989
|
+
\end{cases}
|
|
1990
|
+
|
|
1991
|
+
where `n=m4^k` and `m` is not divisible by 4 if `m \neq 0`.
|
|
1992
|
+
|
|
1993
|
+
The individual terms of the Baum-Sweet Sequence are also given by:
|
|
1994
|
+
|
|
1995
|
+
.. MATH::
|
|
1996
|
+
|
|
1997
|
+
b_n = \begin{cases}
|
|
1998
|
+
1, & \text{if the binary representation of} n \text{ contains no block of consecutive 0s of odd length}\\
|
|
1999
|
+
0, & \text{otherwise}\\
|
|
2000
|
+
\end{cases}\\
|
|
2001
|
+
|
|
2002
|
+
for `n > 0` with `b_0 = 1`.
|
|
2003
|
+
|
|
2004
|
+
For more information see:
|
|
2005
|
+
:wikipedia:`Baum-Sweet_sequence`.
|
|
2006
|
+
|
|
2007
|
+
EXAMPLES:
|
|
2008
|
+
|
|
2009
|
+
Baum-Sweet Word::
|
|
2010
|
+
|
|
2011
|
+
sage: w = words.BaumSweetWord(); w
|
|
2012
|
+
word: 1101100101001001100100000100100101001001...
|
|
2013
|
+
|
|
2014
|
+
Block Definition::
|
|
2015
|
+
|
|
2016
|
+
sage: w = words.BaumSweetWord()
|
|
2017
|
+
sage: f = lambda n: '1' if all(len(x)%2==0 for x in bin(n)[2:].split('1')) else '0'
|
|
2018
|
+
sage: all(f(i) == w[i] for i in range(1,100))
|
|
2019
|
+
True
|
|
2020
|
+
"""
|
|
2021
|
+
outer = WordMorphism('a->00,b->01,c->10,d->11')
|
|
2022
|
+
inner = WordMorphism('a->aa,b->cb,c->ba,d->db')
|
|
2023
|
+
return outer(inner.fixed_point('d'))
|
|
2024
|
+
|
|
2025
|
+
|
|
2026
|
+
words = WordGenerator()
|