passagemath-combinat 10.6.42__cp314-cp314t-win_amd64.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/DELVEWHEEL +2 -0
- passagemath_combinat-10.6.42.dist-info/METADATA +160 -0
- passagemath_combinat-10.6.42.dist-info/RECORD +401 -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-10-3a5f019e2510aeaad918cab2b57a689d.dll +0 -0
- passagemath_combinat.libs/libsymmetrica-3-7dcf900932804d0df5fd0919b4668720.dll +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 +44 -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.cp314t-win_amd64.pyd +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.cp314t-win_amd64.pyd +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.cp314t-win_amd64.pyd +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.cp314t-win_amd64.pyd +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.cp314t-win_amd64.pyd +0 -0
- sage/combinat/debruijn_sequence.pyx +355 -0
- sage/combinat/decorated_permutation.py +270 -0
- sage/combinat/degree_sequences.cp314t-win_amd64.pyd +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.cp314t-win_amd64.pyd +0 -0
- sage/combinat/expnums.pyx +148 -0
- sage/combinat/fast_vector_partitions.cp314t-win_amd64.pyd +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.cp314t-win_amd64.pyd +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.cp314t-win_amd64.pyd +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.cp314t-win_amd64.pyd +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.cp314t-win_amd64.pyd +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.cp314t-win_amd64.pyd +0 -0
- sage/combinat/words/word_char.pyx +847 -0
- sage/combinat/words/word_datatypes.cp314t-win_amd64.pyd +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.cp314t-win_amd64.pyd +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.cp314t-win_amd64.pyd +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.cp314t-win_amd64.pyd +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.cp314t-win_amd64.pyd +0 -0
- sage/sat/solvers/satsolver.pxd +3 -0
- sage/sat/solvers/satsolver.pyx +405 -0
|
@@ -0,0 +1,1249 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-combinat
|
|
2
|
+
r"""
|
|
3
|
+
Discrete dynamical systems
|
|
4
|
+
==========================
|
|
5
|
+
|
|
6
|
+
A *discrete dynamical system* (henceforth *DDS*) is a pair
|
|
7
|
+
`(X, \phi)` of a set `X` and a map `\phi : X \to X`.
|
|
8
|
+
(This is one of several things known as a "discrete dynamical
|
|
9
|
+
system" in mathematics.)
|
|
10
|
+
|
|
11
|
+
This file implements the following classes for discrete
|
|
12
|
+
dynamical systems:
|
|
13
|
+
|
|
14
|
+
- :class:`DiscreteDynamicalSystem`: general discrete dynamical
|
|
15
|
+
system, as above.
|
|
16
|
+
Inherit from this class if the ground set of your DDS is
|
|
17
|
+
infinite or large enough that you want to avoid it getting
|
|
18
|
+
stored as a list.
|
|
19
|
+
See the doc of this class for further details.
|
|
20
|
+
|
|
21
|
+
- :class:`FiniteDynamicalSystem`: finite discrete dynamical
|
|
22
|
+
system.
|
|
23
|
+
This can be instantiated by calling
|
|
24
|
+
:class:`DiscreteDynamicalSystem` with the parameter
|
|
25
|
+
``is_finite`` set to ``True``.
|
|
26
|
+
|
|
27
|
+
- :class:`InvertibleDiscreteDynamicalSystem`: invertible
|
|
28
|
+
discrete dynamical system.
|
|
29
|
+
This implements an ``inverse_evolution`` method for `\phi^{-1}`
|
|
30
|
+
(the default implementation simply applies `\phi` over and
|
|
31
|
+
over until the original value is revisited; the last value
|
|
32
|
+
before that is then taken to be the result).
|
|
33
|
+
This can be instantiated by calling
|
|
34
|
+
:class:`DiscreteDynamicalSystem` with the parameter
|
|
35
|
+
``inverse`` provided.
|
|
36
|
+
|
|
37
|
+
- :class:`InvertibleFiniteDynamicalSystem`: invertible
|
|
38
|
+
finite discrete dynamical system.
|
|
39
|
+
This can be instantiated by calling
|
|
40
|
+
:class:`DiscreteDynamicalSystem` with the parameter
|
|
41
|
+
``is_finite`` set to ``True`` and the parameter
|
|
42
|
+
``inverse`` provided.
|
|
43
|
+
|
|
44
|
+
.. TODO::
|
|
45
|
+
|
|
46
|
+
- Implement some more functionality for homomesy and
|
|
47
|
+
invariance testing:
|
|
48
|
+
Checking invariance on a sublist;
|
|
49
|
+
computing the first `k` entries of an orbit (useful
|
|
50
|
+
when orbits can be too large);
|
|
51
|
+
orbits_iterator (for when there are too many orbits to
|
|
52
|
+
list);
|
|
53
|
+
etc.
|
|
54
|
+
|
|
55
|
+
- Further examples for non-auto functionality: e.g.,
|
|
56
|
+
infection on a chessboard; Conway's game of life.
|
|
57
|
+
|
|
58
|
+
- Subclasses for DDSes whose ground set is an enumerated set.
|
|
59
|
+
Should we have those?
|
|
60
|
+
|
|
61
|
+
- Implement caching for orbits (can be useful: some DDSes
|
|
62
|
+
have a complicated evolution that shouldn't be recomputed
|
|
63
|
+
every time).
|
|
64
|
+
Does this require a whole new class?
|
|
65
|
+
|
|
66
|
+
- Further functionality for non-invertible DDSes:
|
|
67
|
+
is_recurrent, recurrent_entries, idempotent_power, etc.
|
|
68
|
+
|
|
69
|
+
- Wrap (some of) the cyclic_sieving_phenomenon.py methods
|
|
70
|
+
(:mod:`sage.combinat.cyclic_sieving_phenomenon`).
|
|
71
|
+
|
|
72
|
+
- Interact with sage.dynamics. This requires someone who
|
|
73
|
+
knows the latter part of the Sage library well.
|
|
74
|
+
"""
|
|
75
|
+
# ****************************************************************************
|
|
76
|
+
# Copyright (C) 2018 Darij Grinberg <darijgrinberg@gmail.com>,
|
|
77
|
+
# 2018 Tom Roby <tomrobyuconn@gmail.com>
|
|
78
|
+
#
|
|
79
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
80
|
+
#
|
|
81
|
+
# This code is distributed in the hope that it will be useful,
|
|
82
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
83
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
84
|
+
# General Public License for more details.
|
|
85
|
+
#
|
|
86
|
+
# The full text of the GPL is available at:
|
|
87
|
+
#
|
|
88
|
+
# https://www.gnu.org/licenses/
|
|
89
|
+
# ****************************************************************************
|
|
90
|
+
from sage.categories.sets_cat import Sets
|
|
91
|
+
from sage.structure.sage_object import SageObject
|
|
92
|
+
from sage.misc.classcall_metaclass import ClasscallMetaclass, typecall
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class DiscreteDynamicalSystem(SageObject, metaclass=ClasscallMetaclass):
|
|
96
|
+
r"""
|
|
97
|
+
A discrete dynamical system.
|
|
98
|
+
|
|
99
|
+
A *discrete dynamical system* (henceforth *DDS*) is a
|
|
100
|
+
pair `(X, \phi)` of a set `X` and a map `\phi : X \to X`.
|
|
101
|
+
The set `X` is called the *ground set* of the DDS;
|
|
102
|
+
the map `\phi` is called the *evolution* of the DDS;
|
|
103
|
+
the inverse map `\phi^{-1}` (when it exists) is called the
|
|
104
|
+
*inverse evolution* of the DDS.
|
|
105
|
+
|
|
106
|
+
The DDS is called *finite* if `X` is finite.
|
|
107
|
+
The DDS is called *invertible* if the map `\phi` is
|
|
108
|
+
invertible.
|
|
109
|
+
|
|
110
|
+
Given a DDS `(X, \phi)`, we can study
|
|
111
|
+
|
|
112
|
+
* its orbits (i.e., the lists
|
|
113
|
+
`(s, \phi(s), \phi^2(s), \phi^3(s), \ldots)` for `s \in X`),
|
|
114
|
+
|
|
115
|
+
* its invariants (i.e., maps `f : X \to Y` satisfying
|
|
116
|
+
`f \circ \phi = f`),
|
|
117
|
+
|
|
118
|
+
* its cycles (i.e., lists `(u_1, u_2, \ldots, u_k)` of elements
|
|
119
|
+
of `X` such that `\phi(u_i) = u_{i+1}` for each `i \leq k`,
|
|
120
|
+
where we set `u_{k+1} = u_1`),
|
|
121
|
+
|
|
122
|
+
* its homomesies (i.e., maps `h : X \to A` to a
|
|
123
|
+
`\QQ`-vector space `A` such that the average of the values
|
|
124
|
+
of `h` on each cycle is the same),
|
|
125
|
+
|
|
126
|
+
and various other features.
|
|
127
|
+
(Some of these require `X` to be finite or at least to have
|
|
128
|
+
finite orbits.)
|
|
129
|
+
|
|
130
|
+
INPUT:
|
|
131
|
+
|
|
132
|
+
- ``X`` -- set, list, tuple, or another iterable, or
|
|
133
|
+
``None`` (default: ``None``); the ground set for the DDS.
|
|
134
|
+
This can be ``None`` (in which case Sage will not know
|
|
135
|
+
the ground set, but can still apply evolution to any
|
|
136
|
+
elements that are provided to it).
|
|
137
|
+
Make sure to set the ``create_tuple`` argument to
|
|
138
|
+
``True`` if the ``X`` you provide is an iterator or
|
|
139
|
+
a list, as otherwise your ``X`` would be exposed
|
|
140
|
+
(and thus subject to mutation or exhaustion).
|
|
141
|
+
|
|
142
|
+
- ``phi`` -- function, or callable that acts like a
|
|
143
|
+
function; the evolution of the DDS
|
|
144
|
+
|
|
145
|
+
- ``cache_orbits`` -- boolean (default: ``False``);
|
|
146
|
+
whether or not the orbits should be cached once they
|
|
147
|
+
are computed.
|
|
148
|
+
This currently does nothing, as we are not caching
|
|
149
|
+
orbits yet.
|
|
150
|
+
|
|
151
|
+
- ``create_tuple`` -- boolean (default: ``False``);
|
|
152
|
+
whether or not the input ``X`` should be translated
|
|
153
|
+
into a tuple. Set this to ``True`` to prevent
|
|
154
|
+
mutation if ``X`` is a list, and to prevent
|
|
155
|
+
exhaustion if ``X`` is an iterator.
|
|
156
|
+
|
|
157
|
+
- ``inverse`` -- function, or callable that acts like a
|
|
158
|
+
function, or boolean or ``None`` (default: ``None``);
|
|
159
|
+
the inverse evolution of the DDS, if the DDS is
|
|
160
|
+
invertible. Set this to ``None`` or ``False``
|
|
161
|
+
if the DDS is not invertible (or you don't want Sage
|
|
162
|
+
to treat it as such).
|
|
163
|
+
Alternatively, by setting this argument to ``True``,
|
|
164
|
+
you can signal that the DDS is invertible
|
|
165
|
+
without providing the inverse evolution. (In this case,
|
|
166
|
+
Sage will compute the inverse, assuming the orbits
|
|
167
|
+
to be finite.)
|
|
168
|
+
|
|
169
|
+
- ``is_finite`` -- boolean or ``None`` (default: ``None``);
|
|
170
|
+
whether the DDS is finite. The default option ``None``
|
|
171
|
+
leaves this to Sage to decide.
|
|
172
|
+
Only set this to ``True`` if you provide the ground
|
|
173
|
+
set ``X``.
|
|
174
|
+
|
|
175
|
+
EXAMPLES:
|
|
176
|
+
|
|
177
|
+
The following discrete dynamical system is neither
|
|
178
|
+
finite nor invertible::
|
|
179
|
+
|
|
180
|
+
sage: D = DiscreteDynamicalSystem(NN, lambda x: x + 2)
|
|
181
|
+
sage: D.ground_set()
|
|
182
|
+
Non negative integer semiring
|
|
183
|
+
sage: D.evolution()(5)
|
|
184
|
+
7
|
|
185
|
+
sage: D.evolution_power(7)(5)
|
|
186
|
+
19
|
|
187
|
+
sage: D.evolution_power(0)(5)
|
|
188
|
+
5
|
|
189
|
+
|
|
190
|
+
The necessity of ``create_tuple=True``::
|
|
191
|
+
|
|
192
|
+
sage: X = [0, 1, 2, 3, 4]
|
|
193
|
+
sage: D_wrong = DiscreteDynamicalSystem(X, lambda x: (x**3) % 5)
|
|
194
|
+
sage: D_right = DiscreteDynamicalSystem(X, lambda x: (x**3) % 5, create_tuple=True)
|
|
195
|
+
sage: X[4] = 666 # evil
|
|
196
|
+
sage: D_wrong.ground_set()
|
|
197
|
+
[0, 1, 2, 3, 666]
|
|
198
|
+
sage: D_right.ground_set()
|
|
199
|
+
(0, 1, 2, 3, 4)
|
|
200
|
+
|
|
201
|
+
Here is an invertible (but infinite) discrete dynamical
|
|
202
|
+
system whose orbits are finite::
|
|
203
|
+
|
|
204
|
+
sage: D = DiscreteDynamicalSystem(NN, lambda x: (x + 2 if x % 6 < 4 else x - 4), inverse=True)
|
|
205
|
+
sage: D.ground_set()
|
|
206
|
+
Non negative integer semiring
|
|
207
|
+
sage: D.evolution()(5)
|
|
208
|
+
1
|
|
209
|
+
sage: D.evolution()(1)
|
|
210
|
+
3
|
|
211
|
+
sage: D.evolution()(3)
|
|
212
|
+
5
|
|
213
|
+
sage: D.evolution_power(2)(5)
|
|
214
|
+
3
|
|
215
|
+
sage: D.evolution_power(3)(5)
|
|
216
|
+
5
|
|
217
|
+
sage: D.evolution_power(-2)(5)
|
|
218
|
+
1
|
|
219
|
+
sage: D.inverse_evolution()(4)
|
|
220
|
+
2
|
|
221
|
+
sage: D.orbit(3)
|
|
222
|
+
[3, 5, 1]
|
|
223
|
+
|
|
224
|
+
Setting the ``inverse`` parameter to ``None`` or ``False``
|
|
225
|
+
would give the same system without the functionality that
|
|
226
|
+
relies on invertibility::
|
|
227
|
+
|
|
228
|
+
sage: D = DiscreteDynamicalSystem(NN, lambda x: (x + 2 if x % 6 < 4 else x - 4), inverse=False)
|
|
229
|
+
sage: D.ground_set()
|
|
230
|
+
Non negative integer semiring
|
|
231
|
+
sage: D.evolution()(5)
|
|
232
|
+
1
|
|
233
|
+
sage: D.inverse_evolution()(4)
|
|
234
|
+
Traceback (most recent call last):
|
|
235
|
+
...
|
|
236
|
+
AttributeError: 'DiscreteDynamicalSystem' object has no attribute 'inverse_evolution'...
|
|
237
|
+
sage: D.orbit(3)
|
|
238
|
+
[3, 5, 1]
|
|
239
|
+
|
|
240
|
+
sage: D = DiscreteDynamicalSystem(NN, lambda x: (x + 2 if x % 6 < 4 else x - 4), inverse=None)
|
|
241
|
+
sage: D.ground_set()
|
|
242
|
+
Non negative integer semiring
|
|
243
|
+
sage: D.evolution()(5)
|
|
244
|
+
1
|
|
245
|
+
sage: D.inverse_evolution()(4)
|
|
246
|
+
Traceback (most recent call last):
|
|
247
|
+
...
|
|
248
|
+
AttributeError: 'DiscreteDynamicalSystem' object has no attribute 'inverse_evolution'...
|
|
249
|
+
sage: D.orbit(3)
|
|
250
|
+
[3, 5, 1]
|
|
251
|
+
|
|
252
|
+
Next, let us try out a finite non-invertible DDS::
|
|
253
|
+
|
|
254
|
+
sage: D = DiscreteDynamicalSystem(tuple(range(13)), lambda x: (x**2) % 13)
|
|
255
|
+
sage: D.ground_set()
|
|
256
|
+
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
|
|
257
|
+
sage: D.evolution()(4)
|
|
258
|
+
3
|
|
259
|
+
sage: D.orbit(4)
|
|
260
|
+
[4, 3, 9]
|
|
261
|
+
sage: D.orbit(1)
|
|
262
|
+
[1]
|
|
263
|
+
sage: D.orbit(3)
|
|
264
|
+
[3, 9]
|
|
265
|
+
|
|
266
|
+
Note that the finiteness is automatically being inferred here,
|
|
267
|
+
since the (finite) tuple ``tuple(range(13))`` has been
|
|
268
|
+
provided as the ground set.
|
|
269
|
+
|
|
270
|
+
Finally, here is a finite invertible DDS::
|
|
271
|
+
|
|
272
|
+
sage: X = cartesian_product([[0, 1]]*8)
|
|
273
|
+
sage: Y = [s for s in X if sum(s) == 4]
|
|
274
|
+
sage: rot = lambda s : s[1:] + (s[0],)
|
|
275
|
+
sage: D = DiscreteDynamicalSystem(Y, rot, inverse=True)
|
|
276
|
+
sage: D.evolution()((0, 1, 1, 0, 1, 0, 0, 1))
|
|
277
|
+
(1, 1, 0, 1, 0, 0, 1, 0)
|
|
278
|
+
sage: D.inverse_evolution()((0, 1, 1, 0, 1, 0, 0, 1))
|
|
279
|
+
(1, 0, 1, 1, 0, 1, 0, 0)
|
|
280
|
+
sage: sorted(D.orbit_lengths())
|
|
281
|
+
[2, 4, 8, 8, 8, 8, 8, 8, 8, 8]
|
|
282
|
+
|
|
283
|
+
We could have just as well provided its inverse explicitly::
|
|
284
|
+
|
|
285
|
+
sage: rot7 = lambda s: (s[-1],) + s[:-1]
|
|
286
|
+
sage: D = DiscreteDynamicalSystem(Y, rot, inverse=rot7)
|
|
287
|
+
sage: D.evolution()((0, 1, 1, 0, 1, 0, 0, 1))
|
|
288
|
+
(1, 1, 0, 1, 0, 0, 1, 0)
|
|
289
|
+
sage: D.inverse_evolution()((0, 1, 1, 0, 1, 0, 0, 1))
|
|
290
|
+
(1, 0, 1, 1, 0, 1, 0, 0)
|
|
291
|
+
"""
|
|
292
|
+
@staticmethod
|
|
293
|
+
def __classcall_private__(cls, X, phi, cache_orbits=False, create_tuple=False, inverse=None, is_finite=None):
|
|
294
|
+
"""
|
|
295
|
+
Return the correct object based on input.
|
|
296
|
+
|
|
297
|
+
The main purpose of this method is to decide which
|
|
298
|
+
subclass the object will belong to based on the
|
|
299
|
+
``inverse`` and ``is_finite`` arguments.
|
|
300
|
+
|
|
301
|
+
EXAMPLES::
|
|
302
|
+
|
|
303
|
+
sage: D = DiscreteDynamicalSystem(NN, lambda x: x + 1)
|
|
304
|
+
sage: parent(D)
|
|
305
|
+
<class 'sage.dynamics.finite_dynamical_system.DiscreteDynamicalSystem'>
|
|
306
|
+
|
|
307
|
+
sage: f1 = lambda x: (x + 2 if x % 6 < 4 else x - 4)
|
|
308
|
+
|
|
309
|
+
sage: D = DiscreteDynamicalSystem(NN, f1, inverse=False)
|
|
310
|
+
sage: parent(D)
|
|
311
|
+
<class 'sage.dynamics.finite_dynamical_system.DiscreteDynamicalSystem'>
|
|
312
|
+
|
|
313
|
+
sage: D = DiscreteDynamicalSystem(NN, f1, inverse=None)
|
|
314
|
+
sage: parent(D)
|
|
315
|
+
<class 'sage.dynamics.finite_dynamical_system.DiscreteDynamicalSystem'>
|
|
316
|
+
|
|
317
|
+
sage: D = DiscreteDynamicalSystem(NN, f1, inverse=True)
|
|
318
|
+
sage: parent(D)
|
|
319
|
+
<class 'sage.dynamics.finite_dynamical_system.InvertibleDiscreteDynamicalSystem'>
|
|
320
|
+
|
|
321
|
+
sage: D = DiscreteDynamicalSystem(NN, lambda x: x + 1, is_finite=False)
|
|
322
|
+
sage: parent(D)
|
|
323
|
+
<class 'sage.dynamics.finite_dynamical_system.DiscreteDynamicalSystem'>
|
|
324
|
+
|
|
325
|
+
sage: f2 = lambda x: (x + 1 if x < 3 else 1)
|
|
326
|
+
sage: f3 = lambda x: (x - 1 if x > 3 else 3)
|
|
327
|
+
|
|
328
|
+
sage: D = DiscreteDynamicalSystem([1, 2, 3], f2)
|
|
329
|
+
sage: parent(D)
|
|
330
|
+
<class 'sage.dynamics.finite_dynamical_system.FiniteDynamicalSystem'>
|
|
331
|
+
|
|
332
|
+
sage: D = DiscreteDynamicalSystem([1, 2, 3], f2, inverse=True)
|
|
333
|
+
sage: parent(D)
|
|
334
|
+
<class 'sage.dynamics.finite_dynamical_system.InvertibleFiniteDynamicalSystem'>
|
|
335
|
+
|
|
336
|
+
sage: D = DiscreteDynamicalSystem([1, 2, 3], f2, inverse=f3)
|
|
337
|
+
sage: parent(D)
|
|
338
|
+
<class 'sage.dynamics.finite_dynamical_system.InvertibleFiniteDynamicalSystem'>
|
|
339
|
+
|
|
340
|
+
sage: D = DiscreteDynamicalSystem([1, 2, 3], f2, inverse=False)
|
|
341
|
+
sage: parent(D)
|
|
342
|
+
<class 'sage.dynamics.finite_dynamical_system.FiniteDynamicalSystem'>
|
|
343
|
+
|
|
344
|
+
sage: D = DiscreteDynamicalSystem([1, 2, 3], f2, inverse=None)
|
|
345
|
+
sage: parent(D)
|
|
346
|
+
<class 'sage.dynamics.finite_dynamical_system.FiniteDynamicalSystem'>
|
|
347
|
+
"""
|
|
348
|
+
if is_finite is None:
|
|
349
|
+
is_finite = (X in Sets().Finite() or isinstance(X, (list,tuple,set,frozenset)))
|
|
350
|
+
if inverse:
|
|
351
|
+
if inverse is True: # invertibility claimed, but inverse not provided
|
|
352
|
+
# This is how the input for these subclasses work
|
|
353
|
+
inverse = None
|
|
354
|
+
ret_cls = (InvertibleFiniteDynamicalSystem if is_finite
|
|
355
|
+
else InvertibleDiscreteDynamicalSystem)
|
|
356
|
+
return ret_cls(X, phi, cache_orbits=cache_orbits,
|
|
357
|
+
create_tuple=create_tuple, inverse=inverse)
|
|
358
|
+
if is_finite:
|
|
359
|
+
return FiniteDynamicalSystem(X, phi, cache_orbits=cache_orbits,
|
|
360
|
+
create_tuple=create_tuple)
|
|
361
|
+
return typecall(cls, X, phi, cache_orbits=cache_orbits, create_tuple=create_tuple)
|
|
362
|
+
|
|
363
|
+
def __init__(self, X, phi, cache_orbits=False, create_tuple=False):
|
|
364
|
+
r"""
|
|
365
|
+
Initialize ``self``.
|
|
366
|
+
|
|
367
|
+
EXAMPLES::
|
|
368
|
+
|
|
369
|
+
sage: D = DiscreteDynamicalSystem([1, 3, 4], lambda x: (3 if x == 4 else 1), create_tuple=True)
|
|
370
|
+
sage: TestSuite(D).run(skip ='_test_pickling') # indirect doctest
|
|
371
|
+
sage: D = DiscreteDynamicalSystem([1, 3, 4], lambda x: (3 if x == 4 else 1), create_tuple=True, is_finite=False)
|
|
372
|
+
sage: TestSuite(D).run(skip ='_test_pickling') # indirect doctest
|
|
373
|
+
sage: D = DiscreteDynamicalSystem(NN, lambda x: (3 if x == 4 else 1))
|
|
374
|
+
sage: TestSuite(D).run(skip ='_test_pickling') # indirect doctest
|
|
375
|
+
sage: D = DiscreteDynamicalSystem(None, lambda x: (3 if x == 4 else 1))
|
|
376
|
+
sage: TestSuite(D).run(skip ='_test_pickling') # indirect doctest
|
|
377
|
+
sage: D = DiscreteDynamicalSystem([1, 3, 4], lambda x: x, create_tuple=True)
|
|
378
|
+
sage: TestSuite(D).run(skip ='_test_pickling') # indirect doctest
|
|
379
|
+
"""
|
|
380
|
+
if create_tuple:
|
|
381
|
+
X = tuple(X)
|
|
382
|
+
self._X = X
|
|
383
|
+
self._phi = phi
|
|
384
|
+
self._cache_orbits = cache_orbits
|
|
385
|
+
|
|
386
|
+
def ground_set(self):
|
|
387
|
+
r"""
|
|
388
|
+
Return the ground set of ``self``.
|
|
389
|
+
|
|
390
|
+
This will return ``None`` if no ground set was
|
|
391
|
+
provided in the construction of ``self``.
|
|
392
|
+
|
|
393
|
+
.. WARNING::
|
|
394
|
+
|
|
395
|
+
Unless ``self`` has been constructed with the
|
|
396
|
+
``create_tuple`` parameter set to ``True``,
|
|
397
|
+
this method will return whatever ground set was
|
|
398
|
+
provided to the constructor.
|
|
399
|
+
In particular, if a list was provided, then this
|
|
400
|
+
precise list will be returned; mutating this list
|
|
401
|
+
will then corrupt ``self``.
|
|
402
|
+
|
|
403
|
+
EXAMPLES::
|
|
404
|
+
|
|
405
|
+
sage: D = DiscreteDynamicalSystem([1, 3, 4], lambda x: (3 if x == 4 else 1), create_tuple=True)
|
|
406
|
+
sage: D.ground_set()
|
|
407
|
+
(1, 3, 4)
|
|
408
|
+
"""
|
|
409
|
+
return self._X
|
|
410
|
+
|
|
411
|
+
def evolution(self):
|
|
412
|
+
r"""
|
|
413
|
+
Return the evolution of ``self``.
|
|
414
|
+
|
|
415
|
+
EXAMPLES::
|
|
416
|
+
|
|
417
|
+
sage: D = DiscreteDynamicalSystem([1, 3, 4], lambda x: (3 if x == 4 else 1), create_tuple=True)
|
|
418
|
+
sage: ev = D.evolution()
|
|
419
|
+
sage: ev(1)
|
|
420
|
+
1
|
|
421
|
+
sage: ev(4)
|
|
422
|
+
3
|
|
423
|
+
"""
|
|
424
|
+
return self._phi
|
|
425
|
+
|
|
426
|
+
def evolution_power(self, n):
|
|
427
|
+
r"""
|
|
428
|
+
Return the `n`-th power (with respect to composition)
|
|
429
|
+
of the evolution of ``self``.
|
|
430
|
+
|
|
431
|
+
This requires `n` to be a nonnegative integer.
|
|
432
|
+
|
|
433
|
+
EXAMPLES::
|
|
434
|
+
|
|
435
|
+
sage: D = DiscreteDynamicalSystem(range(10), lambda x: (x + 3) % 10, create_tuple=True)
|
|
436
|
+
sage: ev3 = D.evolution_power(3)
|
|
437
|
+
sage: ev3(1)
|
|
438
|
+
0
|
|
439
|
+
sage: ev3(2)
|
|
440
|
+
1
|
|
441
|
+
sage: ev0 = D.evolution_power(0)
|
|
442
|
+
sage: ev0(1)
|
|
443
|
+
1
|
|
444
|
+
sage: ev0(2)
|
|
445
|
+
2
|
|
446
|
+
sage: D.evolution_power(-1)
|
|
447
|
+
Traceback (most recent call last):
|
|
448
|
+
...
|
|
449
|
+
ValueError: the n-th power of evolution is only defined for nonnegative integers n
|
|
450
|
+
"""
|
|
451
|
+
from sage.rings.semirings.non_negative_integer_semiring import NN
|
|
452
|
+
if n not in NN:
|
|
453
|
+
raise ValueError("the n-th power of evolution is only defined for nonnegative integers n")
|
|
454
|
+
ev = self.evolution()
|
|
455
|
+
|
|
456
|
+
def evn(x):
|
|
457
|
+
y = x
|
|
458
|
+
for _ in range(n):
|
|
459
|
+
y = ev(y)
|
|
460
|
+
return y
|
|
461
|
+
return evn
|
|
462
|
+
|
|
463
|
+
def __iter__(self):
|
|
464
|
+
r"""
|
|
465
|
+
Iterate over the ground set of ``self``.
|
|
466
|
+
|
|
467
|
+
This assumes that an iterable ground set of ``self``
|
|
468
|
+
has been provided.
|
|
469
|
+
|
|
470
|
+
EXAMPLES::
|
|
471
|
+
|
|
472
|
+
sage: D = DiscreteDynamicalSystem(NN, lambda x: x + 2)
|
|
473
|
+
sage: D[:3]
|
|
474
|
+
[0, 1, 2]
|
|
475
|
+
"""
|
|
476
|
+
return iter(self._X)
|
|
477
|
+
|
|
478
|
+
def __getitem__(self, i):
|
|
479
|
+
r"""
|
|
480
|
+
Accessor for the ``i``-th entry of the ground set of
|
|
481
|
+
``self``, assuming that the latter ground set was
|
|
482
|
+
provided as a list or tuple when ``self`` was constructed.
|
|
483
|
+
|
|
484
|
+
EXAMPLES::
|
|
485
|
+
|
|
486
|
+
sage: X = (5, 6, 7, 8)
|
|
487
|
+
sage: D = DiscreteDynamicalSystem(X, lambda x: (x**3) % 4 + 5)
|
|
488
|
+
sage: X[3]
|
|
489
|
+
8
|
|
490
|
+
sage: X[2]
|
|
491
|
+
7
|
|
492
|
+
"""
|
|
493
|
+
return self._X[i]
|
|
494
|
+
|
|
495
|
+
def _repr_(self):
|
|
496
|
+
r"""
|
|
497
|
+
String representation of ``self``.
|
|
498
|
+
|
|
499
|
+
EXAMPLES::
|
|
500
|
+
|
|
501
|
+
sage: D = DiscreteDynamicalSystem(NN, lambda x: x + 2)
|
|
502
|
+
sage: D # indirect doctest
|
|
503
|
+
A discrete dynamical system with ground set
|
|
504
|
+
Non negative integer semiring
|
|
505
|
+
sage: D = DiscreteDynamicalSystem(None, lambda x: x + 2)
|
|
506
|
+
sage: D # indirect doctest
|
|
507
|
+
A discrete dynamical system with unspecified ground set
|
|
508
|
+
"""
|
|
509
|
+
if self._X is None:
|
|
510
|
+
return "A discrete dynamical system with unspecified ground set"
|
|
511
|
+
return "A discrete dynamical system with ground set " \
|
|
512
|
+
+ repr(self._X)
|
|
513
|
+
|
|
514
|
+
def orbit(self, x, preperiod=False):
|
|
515
|
+
r"""
|
|
516
|
+
Return the orbit of the element ``x`` of the ground set
|
|
517
|
+
of ``self`` under the evolution `\phi` of ``self``.
|
|
518
|
+
|
|
519
|
+
This orbit is a list beginning with ``x`` and ending
|
|
520
|
+
with the last element that is not a repetition of a
|
|
521
|
+
previous element.
|
|
522
|
+
If the orbit is infinite, then this method does not
|
|
523
|
+
terminate!
|
|
524
|
+
|
|
525
|
+
If the optional argument ``preperiod`` is set to
|
|
526
|
+
``True``, then this method returns a pair ``(o, k)``,
|
|
527
|
+
where ``o`` is the orbit of ``self``, while `k` is the
|
|
528
|
+
smallest nonnegative integer such that
|
|
529
|
+
`\phi^k(x) \in \left\{ \phi^i(x) \mid i > k \right\}`.
|
|
530
|
+
|
|
531
|
+
The orbit of the element ``x`` is also called the
|
|
532
|
+
"rho" of ``x``, due to its shape when it is depicted
|
|
533
|
+
as a directed graph.
|
|
534
|
+
|
|
535
|
+
EXAMPLES::
|
|
536
|
+
|
|
537
|
+
sage: D = DiscreteDynamicalSystem(tuple(range(11)), lambda x: (x ** 2) % 11)
|
|
538
|
+
sage: D.orbit(6)
|
|
539
|
+
[6, 3, 9, 4, 5]
|
|
540
|
+
sage: D.orbit(6, preperiod=True)
|
|
541
|
+
([6, 3, 9, 4, 5], 1)
|
|
542
|
+
sage: D.orbit(3)
|
|
543
|
+
[3, 9, 4, 5]
|
|
544
|
+
sage: D.orbit(3, preperiod=True)
|
|
545
|
+
([3, 9, 4, 5], 0)
|
|
546
|
+
sage: D.orbit(9)
|
|
547
|
+
[9, 4, 5, 3]
|
|
548
|
+
sage: D.orbit(0)
|
|
549
|
+
[0]
|
|
550
|
+
"""
|
|
551
|
+
orb = [x]
|
|
552
|
+
phi = self._phi
|
|
553
|
+
curr = phi(x)
|
|
554
|
+
while curr not in orb:
|
|
555
|
+
orb.append(curr)
|
|
556
|
+
curr = phi(curr)
|
|
557
|
+
if not preperiod:
|
|
558
|
+
return orb
|
|
559
|
+
return (orb, orb.index(curr))
|
|
560
|
+
|
|
561
|
+
def is_homomesic(self, h, average=None, find_average=False, elements=None):
|
|
562
|
+
r"""
|
|
563
|
+
Check if ``h`` (a map from the ground set of ``self`` to
|
|
564
|
+
a `\QQ`-vector space) is homomesic with respect to ``self``.
|
|
565
|
+
|
|
566
|
+
If the optional argument ``average`` is provided, then
|
|
567
|
+
this also checks that the averages are equal to ``average``.
|
|
568
|
+
|
|
569
|
+
If the optional argument ``find_average`` is set to
|
|
570
|
+
``True``, then this method returns the average of ``h``
|
|
571
|
+
in case ``h`` is homomesic (instead of returning ``True``).
|
|
572
|
+
|
|
573
|
+
If the optional argument ``elements`` (an iterable of
|
|
574
|
+
elements of the ground set of ``self``) is provided, then
|
|
575
|
+
this method only checks homomesy for the cycles in the
|
|
576
|
+
orbits of the elements given in the list ``elements``.
|
|
577
|
+
Note that ``elements`` must be provided if the ground set of
|
|
578
|
+
``self`` is infinite (or cannot be iterated through for any
|
|
579
|
+
other reason), since there is no way to check all the cycles
|
|
580
|
+
in this case.
|
|
581
|
+
|
|
582
|
+
This method will fail to terminate if any element of
|
|
583
|
+
``elements`` has an infinite orbit.
|
|
584
|
+
|
|
585
|
+
Let us recall the definition of homomesy:
|
|
586
|
+
Let `(X, \phi)` be a DDS.
|
|
587
|
+
A *cycle* of `(X, \phi)` is a finite list
|
|
588
|
+
`u = (u_1, u_2, \ldots, u_k)` of elements of `X` such
|
|
589
|
+
that `\phi(u_i) = u_{i+1}` for each `i \leq k`, where we
|
|
590
|
+
set `u_{k+1} = u_1`.
|
|
591
|
+
Note that any element of `X` whose orbit is finite has a
|
|
592
|
+
cycle in its orbit.
|
|
593
|
+
Now, let `h` be a map from `X` to a `\QQ`-vector space `A`.
|
|
594
|
+
If `u = (u_1, u_2, \ldots, u_k)` is any cycle of
|
|
595
|
+
`(X, \phi)`, then the *average* of `h` on this cycle is
|
|
596
|
+
defined to be the element
|
|
597
|
+
`(h(u_1) + h(u_2) + \cdots + h(u_k)) / k` of `A`.
|
|
598
|
+
We say that `h` is *homomesic* (with respect to the DDS
|
|
599
|
+
`(X, \phi)`) if and only if the averages of `h` on all
|
|
600
|
+
cycles of `(X, \phi)` are equal.
|
|
601
|
+
|
|
602
|
+
EXAMPLES::
|
|
603
|
+
|
|
604
|
+
sage: W = Words(2, 5)
|
|
605
|
+
sage: F = DiscreteDynamicalSystem(W, lambda x: x[1:] + Word([x[0]]), is_finite=True, inverse=True)
|
|
606
|
+
sage: F.is_homomesic(lambda w: sum(w))
|
|
607
|
+
False
|
|
608
|
+
sage: F.is_homomesic(lambda w: 1, average=1)
|
|
609
|
+
True
|
|
610
|
+
sage: F.is_homomesic(lambda w: 1, average=0)
|
|
611
|
+
False
|
|
612
|
+
sage: F.is_homomesic(lambda w: 1)
|
|
613
|
+
True
|
|
614
|
+
sage: F.is_homomesic(lambda w: 1, find_average=True)
|
|
615
|
+
1
|
|
616
|
+
sage: F.is_homomesic(lambda w: w[0] - w[1], average=0)
|
|
617
|
+
True
|
|
618
|
+
sage: F.is_homomesic(lambda w: w[0] - w[1], find_average=True)
|
|
619
|
+
0
|
|
620
|
+
|
|
621
|
+
Now, let us check homomesy restricted to specific cycles::
|
|
622
|
+
|
|
623
|
+
sage: F = finite_dynamical_systems.bitstring_rotation(7)
|
|
624
|
+
sage: descents = lambda x: sum(1 for i in range(6) if x[i] > x[i+1])
|
|
625
|
+
sage: F.is_homomesic(descents)
|
|
626
|
+
False
|
|
627
|
+
sage: F.is_homomesic(descents, elements=[(1, 0, 1, 0, 0, 0, 0), (1, 0, 0, 1, 0, 0, 0)])
|
|
628
|
+
True
|
|
629
|
+
sage: F.is_homomesic(descents, elements=[(1, 0, 1, 0, 0, 0, 0), (1, 1, 0, 0, 0, 0, 0)])
|
|
630
|
+
False
|
|
631
|
+
sage: F.is_homomesic(descents, elements=[(1, 0, 1, 0, 0, 0, 0)])
|
|
632
|
+
True
|
|
633
|
+
sage: F.is_homomesic(descents, elements=[])
|
|
634
|
+
True
|
|
635
|
+
|
|
636
|
+
And here is a non-invertible finite dynamical system::
|
|
637
|
+
|
|
638
|
+
sage: F = finite_dynamical_systems.one_line([9, 1, 1, 6, 5, 4, 5, 5, 1])
|
|
639
|
+
sage: F.is_homomesic(lambda i: i)
|
|
640
|
+
True
|
|
641
|
+
sage: F.is_homomesic(lambda i: i % 2)
|
|
642
|
+
False
|
|
643
|
+
sage: F.is_homomesic(lambda i: i % 2, elements=[2, 9, 7])
|
|
644
|
+
True
|
|
645
|
+
sage: F.is_homomesic(lambda i: i % 2, elements=[2, 9, 4])
|
|
646
|
+
False
|
|
647
|
+
sage: F.is_homomesic(lambda i: i % 2, elements=[2, 9, 5, 7, 8, 2])
|
|
648
|
+
True
|
|
649
|
+
"""
|
|
650
|
+
from sage.rings.rational_field import QQ
|
|
651
|
+
orbavgs = [] # This will be the list of all averages on cycles.
|
|
652
|
+
if elements is None:
|
|
653
|
+
# The user has not provided elements, so we need to
|
|
654
|
+
# check all cycles of the DDS.
|
|
655
|
+
for cyc in self.cycles():
|
|
656
|
+
l = len(cyc)
|
|
657
|
+
avg = ~(QQ(l)) * sum(h(i) for i in cyc)
|
|
658
|
+
if avg not in orbavgs:
|
|
659
|
+
if orbavgs:
|
|
660
|
+
return False
|
|
661
|
+
orbavgs.append(avg)
|
|
662
|
+
else:
|
|
663
|
+
# Checking only the cycles of the elements provided
|
|
664
|
+
# by the user.
|
|
665
|
+
for element in elements:
|
|
666
|
+
(orb, ix) = self.orbit(element, preperiod=True)
|
|
667
|
+
cyc = orb[ix:] # the cycle in the orbit of element
|
|
668
|
+
l = len(cyc)
|
|
669
|
+
avg = ~(QQ(l)) * sum(h(i) for i in cyc)
|
|
670
|
+
if avg not in orbavgs:
|
|
671
|
+
if orbavgs:
|
|
672
|
+
return False
|
|
673
|
+
orbavgs.append(avg)
|
|
674
|
+
if not orbavgs:
|
|
675
|
+
return True
|
|
676
|
+
if average is None:
|
|
677
|
+
if find_average:
|
|
678
|
+
return orbavgs[0]
|
|
679
|
+
return True
|
|
680
|
+
return orbavgs[0] == average
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
class InvertibleDiscreteDynamicalSystem(DiscreteDynamicalSystem):
|
|
684
|
+
r"""
|
|
685
|
+
An invertible discrete dynamical system.
|
|
686
|
+
|
|
687
|
+
A *discrete dynamical system* (henceforth *DDS*) is a
|
|
688
|
+
pair `(X, \phi)` of a set `X` and a map `\phi : X \to X`.
|
|
689
|
+
This set `X` is called the *ground set* of the DDS, while
|
|
690
|
+
the map `\phi` is called the *evolution* of the DDS.
|
|
691
|
+
An *invertible DDS* is a DDS `(X, \phi)` whose evolution
|
|
692
|
+
`\phi` is invertible.
|
|
693
|
+
In that case, `\phi^{-1}` is called the *inverse evolution*
|
|
694
|
+
of the DDS.
|
|
695
|
+
|
|
696
|
+
See :class:`DiscreteDynamicalSystem` for details.
|
|
697
|
+
|
|
698
|
+
INPUT:
|
|
699
|
+
|
|
700
|
+
- ``X`` -- set, list, tuple, or another iterable, or
|
|
701
|
+
``None``; the ground set for the DDS. This can be
|
|
702
|
+
``None`` in case of a
|
|
703
|
+
:class:`DiscreteDynamicalSystem` or a
|
|
704
|
+
:class:`InvertibleDiscreteDynamicalSystem`.
|
|
705
|
+
Make sure to set the ``create_tuple`` argument to
|
|
706
|
+
``True`` if you provide an iterator or a list for
|
|
707
|
+
``X``, as otherwise the input would be exposed.
|
|
708
|
+
|
|
709
|
+
- ``phi`` -- function, or callable that acts like a
|
|
710
|
+
function; the evolution of the DDS
|
|
711
|
+
|
|
712
|
+
- ``inverse`` -- function, or callable that acts like a
|
|
713
|
+
function; the inverse evolution of the DDS. (A
|
|
714
|
+
default implementation is implemented when this
|
|
715
|
+
argument is not provided; but it assumes the orbits
|
|
716
|
+
to be finite.)
|
|
717
|
+
|
|
718
|
+
- ``cache_orbits`` -- boolean (default: ``False``);
|
|
719
|
+
whether or not the orbits should be cached once they
|
|
720
|
+
are computed.
|
|
721
|
+
|
|
722
|
+
- ``create_tuple`` -- boolean (default: ``False``);
|
|
723
|
+
whether or not the input ``X`` should be translated
|
|
724
|
+
into a tuple (set this to ``True`` to prevent
|
|
725
|
+
mutation if ``X`` is a list, and to prevent
|
|
726
|
+
exhaustion if ``X`` is an iterator).
|
|
727
|
+
|
|
728
|
+
EXAMPLES::
|
|
729
|
+
|
|
730
|
+
sage: from sage.dynamics.finite_dynamical_system import InvertibleDiscreteDynamicalSystem
|
|
731
|
+
sage: D = InvertibleDiscreteDynamicalSystem(NN, lambda x: (x + 2 if x % 4 < 2 else x - 2))
|
|
732
|
+
sage: D.ground_set()
|
|
733
|
+
Non negative integer semiring
|
|
734
|
+
sage: D.evolution()(5)
|
|
735
|
+
7
|
|
736
|
+
sage: D.evolution()(6)
|
|
737
|
+
4
|
|
738
|
+
sage: D.evolution()(4)
|
|
739
|
+
6
|
|
740
|
+
sage: D.inverse_evolution()(4)
|
|
741
|
+
6
|
|
742
|
+
|
|
743
|
+
The necessity of ``create_tuple=True``::
|
|
744
|
+
|
|
745
|
+
sage: X = [0, 1, 2, 3, 4]
|
|
746
|
+
sage: D_wrong = InvertibleDiscreteDynamicalSystem(X, lambda x: (x**3) % 5)
|
|
747
|
+
sage: D_right = InvertibleDiscreteDynamicalSystem(X, lambda x: (x**3) % 5, create_tuple=True)
|
|
748
|
+
sage: X[4] = 666 # evil
|
|
749
|
+
sage: D_wrong.ground_set()
|
|
750
|
+
[0, 1, 2, 3, 666]
|
|
751
|
+
sage: D_right.ground_set()
|
|
752
|
+
(0, 1, 2, 3, 4)
|
|
753
|
+
"""
|
|
754
|
+
def __init__(self, X, phi, inverse=None, cache_orbits=False, create_tuple=False):
|
|
755
|
+
r"""
|
|
756
|
+
Initialize ``self``.
|
|
757
|
+
|
|
758
|
+
EXAMPLES::
|
|
759
|
+
|
|
760
|
+
sage: D = DiscreteDynamicalSystem([1, 3, 4], lambda x: x, create_tuple=True, inverse=True)
|
|
761
|
+
sage: TestSuite(D).run(skip ='_test_pickling') # indirect doctest
|
|
762
|
+
sage: D = DiscreteDynamicalSystem([1, 3, 4], lambda x: x, create_tuple=True, is_finite=False, inverse=True)
|
|
763
|
+
sage: TestSuite(D).run(skip ='_test_pickling') # indirect doctest
|
|
764
|
+
sage: D = DiscreteDynamicalSystem(NN, lambda x: x, inverse=True)
|
|
765
|
+
sage: TestSuite(D).run(skip ='_test_pickling') # indirect doctest
|
|
766
|
+
sage: D = DiscreteDynamicalSystem(None, lambda x: x, inverse=True)
|
|
767
|
+
sage: TestSuite(D).run(skip ='_test_pickling') # indirect doctest
|
|
768
|
+
"""
|
|
769
|
+
if create_tuple:
|
|
770
|
+
X = tuple(X)
|
|
771
|
+
self._X = X
|
|
772
|
+
self._phi = phi
|
|
773
|
+
if inverse is None:
|
|
774
|
+
self._inverse = self.inverse_evolution_default
|
|
775
|
+
else:
|
|
776
|
+
self._inverse = inverse
|
|
777
|
+
self._cache_orbits = cache_orbits
|
|
778
|
+
|
|
779
|
+
def evolution_power(self, n):
|
|
780
|
+
r"""
|
|
781
|
+
Return the `n`-th power (with respect to composition)
|
|
782
|
+
of the evolution of ``self``.
|
|
783
|
+
|
|
784
|
+
This requires `n` to be an integer.
|
|
785
|
+
|
|
786
|
+
EXAMPLES::
|
|
787
|
+
|
|
788
|
+
sage: D = DiscreteDynamicalSystem(range(10), lambda x: (x + 3) % 10, create_tuple=True, inverse=True)
|
|
789
|
+
sage: ev3 = D.evolution_power(3)
|
|
790
|
+
sage: ev3(1)
|
|
791
|
+
0
|
|
792
|
+
sage: ev3(2)
|
|
793
|
+
1
|
|
794
|
+
sage: ev0 = D.evolution_power(0)
|
|
795
|
+
sage: ev0(1)
|
|
796
|
+
1
|
|
797
|
+
sage: ev0(2)
|
|
798
|
+
2
|
|
799
|
+
sage: evm1 = D.evolution_power(-1)
|
|
800
|
+
sage: evm1(1)
|
|
801
|
+
8
|
|
802
|
+
sage: evm1(2)
|
|
803
|
+
9
|
|
804
|
+
sage: evm2 = D.evolution_power(-2)
|
|
805
|
+
sage: evm2(1)
|
|
806
|
+
5
|
|
807
|
+
sage: evm2(2)
|
|
808
|
+
6
|
|
809
|
+
"""
|
|
810
|
+
from sage.rings.integer_ring import ZZ
|
|
811
|
+
if n not in ZZ:
|
|
812
|
+
raise ValueError("the n-th power of evolution is only defined for integers n")
|
|
813
|
+
if n >= 0:
|
|
814
|
+
ev = self.evolution()
|
|
815
|
+
else:
|
|
816
|
+
ev = self.inverse_evolution()
|
|
817
|
+
n = -n
|
|
818
|
+
|
|
819
|
+
def evn(x):
|
|
820
|
+
y = x
|
|
821
|
+
for _ in range(n):
|
|
822
|
+
y = ev(y)
|
|
823
|
+
return y
|
|
824
|
+
return evn
|
|
825
|
+
|
|
826
|
+
def _repr_(self):
|
|
827
|
+
r"""
|
|
828
|
+
String representation of ``self``.
|
|
829
|
+
|
|
830
|
+
EXAMPLES::
|
|
831
|
+
|
|
832
|
+
sage: D = DiscreteDynamicalSystem(NN, lambda x: (x + 2 if x % 4 < 2 else x - 2), inverse=True)
|
|
833
|
+
sage: D # indirect doctest
|
|
834
|
+
An invertible discrete dynamical system with ground set
|
|
835
|
+
Non negative integer semiring
|
|
836
|
+
sage: D = DiscreteDynamicalSystem(None, lambda x: x + 2, inverse=True)
|
|
837
|
+
sage: D # indirect doctest
|
|
838
|
+
An invertible discrete dynamical system with unspecified ground set
|
|
839
|
+
"""
|
|
840
|
+
if self._X is None:
|
|
841
|
+
return "An invertible discrete dynamical system with unspecified ground set"
|
|
842
|
+
return "An invertible discrete dynamical system with ground set " \
|
|
843
|
+
+ repr(self._X)
|
|
844
|
+
|
|
845
|
+
def inverse_evolution(self):
|
|
846
|
+
r"""
|
|
847
|
+
Return the inverse evolution of ``self`` (as a map
|
|
848
|
+
from the ground set of ``self`` to itself).
|
|
849
|
+
|
|
850
|
+
EXAMPLES::
|
|
851
|
+
|
|
852
|
+
sage: D = DiscreteDynamicalSystem(tuple(range(8)), lambda x: (x + 2) % 8, inverse=True)
|
|
853
|
+
sage: D.inverse_evolution()(1)
|
|
854
|
+
7
|
|
855
|
+
sage: D.inverse_evolution()(3)
|
|
856
|
+
1
|
|
857
|
+
|
|
858
|
+
sage: D = DiscreteDynamicalSystem(ZZ, lambda x: (x + 2) % 8, inverse=True)
|
|
859
|
+
sage: D.inverse_evolution()(1)
|
|
860
|
+
7
|
|
861
|
+
sage: D.inverse_evolution()(3)
|
|
862
|
+
1
|
|
863
|
+
"""
|
|
864
|
+
return self._inverse
|
|
865
|
+
|
|
866
|
+
def verify_inverse_evolution(self, x=None):
|
|
867
|
+
r"""
|
|
868
|
+
Verify that the composition of evolution and
|
|
869
|
+
inverse evolution on ``self`` is the identity
|
|
870
|
+
(both ways).
|
|
871
|
+
|
|
872
|
+
The optional argument ``x``, if provided, restricts
|
|
873
|
+
the testing to the element ``x`` only.
|
|
874
|
+
Otherwise, all elements of the ground set are
|
|
875
|
+
tested (if they can be enumerated).
|
|
876
|
+
|
|
877
|
+
This is mostly used to check the correctness of
|
|
878
|
+
self-implemented inverse evolution methods.
|
|
879
|
+
|
|
880
|
+
EXAMPLES::
|
|
881
|
+
|
|
882
|
+
sage: D = DiscreteDynamicalSystem(tuple(range(8)), lambda x: (x + 2) % 8, inverse=True)
|
|
883
|
+
sage: D.verify_inverse_evolution()
|
|
884
|
+
True
|
|
885
|
+
sage: D.verify_inverse_evolution(3)
|
|
886
|
+
True
|
|
887
|
+
sage: fake_inverse = lambda x: x
|
|
888
|
+
sage: D = DiscreteDynamicalSystem(tuple(range(8)), lambda x: (x + 2) % 8, inverse=fake_inverse)
|
|
889
|
+
sage: D.verify_inverse_evolution()
|
|
890
|
+
False
|
|
891
|
+
sage: D.verify_inverse_evolution(3)
|
|
892
|
+
False
|
|
893
|
+
"""
|
|
894
|
+
ev = self.evolution()
|
|
895
|
+
iev = self.inverse_evolution()
|
|
896
|
+
if x is None:
|
|
897
|
+
els = self.ground_set()
|
|
898
|
+
else:
|
|
899
|
+
els = [x]
|
|
900
|
+
for y in els:
|
|
901
|
+
if ev(iev(y)) != y:
|
|
902
|
+
return False
|
|
903
|
+
if iev(ev(y)) != y:
|
|
904
|
+
return False
|
|
905
|
+
return True
|
|
906
|
+
|
|
907
|
+
def orbit(self, x, preperiod=False):
|
|
908
|
+
r"""
|
|
909
|
+
Return the orbit of the element ``x`` of the ground set
|
|
910
|
+
of ``self``.
|
|
911
|
+
|
|
912
|
+
This orbit is a list beginning with ``x`` and ending
|
|
913
|
+
with the last element until ``x`` reappears.
|
|
914
|
+
If ``x`` never reappears, then this will not terminate!
|
|
915
|
+
|
|
916
|
+
If the optional argument ``preperiod`` is set to
|
|
917
|
+
``True``, then this method returns a pair ``(o, k)``,
|
|
918
|
+
where ``o`` is the orbit of ``self``, while `k` is the
|
|
919
|
+
smallest nonnegative integer such that
|
|
920
|
+
`\phi^k(x) \in \left\{ \phi^i(x) \mid i > k \right\}`.
|
|
921
|
+
Note that `k` is necessarily `0`, since the DDS
|
|
922
|
+
``self`` is invertible!
|
|
923
|
+
|
|
924
|
+
EXAMPLES::
|
|
925
|
+
|
|
926
|
+
sage: D = DiscreteDynamicalSystem(tuple(range(8)), lambda x: (x + 2) % 8, inverse=True)
|
|
927
|
+
sage: D.ground_set()
|
|
928
|
+
(0, 1, 2, 3, 4, 5, 6, 7)
|
|
929
|
+
sage: D.orbit(2)
|
|
930
|
+
[2, 4, 6, 0]
|
|
931
|
+
sage: D.orbit(5)
|
|
932
|
+
[5, 7, 1, 3]
|
|
933
|
+
sage: D.orbit(5, preperiod=True)
|
|
934
|
+
([5, 7, 1, 3], 0)
|
|
935
|
+
|
|
936
|
+
sage: D = DiscreteDynamicalSystem(ZZ, lambda x: (x + 2) % 8, inverse=True)
|
|
937
|
+
sage: D.ground_set()
|
|
938
|
+
Integer Ring
|
|
939
|
+
sage: D.orbit(2)
|
|
940
|
+
[2, 4, 6, 0]
|
|
941
|
+
sage: D.orbit(5)
|
|
942
|
+
[5, 7, 1, 3]
|
|
943
|
+
sage: D.orbit(5, preperiod=True)
|
|
944
|
+
([5, 7, 1, 3], 0)
|
|
945
|
+
"""
|
|
946
|
+
orb = [x]
|
|
947
|
+
phi = self._phi
|
|
948
|
+
curr = phi(x)
|
|
949
|
+
while curr != x:
|
|
950
|
+
orb.append(curr)
|
|
951
|
+
curr = phi(curr)
|
|
952
|
+
if not preperiod:
|
|
953
|
+
return orb
|
|
954
|
+
return (orb, 0)
|
|
955
|
+
|
|
956
|
+
def inverse_evolution_default(self, x):
|
|
957
|
+
r"""
|
|
958
|
+
Return the inverse evolution of ``self``, applied
|
|
959
|
+
to the element ``x`` of the ground set of ``self``.
|
|
960
|
+
|
|
961
|
+
This is the default implementation, assuming that
|
|
962
|
+
the orbit of ``x`` is finite.
|
|
963
|
+
|
|
964
|
+
EXAMPLES::
|
|
965
|
+
|
|
966
|
+
sage: D = DiscreteDynamicalSystem(tuple(range(8)), lambda x: (x + 2) % 8, inverse=True)
|
|
967
|
+
sage: D.inverse_evolution_default(1)
|
|
968
|
+
7
|
|
969
|
+
sage: D.inverse_evolution_default(3)
|
|
970
|
+
1
|
|
971
|
+
"""
|
|
972
|
+
return self.orbit(x)[-1]
|
|
973
|
+
|
|
974
|
+
|
|
975
|
+
class FiniteDynamicalSystem(DiscreteDynamicalSystem):
|
|
976
|
+
r"""
|
|
977
|
+
A finite discrete dynamical system.
|
|
978
|
+
|
|
979
|
+
A *finite discrete dynamical system* (henceforth *FDDS*) is a
|
|
980
|
+
pair `(X, \phi)` of a finite set `X` and a map `\phi : X \to X`.
|
|
981
|
+
This set `X` is called the *ground set* of the FDDS, while
|
|
982
|
+
the map `\phi` is called the *evolution* of the FDDS.
|
|
983
|
+
|
|
984
|
+
The ground set `X` should always be provided as an
|
|
985
|
+
iterable when defining a :class:`FiniteDynamicalSystem`.
|
|
986
|
+
|
|
987
|
+
EXAMPLES::
|
|
988
|
+
|
|
989
|
+
sage: from sage.dynamics.finite_dynamical_system import FiniteDynamicalSystem
|
|
990
|
+
sage: D = FiniteDynamicalSystem(tuple(range(11)), lambda x: (x**2) % 11)
|
|
991
|
+
sage: D.ground_set()
|
|
992
|
+
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
|
|
993
|
+
sage: D.evolution()(4)
|
|
994
|
+
5
|
|
995
|
+
sage: D.orbit(4)
|
|
996
|
+
[4, 5, 3, 9]
|
|
997
|
+
sage: D.orbit(1)
|
|
998
|
+
[1]
|
|
999
|
+
sage: D.orbit(2)
|
|
1000
|
+
[2, 4, 5, 3, 9]
|
|
1001
|
+
|
|
1002
|
+
sage: X = cartesian_product([[0, 1]]*8)
|
|
1003
|
+
sage: Y = [s for s in X if sum(s) == 4]
|
|
1004
|
+
sage: rot = lambda s : s[1:] + (0,)
|
|
1005
|
+
sage: D = FiniteDynamicalSystem(Y, rot)
|
|
1006
|
+
sage: D.evolution()((1, 1, 1, 0, 1, 0, 0, 1))
|
|
1007
|
+
(1, 1, 0, 1, 0, 0, 1, 0)
|
|
1008
|
+
"""
|
|
1009
|
+
def _repr_(self):
|
|
1010
|
+
r"""
|
|
1011
|
+
String representation of ``self``.
|
|
1012
|
+
|
|
1013
|
+
EXAMPLES::
|
|
1014
|
+
|
|
1015
|
+
sage: D = DiscreteDynamicalSystem(tuple(range(11)), lambda x: (x**2) % 11)
|
|
1016
|
+
sage: D
|
|
1017
|
+
A finite discrete dynamical system with ground set
|
|
1018
|
+
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
|
|
1019
|
+
"""
|
|
1020
|
+
return "A finite discrete dynamical system with ground set " \
|
|
1021
|
+
+ repr(self._X)
|
|
1022
|
+
|
|
1023
|
+
def is_invariant(self, f):
|
|
1024
|
+
r"""
|
|
1025
|
+
Check if ``f`` is an invariant of ``self``.
|
|
1026
|
+
|
|
1027
|
+
Let `(X, \phi)` be a discrete dynamical system.
|
|
1028
|
+
Let `Y` be any set. Let `f : X \to Y` be any map.
|
|
1029
|
+
Then, we say that `f` is an *invariant* of `(X, \phi)`
|
|
1030
|
+
if and only if `f \circ \phi = f`.
|
|
1031
|
+
|
|
1032
|
+
EXAMPLES::
|
|
1033
|
+
|
|
1034
|
+
sage: W = Words(2, 5)
|
|
1035
|
+
sage: F = DiscreteDynamicalSystem(W, lambda x: x[1:] + Word([x[0]]), is_finite=True)
|
|
1036
|
+
sage: F.is_invariant(lambda w: sum(w))
|
|
1037
|
+
True
|
|
1038
|
+
sage: F.is_invariant(lambda w: 1)
|
|
1039
|
+
True
|
|
1040
|
+
sage: F.is_invariant(lambda w: w[0] - w[1])
|
|
1041
|
+
False
|
|
1042
|
+
sage: F.is_invariant(lambda w: sum(i**2 for i in w))
|
|
1043
|
+
True
|
|
1044
|
+
|
|
1045
|
+
Invariants and non-invariants of a permutation::
|
|
1046
|
+
|
|
1047
|
+
sage: F = finite_dynamical_systems.permutation([3, 4, 5, 6, 1, 2])
|
|
1048
|
+
sage: F.is_invariant(lambda i: i % 2)
|
|
1049
|
+
True
|
|
1050
|
+
sage: F.is_invariant(lambda i: i % 3)
|
|
1051
|
+
False
|
|
1052
|
+
sage: F.is_invariant(lambda i: i > 1)
|
|
1053
|
+
False
|
|
1054
|
+
sage: F.is_invariant(lambda i: i % 2 == 0)
|
|
1055
|
+
True
|
|
1056
|
+
"""
|
|
1057
|
+
phi = self._phi
|
|
1058
|
+
return all(f(phi(i)) == f(i) for i in self._X)
|
|
1059
|
+
|
|
1060
|
+
def cycles(self):
|
|
1061
|
+
r"""
|
|
1062
|
+
Return a list of all cycles of ``self``, up to
|
|
1063
|
+
cyclic rotation.
|
|
1064
|
+
|
|
1065
|
+
We recall the definition of cycles:
|
|
1066
|
+
Let `(X, \phi)` be a DDS.
|
|
1067
|
+
A *cycle* of `(X, \phi)` is a finite list
|
|
1068
|
+
`u = (u_1, u_2, \ldots, u_k)` of elements of `X` such
|
|
1069
|
+
that `\phi(u_i) = u_{i+1}` for each `i \leq k`, where we
|
|
1070
|
+
set `u_{k+1} = u_1`.
|
|
1071
|
+
Note that any element of `X` whose orbit is finite has a
|
|
1072
|
+
cycle in its orbit.
|
|
1073
|
+
|
|
1074
|
+
EXAMPLES::
|
|
1075
|
+
|
|
1076
|
+
sage: BS = finite_dynamical_systems.bulgarian_solitaire
|
|
1077
|
+
sage: BS(8).cycles()
|
|
1078
|
+
[[[4, 3, 1], [3, 3, 2], [3, 2, 2, 1], [4, 2, 1, 1]],
|
|
1079
|
+
[[4, 2, 2], [3, 3, 1, 1]]]
|
|
1080
|
+
sage: BS(6).cycles()
|
|
1081
|
+
[[[3, 2, 1]]]
|
|
1082
|
+
|
|
1083
|
+
sage: D = DiscreteDynamicalSystem(tuple(range(6)), lambda x: (x + 2) % 6)
|
|
1084
|
+
sage: D.cycles()
|
|
1085
|
+
[[5, 1, 3], [4, 0, 2]]
|
|
1086
|
+
sage: D = DiscreteDynamicalSystem(tuple(range(6)), lambda x: (x ** 2) % 6)
|
|
1087
|
+
sage: D.cycles()
|
|
1088
|
+
[[1], [4], [3], [0]]
|
|
1089
|
+
sage: D = DiscreteDynamicalSystem(tuple(range(11)), lambda x: (x ** 2 - 1) % 11)
|
|
1090
|
+
sage: D.cycles()
|
|
1091
|
+
[[10, 0], [8], [4]]
|
|
1092
|
+
|
|
1093
|
+
sage: F = finite_dynamical_systems.one_line([4, 7, 2, 6, 2, 10, 9, 11, 5, 6, 12, 12, 12, 6])
|
|
1094
|
+
sage: F.cycles()
|
|
1095
|
+
[[6, 10], [12], [9, 5, 2, 7]]
|
|
1096
|
+
"""
|
|
1097
|
+
l = list(self)
|
|
1098
|
+
cycs = []
|
|
1099
|
+
while l:
|
|
1100
|
+
start = l[-1]
|
|
1101
|
+
(orb, ix) = self.orbit(start, preperiod=True)
|
|
1102
|
+
if orb[ix] in l:
|
|
1103
|
+
# This means we've actually found a new cycle,
|
|
1104
|
+
# not just a new path to an old cycle.
|
|
1105
|
+
cycs.append(orb[ix:])
|
|
1106
|
+
for j in orb:
|
|
1107
|
+
try:
|
|
1108
|
+
l.remove(j)
|
|
1109
|
+
except ValueError:
|
|
1110
|
+
# Here we break out of the for-loop, because
|
|
1111
|
+
# if ``j`` has already been removed from
|
|
1112
|
+
# ``l``, then all later elements of the orbit
|
|
1113
|
+
# must have been removed from ``l`` as well
|
|
1114
|
+
# (indeed, the set of elements that have been
|
|
1115
|
+
# removed from ``l`` is closed under ``phi``).
|
|
1116
|
+
break
|
|
1117
|
+
return cycs
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
class InvertibleFiniteDynamicalSystem(InvertibleDiscreteDynamicalSystem, FiniteDynamicalSystem):
|
|
1121
|
+
r"""
|
|
1122
|
+
An invertible finite discrete dynamical system.
|
|
1123
|
+
|
|
1124
|
+
A *finite discrete dynamical system* (henceforth *FDDS*) is a
|
|
1125
|
+
pair `(X, \phi)` of a finite set `X` and a map `\phi : X \to X`.
|
|
1126
|
+
This set `X` is called the *ground set* of the FDDS, while
|
|
1127
|
+
the map `\phi` is called the *evolution* of the FDDS.
|
|
1128
|
+
An FDDS `(X, \phi)` is called *invertible* if the map `\phi`
|
|
1129
|
+
is invertible; in this case, `\phi^{-1}` is called the
|
|
1130
|
+
*inverse evolution* of the FDDS.
|
|
1131
|
+
|
|
1132
|
+
The ground set `X` should always be provided as an
|
|
1133
|
+
iterable when defining a :class:`FiniteDynamicalSystem`.
|
|
1134
|
+
|
|
1135
|
+
EXAMPLES::
|
|
1136
|
+
|
|
1137
|
+
sage: from sage.dynamics.finite_dynamical_system import InvertibleFiniteDynamicalSystem
|
|
1138
|
+
sage: D = InvertibleFiniteDynamicalSystem(tuple(range(5)), lambda x: (x + 2) % 5)
|
|
1139
|
+
sage: D.ground_set()
|
|
1140
|
+
(0, 1, 2, 3, 4)
|
|
1141
|
+
sage: D.evolution()(4)
|
|
1142
|
+
1
|
|
1143
|
+
sage: D.orbits()
|
|
1144
|
+
[[4, 1, 3, 0, 2]]
|
|
1145
|
+
sage: D.inverse_evolution()(2)
|
|
1146
|
+
0
|
|
1147
|
+
sage: D.inverse_evolution()(1)
|
|
1148
|
+
4
|
|
1149
|
+
sage: D.evolution_power(-1)(1)
|
|
1150
|
+
4
|
|
1151
|
+
sage: D.evolution_power(-2)(1)
|
|
1152
|
+
2
|
|
1153
|
+
|
|
1154
|
+
sage: X = cartesian_product([[0, 1]]*8)
|
|
1155
|
+
sage: Y = [s for s in X if sum(s) == 4]
|
|
1156
|
+
sage: rot = lambda s : s[1:] + (s[0],)
|
|
1157
|
+
sage: D = InvertibleFiniteDynamicalSystem(Y, rot)
|
|
1158
|
+
sage: D.evolution()((0, 1, 1, 0, 1, 0, 0, 1))
|
|
1159
|
+
(1, 1, 0, 1, 0, 0, 1, 0)
|
|
1160
|
+
sage: D.inverse_evolution()((0, 1, 1, 0, 1, 0, 0, 1))
|
|
1161
|
+
(1, 0, 1, 1, 0, 1, 0, 0)
|
|
1162
|
+
sage: sorted(D.orbit_lengths())
|
|
1163
|
+
[2, 4, 8, 8, 8, 8, 8, 8, 8, 8]
|
|
1164
|
+
"""
|
|
1165
|
+
def _repr_(self):
|
|
1166
|
+
r"""
|
|
1167
|
+
String representation of ``self``.
|
|
1168
|
+
|
|
1169
|
+
EXAMPLES::
|
|
1170
|
+
|
|
1171
|
+
sage: D = DiscreteDynamicalSystem(tuple(range(5)), lambda x: (x + 2) % 5, inverse=True)
|
|
1172
|
+
sage: D
|
|
1173
|
+
An invertible finite discrete dynamical system with ground set
|
|
1174
|
+
(0, 1, 2, 3, 4)
|
|
1175
|
+
"""
|
|
1176
|
+
return "An invertible finite discrete dynamical system with ground set " \
|
|
1177
|
+
+ repr(self._X)
|
|
1178
|
+
|
|
1179
|
+
def orbits(self):
|
|
1180
|
+
r"""
|
|
1181
|
+
Return a list of all orbits of ``self``, up to
|
|
1182
|
+
cyclic rotation.
|
|
1183
|
+
|
|
1184
|
+
EXAMPLES::
|
|
1185
|
+
|
|
1186
|
+
sage: D = DiscreteDynamicalSystem(tuple(range(6)), lambda x: (x + 2) % 6, inverse=True)
|
|
1187
|
+
sage: D.orbits()
|
|
1188
|
+
[[5, 1, 3], [4, 0, 2]]
|
|
1189
|
+
sage: D = DiscreteDynamicalSystem(tuple(range(6)), lambda x: (x + 3) % 6, inverse=True)
|
|
1190
|
+
sage: D.orbits()
|
|
1191
|
+
[[5, 2], [4, 1], [3, 0]]
|
|
1192
|
+
"""
|
|
1193
|
+
phi = self._phi
|
|
1194
|
+
l = list(self)
|
|
1195
|
+
orbs = []
|
|
1196
|
+
while l:
|
|
1197
|
+
start = l.pop()
|
|
1198
|
+
orb = [start]
|
|
1199
|
+
curr = phi(start)
|
|
1200
|
+
while curr != start:
|
|
1201
|
+
l.remove(curr)
|
|
1202
|
+
orb.append(curr)
|
|
1203
|
+
curr = phi(curr)
|
|
1204
|
+
orbs.append(orb)
|
|
1205
|
+
return orbs
|
|
1206
|
+
|
|
1207
|
+
def cycles(self):
|
|
1208
|
+
r"""
|
|
1209
|
+
Return a list of all cycles of ``self``, up to
|
|
1210
|
+
cyclic rotation.
|
|
1211
|
+
|
|
1212
|
+
We recall the definition of cycles:
|
|
1213
|
+
Let `(X, \phi)` be a DDS.
|
|
1214
|
+
A *cycle* of `(X, \phi)` is a finite list
|
|
1215
|
+
`u = (u_1, u_2, \ldots, u_k)` of elements of `X` such
|
|
1216
|
+
that `\phi(u_i) = u_{i+1}` for each `i \leq k`, where we
|
|
1217
|
+
set `u_{k+1} = u_1`.
|
|
1218
|
+
Note that any element of `X` whose orbit is finite has a
|
|
1219
|
+
cycle in its orbit.
|
|
1220
|
+
|
|
1221
|
+
Since ``self`` is invertible, the cycles of ``self``
|
|
1222
|
+
are the same as its orbits.
|
|
1223
|
+
|
|
1224
|
+
EXAMPLES::
|
|
1225
|
+
|
|
1226
|
+
sage: D = DiscreteDynamicalSystem(tuple(range(6)), lambda x: (x + 2) % 6, inverse=True)
|
|
1227
|
+
sage: D.cycles()
|
|
1228
|
+
[[5, 1, 3], [4, 0, 2]]
|
|
1229
|
+
sage: D = DiscreteDynamicalSystem(tuple(range(6)), lambda x: (x + 3) % 6, inverse=True)
|
|
1230
|
+
sage: D.cycles()
|
|
1231
|
+
[[5, 2], [4, 1], [3, 0]]
|
|
1232
|
+
"""
|
|
1233
|
+
return self.orbits()
|
|
1234
|
+
|
|
1235
|
+
def orbit_lengths(self):
|
|
1236
|
+
r"""
|
|
1237
|
+
Return a list of the lengths of all orbits of
|
|
1238
|
+
``self``.
|
|
1239
|
+
|
|
1240
|
+
EXAMPLES::
|
|
1241
|
+
|
|
1242
|
+
sage: D = DiscreteDynamicalSystem(tuple(range(6)), lambda x: (x + 2) % 6, inverse=True)
|
|
1243
|
+
sage: D.orbit_lengths()
|
|
1244
|
+
[3, 3]
|
|
1245
|
+
sage: D = DiscreteDynamicalSystem(tuple(range(6)), lambda x: (x + 3) % 6, inverse=True)
|
|
1246
|
+
sage: D.orbit_lengths()
|
|
1247
|
+
[2, 2, 2]
|
|
1248
|
+
"""
|
|
1249
|
+
return [len(orb) for orb in self.orbits()]
|