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,4897 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-combinat
|
|
2
|
+
# sage.doctest: needs requests sage.combinat
|
|
3
|
+
r"""
|
|
4
|
+
FindStat - the search engine for combinatorial statistics and maps
|
|
5
|
+
|
|
6
|
+
The interface to the FindStat database is ::
|
|
7
|
+
|
|
8
|
+
sage: findstat()
|
|
9
|
+
The Combinatorial Statistic Finder (https://www.findstat.org/)
|
|
10
|
+
|
|
11
|
+
We use the following three notions
|
|
12
|
+
|
|
13
|
+
- A *combinatorial collection* is a set `S` with interesting combinatorial properties,
|
|
14
|
+
- a *combinatorial map* is a combinatorially interesting map `f: S \to S'` between combinatorial collections, and
|
|
15
|
+
- a *combinatorial statistic* is a combinatorially interesting map `s: S \to \ZZ`.
|
|
16
|
+
|
|
17
|
+
You can use the FindStat interface to
|
|
18
|
+
|
|
19
|
+
- identify a combinatorial statistic or map given the values on a few small objects,
|
|
20
|
+
- obtain more terms, formulae, references, etc. for a given statistic or map,
|
|
21
|
+
- edit statistics and maps and submit new statistics.
|
|
22
|
+
|
|
23
|
+
The main entry points to the database are
|
|
24
|
+
|
|
25
|
+
- :func:`findstat` to search for matching statistics,
|
|
26
|
+
- :func:`findmap` to search for matching maps.
|
|
27
|
+
|
|
28
|
+
A guided tour
|
|
29
|
+
-------------
|
|
30
|
+
|
|
31
|
+
Retrieving information
|
|
32
|
+
^^^^^^^^^^^^^^^^^^^^^^
|
|
33
|
+
|
|
34
|
+
The most straightforward application of the FindStat interface is to
|
|
35
|
+
gather information about a combinatorial statistic. To do this, we
|
|
36
|
+
supply :func:`findstat` with a list of ``(object, value)`` pairs.
|
|
37
|
+
For example::
|
|
38
|
+
|
|
39
|
+
sage: PM = PerfectMatchings
|
|
40
|
+
sage: r = findstat([(m, m.number_of_nestings()) for n in range(6) for m in PM(2*n)], depth=1); r # optional -- internet
|
|
41
|
+
0: St000042oMp00116 (quality [100, 100])
|
|
42
|
+
1: St000041 (quality [20, 100])
|
|
43
|
+
...
|
|
44
|
+
|
|
45
|
+
The result of this query is a list (presented as a
|
|
46
|
+
:class:`sage.databases.oeis.FancyTuple`) of matches. Each match
|
|
47
|
+
consists of a :class:`FindStatCompoundStatistic` `s: S \to \ZZ` and
|
|
48
|
+
an indication of the quality of the match.
|
|
49
|
+
|
|
50
|
+
The precise meaning of the result is as follows:
|
|
51
|
+
|
|
52
|
+
The composition `f_n \circ ... \circ f_2 \circ f_1` applied to
|
|
53
|
+
the objects sent to FindStat agrees with all ``(object, value)``
|
|
54
|
+
pairs of `s` in the database. The optional parameter ``depth=1``
|
|
55
|
+
limits the output to `n=1`.
|
|
56
|
+
|
|
57
|
+
Suppose that the quality of the match is `(q_a, q_d)`. Then
|
|
58
|
+
`q_a` is the percentage of ``(object, value)`` pairs that are in
|
|
59
|
+
the database among those which were sent to FindStat, and `q_d`
|
|
60
|
+
is the percentage of ``(object, value)`` pairs with distinct
|
|
61
|
+
values in the database among those which were sent to FindStat.
|
|
62
|
+
|
|
63
|
+
Put differently, if ``quality`` is not too small it is likely that
|
|
64
|
+
the statistic sent to FindStat equals `s \circ f_n \circ ... \circ
|
|
65
|
+
f_2 \circ f_1`. If `q_a` is large, but `q_b` is small, then there
|
|
66
|
+
were many matches, but while the sought for statistic attains many
|
|
67
|
+
distinct values, the match found by FindStat covers only ``(object,
|
|
68
|
+
value)`` pairs for few values.
|
|
69
|
+
|
|
70
|
+
In the case at hand, for the match ``St000041``, the list of maps is
|
|
71
|
+
empty. We can retrieve the description of the statistic from the
|
|
72
|
+
database as follows::
|
|
73
|
+
|
|
74
|
+
sage: print(r[1].statistic().description()) # optional -- internet
|
|
75
|
+
The number of nestings of a perfect matching.
|
|
76
|
+
<BLANKLINE>
|
|
77
|
+
<BLANKLINE>
|
|
78
|
+
This is the number of pairs of edges $((a,b), (c,d))$ such that $a\le c\le d\le b$. i.e., the edge $(c,d)$ is nested inside $(a,b)$...
|
|
79
|
+
|
|
80
|
+
We can check the references::
|
|
81
|
+
|
|
82
|
+
sage: r[1].statistic().references() # optional -- internet
|
|
83
|
+
0: [1] de Médicis, A., Viennot, X. G., Moments des $q$-polynômes de Laguerre et la bijection de Foata-Zeilberger [[MathSciNet:1288802]]
|
|
84
|
+
1: [2] Simion, R., Stanton, D., Octabasic Laguerre polynomials and permutation statistics [[MathSciNet:1418763]]...
|
|
85
|
+
|
|
86
|
+
If you prefer, you can look at this information also in your browser::
|
|
87
|
+
|
|
88
|
+
sage: r[1].statistic().browse() # optional -- webbrowser
|
|
89
|
+
|
|
90
|
+
Another interesting possibility is to look for equidistributed
|
|
91
|
+
statistics. Instead of submitting a list of ``(object, value)``
|
|
92
|
+
pairs, we pass a list of pairs ``(objects, values)``::
|
|
93
|
+
|
|
94
|
+
sage: data = [(PM(2*n), [m.number_of_nestings() for m in PM(2*n)]) for n in range(5)]
|
|
95
|
+
sage: findstat(data, depth=0) # optional -- internet
|
|
96
|
+
0: St000041 (quality [99, 100])
|
|
97
|
+
1: St000042 (quality [99, 100])
|
|
98
|
+
|
|
99
|
+
This results tells us that the database contains another entry that
|
|
100
|
+
is equidistributed with the number of nestings on perfect matchings
|
|
101
|
+
of size at most `10`, namely the number of crossings. Note that
|
|
102
|
+
there is a limit on the number of elements FindStat accepts for a
|
|
103
|
+
query, which is currently `1000`. Queries with more than `1000`
|
|
104
|
+
elements are truncated.
|
|
105
|
+
|
|
106
|
+
Let us now look at a slightly more complicated example, where the
|
|
107
|
+
submitted statistic is the composition of a sequence of combinatorial
|
|
108
|
+
maps and a statistic known to FindStat. We use the occasion to
|
|
109
|
+
advertise yet another way to pass values to FindStat::
|
|
110
|
+
|
|
111
|
+
sage: r = findstat(Permutations, lambda pi: pi.saliances()[0], depth=2); r # optional -- internet
|
|
112
|
+
0: St000740oMp00066 with offset 1 (quality [100, 100])
|
|
113
|
+
1: St000740oMp00087 with offset 1 (quality [100, 100])
|
|
114
|
+
...
|
|
115
|
+
|
|
116
|
+
Note that some of the matches are up to a global offset. For
|
|
117
|
+
example, we have::
|
|
118
|
+
|
|
119
|
+
sage: r[1].info() # optional -- internet
|
|
120
|
+
after adding 1 to every value
|
|
121
|
+
and applying
|
|
122
|
+
Mp00087: inverse first fundamental transformation: Permutations -> Permutations
|
|
123
|
+
to the objects (see `.compound_map()` for details)
|
|
124
|
+
<BLANKLINE>
|
|
125
|
+
your input matches
|
|
126
|
+
St000740: The last entry of a permutation.
|
|
127
|
+
<BLANKLINE>
|
|
128
|
+
among the values you sent, 100 percent are actually in the database,
|
|
129
|
+
among the distinct values you sent, 100 percent are actually in the database
|
|
130
|
+
|
|
131
|
+
Let us pick another particular result::
|
|
132
|
+
|
|
133
|
+
sage: s = findstat("St000051oMp00061oMp00069") # optional -- internet
|
|
134
|
+
sage: s.info() # optional -- internet
|
|
135
|
+
Mp00069: complement: Permutations -> Permutations
|
|
136
|
+
Mp00061: to increasing tree: Permutations -> Binary trees
|
|
137
|
+
St000051: The size of the left subtree of a binary tree.
|
|
138
|
+
|
|
139
|
+
To obtain the value of the statistic sent to FindStat on a given
|
|
140
|
+
object, apply the maps in the list in the given order to this object,
|
|
141
|
+
and evaluate the statistic on the result. For example, let us check
|
|
142
|
+
that the result given by FindStat agrees with our statistic on the
|
|
143
|
+
following permutation::
|
|
144
|
+
|
|
145
|
+
sage: pi = Permutation([3,1,4,5,2]); pi.saliances()[0]
|
|
146
|
+
3
|
|
147
|
+
|
|
148
|
+
We first have to find out, what the maps and the statistic actually do::
|
|
149
|
+
|
|
150
|
+
sage: print(s.statistic().description()) # optional -- internet
|
|
151
|
+
The size of the left subtree of a binary tree.
|
|
152
|
+
|
|
153
|
+
sage: print(s.statistic().sage_code()) # optional -- internet
|
|
154
|
+
def statistic(T):
|
|
155
|
+
return T[0].node_number()
|
|
156
|
+
|
|
157
|
+
sage: print("\n\n".join(m.sage_code() for m in s.compound_map())) # optional -- internet
|
|
158
|
+
def mapping(sigma):
|
|
159
|
+
return sigma.complement()
|
|
160
|
+
<BLANKLINE>
|
|
161
|
+
def mapping(sigma):
|
|
162
|
+
return sigma.increasing_tree_shape()
|
|
163
|
+
|
|
164
|
+
So, the following should coincide with what we sent FindStat::
|
|
165
|
+
|
|
166
|
+
sage: pi.complement().increasing_tree_shape()[0].node_number()
|
|
167
|
+
3
|
|
168
|
+
|
|
169
|
+
Editing and submitting statistics
|
|
170
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
171
|
+
|
|
172
|
+
Of course, often a statistic will not be in the database::
|
|
173
|
+
|
|
174
|
+
sage: s = findstat([(d, randint(1,1000)) for d in DyckWords(4)]); s # optional -- internet
|
|
175
|
+
St000000: a new statistic on Dyck paths
|
|
176
|
+
|
|
177
|
+
In this case, and if the statistic might be "interesting", please
|
|
178
|
+
consider submitting it to the database using
|
|
179
|
+
:meth:`FindStatStatistic.submit`.
|
|
180
|
+
|
|
181
|
+
Also, you may notice omissions, typos or even mistakes in the
|
|
182
|
+
description, the code and the references. In this case, simply
|
|
183
|
+
replace the value by using :meth:`FindStatFunction.set_description`,
|
|
184
|
+
:meth:`FindStatStatistic.set_code` or
|
|
185
|
+
:meth:`FindStatFunction.set_references_raw`, and then
|
|
186
|
+
:meth:`FindStatStatistic.submit` your changes for review by the
|
|
187
|
+
FindStat team.
|
|
188
|
+
|
|
189
|
+
AUTHORS:
|
|
190
|
+
|
|
191
|
+
- Martin Rubey (2015): initial version
|
|
192
|
+
- Martin Rubey (2020): rewrite, adapt to new FindStat API
|
|
193
|
+
"""
|
|
194
|
+
# ****************************************************************************
|
|
195
|
+
# Copyright (C) 2015 Martin Rubey <martin.rubey@tuwien.ac.at>,
|
|
196
|
+
#
|
|
197
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
198
|
+
# as published by the Free Software Foundation; either version 2 of
|
|
199
|
+
# the License, or (at your option) any later version.
|
|
200
|
+
# https://www.gnu.org/licenses/
|
|
201
|
+
# ****************************************************************************
|
|
202
|
+
from sage.misc.lazy_list import lazy_list
|
|
203
|
+
from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass
|
|
204
|
+
from sage.structure.element import Element
|
|
205
|
+
from sage.structure.parent import Parent
|
|
206
|
+
from sage.structure.unique_representation import UniqueRepresentation
|
|
207
|
+
|
|
208
|
+
from sage.categories.sets_cat import Sets
|
|
209
|
+
from sage.structure.sage_object import SageObject
|
|
210
|
+
from sage.structure.richcmp import richcmp
|
|
211
|
+
|
|
212
|
+
from sage.misc.verbose import verbose
|
|
213
|
+
from sage.rings.integer import Integer
|
|
214
|
+
from sage.databases.oeis import FancyTuple
|
|
215
|
+
|
|
216
|
+
from ast import literal_eval
|
|
217
|
+
from copy import deepcopy
|
|
218
|
+
from pathlib import Path
|
|
219
|
+
import re
|
|
220
|
+
import webbrowser
|
|
221
|
+
import tempfile
|
|
222
|
+
import inspect
|
|
223
|
+
import html
|
|
224
|
+
import requests
|
|
225
|
+
from json.decoder import JSONDecodeError
|
|
226
|
+
import itertools
|
|
227
|
+
|
|
228
|
+
# Combinatorial collections
|
|
229
|
+
from sage.combinat.alternating_sign_matrix import AlternatingSignMatrix, AlternatingSignMatrices
|
|
230
|
+
from sage.combinat.binary_tree import BinaryTree, BinaryTrees
|
|
231
|
+
from sage.combinat.core import Core, Cores
|
|
232
|
+
from sage.combinat.dyck_word import DyckWord, DyckWords
|
|
233
|
+
from sage.combinat.root_system.cartan_type import CartanType_abstract, CartanType
|
|
234
|
+
from sage.combinat.gelfand_tsetlin_patterns import GelfandTsetlinPattern, GelfandTsetlinPatterns
|
|
235
|
+
from sage.graphs.graph import Graph
|
|
236
|
+
from sage.combinat.composition import Composition, Compositions
|
|
237
|
+
from sage.combinat.partition import Partition, Partitions
|
|
238
|
+
from sage.combinat.ordered_tree import OrderedTree, OrderedTrees
|
|
239
|
+
from sage.combinat.parking_functions import ParkingFunction, ParkingFunctions
|
|
240
|
+
from sage.combinat.perfect_matching import PerfectMatching, PerfectMatchings
|
|
241
|
+
from sage.combinat.permutation import Permutation, Permutations
|
|
242
|
+
from sage.combinat.posets.posets import Poset, FinitePoset
|
|
243
|
+
from sage.combinat.posets.lattices import LatticePoset, FiniteLatticePoset
|
|
244
|
+
from sage.combinat.posets.poset_examples import Posets
|
|
245
|
+
from sage.combinat.tableau import SemistandardTableau, SemistandardTableaux, StandardTableau, StandardTableaux
|
|
246
|
+
from sage.combinat.set_partition import SetPartition, SetPartitions
|
|
247
|
+
from sage.combinat.skew_partition import SkewPartition, SkewPartitions
|
|
248
|
+
from sage.graphs.graph_generators import graphs
|
|
249
|
+
from sage.combinat.words.word import Word
|
|
250
|
+
from sage.combinat.words.words import Words
|
|
251
|
+
from sage.combinat.words.abstract_word import Word_class
|
|
252
|
+
from sage.combinat.colored_permutations import SignedPermutation, SignedPermutations
|
|
253
|
+
from sage.combinat.plane_partition import PlanePartition
|
|
254
|
+
from sage.combinat.decorated_permutation import DecoratedPermutation, DecoratedPermutations
|
|
255
|
+
from sage.combinat.set_partition_ordered import OrderedSetPartition, OrderedSetPartitions
|
|
256
|
+
|
|
257
|
+
######################################################################
|
|
258
|
+
# the FindStat URLs
|
|
259
|
+
FINDSTAT_URL = 'https://www.findstat.org/'
|
|
260
|
+
FINDSTAT_API = FINDSTAT_URL + "api/"
|
|
261
|
+
FINDSTAT_API_COLLECTIONS = FINDSTAT_API + 'CollectionsDatabase/'
|
|
262
|
+
FINDSTAT_API_STATISTICS = FINDSTAT_API + 'StatisticsDatabase/'
|
|
263
|
+
FINDSTAT_API_MAPS = FINDSTAT_API + 'MapsDatabase/'
|
|
264
|
+
|
|
265
|
+
FINDSTAT_URL_LOGIN = FINDSTAT_URL + "?action=login"
|
|
266
|
+
FINDSTAT_URL_COLLECTIONS = FINDSTAT_URL + 'CollectionsDatabase/'
|
|
267
|
+
FINDSTAT_URL_STATISTICS = FINDSTAT_URL + 'StatisticsDatabase/'
|
|
268
|
+
FINDSTAT_URL_EDIT_STATISTIC = FINDSTAT_URL + 'EditStatistic/'
|
|
269
|
+
FINDSTAT_URL_NEW_STATISTIC = FINDSTAT_URL + 'NewStatistic/'
|
|
270
|
+
FINDSTAT_URL_MAPS = FINDSTAT_URL + 'MapsDatabase/'
|
|
271
|
+
FINDSTAT_URL_EDIT_MAP = FINDSTAT_URL + 'EditMap/'
|
|
272
|
+
FINDSTAT_URL_NEW_MAP = FINDSTAT_URL + 'NewMap/'
|
|
273
|
+
|
|
274
|
+
######################################################################
|
|
275
|
+
# the number of values FindStat allows to search for at most
|
|
276
|
+
FINDSTAT_MAX_VALUES = 1000
|
|
277
|
+
# the number of values FindStat needs at least to search for
|
|
278
|
+
FINDSTAT_MIN_VALUES = 3
|
|
279
|
+
# the maximal number of maps that FindStat composes by default to find a match
|
|
280
|
+
FINDSTAT_DEFAULT_DEPTH = 2
|
|
281
|
+
# the number of values FindStat allows to submit at most
|
|
282
|
+
FINDSTAT_MAX_SUBMISSION_VALUES = 1200
|
|
283
|
+
|
|
284
|
+
# separates name from description
|
|
285
|
+
FINDSTAT_SEPARATOR_NAME = "\n"
|
|
286
|
+
# separates values
|
|
287
|
+
FINDSTAT_VALUE_SEPARATOR = ";"
|
|
288
|
+
FINDSTAT_MAP_SEPARATOR = "o"
|
|
289
|
+
# regexp to recognize a statistic identifier
|
|
290
|
+
FINDSTAT_STATISTIC_REGEXP = '^St[0-9]{6}$'
|
|
291
|
+
FINDSTAT_MAP_REGEXP = '^Mp[0-9]{5}$'
|
|
292
|
+
FINDSTAT_COLLECTION_REGEXP = '^Cc[0-9]{4}$'
|
|
293
|
+
FINDSTAT_STATISTIC_PADDED_IDENTIFIER = "St%06d"
|
|
294
|
+
FINDSTAT_MAP_PADDED_IDENTIFIER = "Mp%05d"
|
|
295
|
+
FINDSTAT_COLLECTION_PADDED_IDENTIFIER = "Cc%04d"
|
|
296
|
+
|
|
297
|
+
######################################################################
|
|
298
|
+
|
|
299
|
+
# the format string for using POST
|
|
300
|
+
# WARNING: we use html.escape to avoid injection problems, thus we expect double quotes as field delimiters.
|
|
301
|
+
FINDSTAT_POST_HEADER = """
|
|
302
|
+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
|
|
303
|
+
<script>
|
|
304
|
+
$(document).ready(function() {$("#form").submit(); });
|
|
305
|
+
</script>
|
|
306
|
+
"""
|
|
307
|
+
FINDSTAT_NEWSTATISTIC_FORM_HEADER = '<form id="form" name="NewStatistic" action="%s" enctype="multipart/form-data" method="post" />'
|
|
308
|
+
FINDSTAT_NEWMAP_FORM_HEADER = '<form id="form" name="NewMap" action="%s" enctype="multipart/form-data" method="post" />'
|
|
309
|
+
FINDSTAT_FORM_FORMAT = '<input type="hidden" name="%s" value="%s" />'
|
|
310
|
+
FINDSTAT_FORM_FOOTER = '</form>'
|
|
311
|
+
|
|
312
|
+
######################################################################
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
class FindStat(UniqueRepresentation, SageObject):
|
|
316
|
+
r"""
|
|
317
|
+
The Combinatorial Statistic Finder.
|
|
318
|
+
|
|
319
|
+
:class:`FindStat` is a class preserving user information.
|
|
320
|
+
"""
|
|
321
|
+
def __init__(self):
|
|
322
|
+
r"""
|
|
323
|
+
Initialize the database.
|
|
324
|
+
|
|
325
|
+
TESTS::
|
|
326
|
+
|
|
327
|
+
sage: findstat()
|
|
328
|
+
The Combinatorial Statistic Finder (https://www.findstat.org/)
|
|
329
|
+
"""
|
|
330
|
+
# user credentials if provided
|
|
331
|
+
self._user_name = ""
|
|
332
|
+
self._user_email = ""
|
|
333
|
+
self._allow_execution = False
|
|
334
|
+
|
|
335
|
+
def __repr__(self):
|
|
336
|
+
r"""
|
|
337
|
+
Return the representation of ``self``.
|
|
338
|
+
|
|
339
|
+
EXAMPLES::
|
|
340
|
+
|
|
341
|
+
sage: findstat()
|
|
342
|
+
The Combinatorial Statistic Finder (https://www.findstat.org/)
|
|
343
|
+
"""
|
|
344
|
+
return "The Combinatorial Statistic Finder (%s)" % FINDSTAT_URL
|
|
345
|
+
|
|
346
|
+
def browse(self):
|
|
347
|
+
r"""
|
|
348
|
+
Open the FindStat web page in a browser.
|
|
349
|
+
|
|
350
|
+
EXAMPLES::
|
|
351
|
+
|
|
352
|
+
sage: findstat().browse() # optional -- webbrowser
|
|
353
|
+
"""
|
|
354
|
+
webbrowser.open(FINDSTAT_URL)
|
|
355
|
+
|
|
356
|
+
def set_user(self, name=None, email=None):
|
|
357
|
+
r"""
|
|
358
|
+
Set the user for the session.
|
|
359
|
+
|
|
360
|
+
INPUT:
|
|
361
|
+
|
|
362
|
+
- ``name`` -- the name of the user
|
|
363
|
+
|
|
364
|
+
- ``email`` -- an email address of the user
|
|
365
|
+
|
|
366
|
+
This information is used when submitting a statistic with
|
|
367
|
+
:meth:`FindStatStatistic.submit`.
|
|
368
|
+
|
|
369
|
+
EXAMPLES::
|
|
370
|
+
|
|
371
|
+
sage: findstat().set_user(name='Anonymous', email='invalid@org')
|
|
372
|
+
|
|
373
|
+
.. NOTE::
|
|
374
|
+
|
|
375
|
+
It is usually more convenient to login into the FindStat
|
|
376
|
+
web page using the :meth:`login` method.
|
|
377
|
+
"""
|
|
378
|
+
if not isinstance(name, str):
|
|
379
|
+
raise ValueError("the given name (%s) should be a string" % name)
|
|
380
|
+
if not isinstance(email, str):
|
|
381
|
+
raise ValueError("the given email (%s) should be a string" % email)
|
|
382
|
+
self._user_name = name
|
|
383
|
+
self._user_email = email
|
|
384
|
+
|
|
385
|
+
def user_name(self):
|
|
386
|
+
"""
|
|
387
|
+
Return the user name used for submissions.
|
|
388
|
+
|
|
389
|
+
EXAMPLES::
|
|
390
|
+
|
|
391
|
+
sage: findstat().set_user(name='Anonymous', email='invalid@org')
|
|
392
|
+
sage: findstat().user_name()
|
|
393
|
+
'Anonymous'
|
|
394
|
+
"""
|
|
395
|
+
return self._user_name
|
|
396
|
+
|
|
397
|
+
def user_email(self):
|
|
398
|
+
"""
|
|
399
|
+
Return the user name used for submissions.
|
|
400
|
+
|
|
401
|
+
EXAMPLES::
|
|
402
|
+
|
|
403
|
+
sage: findstat().set_user(name='Anonymous', email='invalid@org')
|
|
404
|
+
sage: findstat().user_email()
|
|
405
|
+
'invalid@org'
|
|
406
|
+
"""
|
|
407
|
+
return self._user_email
|
|
408
|
+
|
|
409
|
+
def login(self):
|
|
410
|
+
r"""
|
|
411
|
+
Open the FindStat login page in a browser.
|
|
412
|
+
|
|
413
|
+
EXAMPLES::
|
|
414
|
+
|
|
415
|
+
sage: findstat().login() # optional -- webbrowser
|
|
416
|
+
"""
|
|
417
|
+
webbrowser.open(FINDSTAT_URL_LOGIN)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
######################################################################
|
|
421
|
+
# tools
|
|
422
|
+
######################################################################
|
|
423
|
+
def _get_json(url, **kwargs):
|
|
424
|
+
"""
|
|
425
|
+
Return the json response or raise an error.
|
|
426
|
+
|
|
427
|
+
EXAMPLES::
|
|
428
|
+
|
|
429
|
+
sage: from sage.databases.findstat import _get_json, FINDSTAT_API_MAPS
|
|
430
|
+
sage: _get_json(FINDSTAT_API_MAPS + "?fields=yyy") # optional -- internet
|
|
431
|
+
Traceback (most recent call last):
|
|
432
|
+
...
|
|
433
|
+
ValueError: E018: Unknown fields in Map, Combinatorial map: to semistandard tableau via monotone triangles: ['yyy']
|
|
434
|
+
"""
|
|
435
|
+
response = requests.get(url)
|
|
436
|
+
if response.ok:
|
|
437
|
+
try:
|
|
438
|
+
result = response.json(**kwargs)
|
|
439
|
+
except JSONDecodeError:
|
|
440
|
+
raise ValueError(response.text)
|
|
441
|
+
if "error" in result:
|
|
442
|
+
raise ValueError(result["error"])
|
|
443
|
+
return result
|
|
444
|
+
raise ConnectionError(response.text)
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def _post_json(url, data, **kwargs):
|
|
448
|
+
"""
|
|
449
|
+
Return the json response or raise an error.
|
|
450
|
+
|
|
451
|
+
EXAMPLES::
|
|
452
|
+
|
|
453
|
+
sage: from sage.databases.findstat import _post_json, FINDSTAT_API_STATISTICS
|
|
454
|
+
sage: _post_json(FINDSTAT_API_STATISTICS, {"fields": "yyy"}) # optional -- internet
|
|
455
|
+
Traceback (most recent call last):
|
|
456
|
+
...
|
|
457
|
+
ValueError: E018: Unknown fields in Statistic, Combinatorial statistic: St000001: ['yyy']
|
|
458
|
+
"""
|
|
459
|
+
response = requests.post(url, data=data)
|
|
460
|
+
if response.ok:
|
|
461
|
+
try:
|
|
462
|
+
result = response.json(**kwargs)
|
|
463
|
+
except JSONDecodeError:
|
|
464
|
+
raise ValueError(response.text)
|
|
465
|
+
if "error" in result:
|
|
466
|
+
raise ValueError(result["error"])
|
|
467
|
+
return result
|
|
468
|
+
raise ConnectionError(response.text)
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def _submit(args, url):
|
|
472
|
+
r"""
|
|
473
|
+
Open a post form containing fields for each of the arguments,
|
|
474
|
+
which is sent to the given url.
|
|
475
|
+
|
|
476
|
+
INPUT:
|
|
477
|
+
|
|
478
|
+
- args, a dictionary whose keys are the form fields
|
|
479
|
+
|
|
480
|
+
- url, the goal of the post request
|
|
481
|
+
|
|
482
|
+
EXAMPLES::
|
|
483
|
+
|
|
484
|
+
sage: from sage.databases.findstat import _submit, FINDSTAT_NEWSTATISTIC_FORM_HEADER, FINDSTAT_URL_NEW_STATISTIC
|
|
485
|
+
sage: url = FINDSTAT_NEWSTATISTIC_FORM_HEADER % FINDSTAT_URL_NEW_STATISTIC
|
|
486
|
+
sage: args = {"OriginalStatistic": "St000000",
|
|
487
|
+
....: "Domain": "Cc0001",
|
|
488
|
+
....: "Values": "[1,3,2]=>17\n[1,2,3]=>83",
|
|
489
|
+
....: "Description": "Not a good statistic",
|
|
490
|
+
....: "References": "[[arXiv:1102.4226]]",
|
|
491
|
+
....: "Code": "def statistic(p):\n return 1",
|
|
492
|
+
....: "SageCode": "def statistic(p):\n return 1",
|
|
493
|
+
....: "CurrentAuthor": "",
|
|
494
|
+
....: "CurrentEmail": ""}
|
|
495
|
+
sage: _submit(args, url) # optional -- webbrowser
|
|
496
|
+
"""
|
|
497
|
+
f = tempfile.NamedTemporaryFile(mode='w', suffix='.html', encoding='utf-8', delete=False)
|
|
498
|
+
verbose("Created temporary file %s" % f.name, caller_name='FindStat')
|
|
499
|
+
f.write('<!doctype html>\n<html lang="en">\n<meta charset="utf-8">\n')
|
|
500
|
+
f.write(FINDSTAT_POST_HEADER)
|
|
501
|
+
f.write(url)
|
|
502
|
+
for key, value in args.items():
|
|
503
|
+
if value:
|
|
504
|
+
verbose("writing argument %s" % key, caller_name='FindStat')
|
|
505
|
+
value_encoded = html.escape(value, quote=True)
|
|
506
|
+
html_content = FINDSTAT_FORM_FORMAT % (key, value_encoded)
|
|
507
|
+
f.write(html_content)
|
|
508
|
+
else:
|
|
509
|
+
verbose("skipping argument %s because it is empty" % key, caller_name='FindStat')
|
|
510
|
+
f.write(FINDSTAT_FORM_FOOTER)
|
|
511
|
+
f.close()
|
|
512
|
+
verbose("Opening file with webbrowser", caller_name='FindStat')
|
|
513
|
+
webbrowser.open(Path(f.name).as_uri())
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
def _data_to_str(data, domain, codomain=None):
|
|
517
|
+
"""
|
|
518
|
+
Return a string representation of the given list of ``(objects,
|
|
519
|
+
values)`` pairs suitable for a FindStat query.
|
|
520
|
+
|
|
521
|
+
INPUT:
|
|
522
|
+
|
|
523
|
+
- ``data`` -- list of lists of objects
|
|
524
|
+
|
|
525
|
+
- ``domain`` -- :class:`FindStatCollection`
|
|
526
|
+
|
|
527
|
+
- ``codomain`` -- (optional) :class:`FindStatCollection` or ``None``
|
|
528
|
+
|
|
529
|
+
If ``codomain`` is ``None``, the values are treated as integers.
|
|
530
|
+
|
|
531
|
+
TESTS::
|
|
532
|
+
|
|
533
|
+
sage: n = 3; l = lambda i: [pi for pi in Permutations(n) if pi(1) == i]
|
|
534
|
+
sage: data = [([pi for pi in l(i)], [pi(1) for pi in l(i)]) for i in range(1,n+1)]
|
|
535
|
+
sage: data.append(([Permutation([1,2])], [1]))
|
|
536
|
+
sage: from sage.databases.findstat import FindStatCollection, _data_to_str
|
|
537
|
+
sage: print(_data_to_str(data, FindStatCollection(1))) # optional -- internet
|
|
538
|
+
[1, 2, 3]
|
|
539
|
+
[1, 3, 2]
|
|
540
|
+
====> 1;1
|
|
541
|
+
[2, 1, 3]
|
|
542
|
+
[2, 3, 1]
|
|
543
|
+
====> 2;2
|
|
544
|
+
[3, 1, 2]
|
|
545
|
+
[3, 2, 1]
|
|
546
|
+
====> 3;3
|
|
547
|
+
[1, 2]
|
|
548
|
+
====> 1
|
|
549
|
+
"""
|
|
550
|
+
to_str_dom = domain.to_string()
|
|
551
|
+
if codomain is None:
|
|
552
|
+
to_str_codom = str
|
|
553
|
+
else:
|
|
554
|
+
to_str_codom = codomain.to_string()
|
|
555
|
+
|
|
556
|
+
return "\n".join("\n".join(to_str_dom(element) for element in elements)
|
|
557
|
+
+ "\n====> "
|
|
558
|
+
+ FINDSTAT_VALUE_SEPARATOR.join(to_str_codom(value)
|
|
559
|
+
for value in values)
|
|
560
|
+
for elements, values in data)
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
def _data_from_iterable(iterable, mapping=False, domain=None,
|
|
564
|
+
codomain=None, check=True):
|
|
565
|
+
"""
|
|
566
|
+
Return a list of pairs of lists of the same size, domain, and if
|
|
567
|
+
applicable, codomain.
|
|
568
|
+
|
|
569
|
+
INPUT:
|
|
570
|
+
|
|
571
|
+
- iterable, a pair of lists of the same size, or an iterable of
|
|
572
|
+
pairs, such that every pair consists of two iterables of the
|
|
573
|
+
same size, or a single element and a single value. Every
|
|
574
|
+
object must be a :class:`SageObject`.
|
|
575
|
+
|
|
576
|
+
- ``mapping`` -- boolean (default: ``False``); ``False``, if the codomain
|
|
577
|
+
is ``Integer`` and ``True`` if it is a FindStat collection
|
|
578
|
+
|
|
579
|
+
- ``domain`` -- (default: ``None``) a :class:`FindStatCollection`, if
|
|
580
|
+
``None`` it is guessed from the iterable
|
|
581
|
+
|
|
582
|
+
- ``codomain`` -- (default: ``None``) a :class:`FindStatCollection`, if
|
|
583
|
+
``None`` it is guessed from the iterable
|
|
584
|
+
|
|
585
|
+
TESTS::
|
|
586
|
+
|
|
587
|
+
sage: from sage.databases.findstat import FindStatCollection, _data_from_iterable
|
|
588
|
+
sage: PM = PerfectMatchings
|
|
589
|
+
sage: l = [(PM(2*n), [m.number_of_nestings() for m in PM(2*n)]) for n in range(5)]
|
|
590
|
+
sage: _data_from_iterable(l) # optional -- internet
|
|
591
|
+
(lazy list [([[]], [0]), ([[(1, 2)]], [0]), ([[(1, 2), (3, 4)], [(1, 3), (2, 4)], [(1, 4), (2, 3)]], [0, 0, 1]), ...],
|
|
592
|
+
a subset of Cc0012: Perfect matchings)
|
|
593
|
+
sage: domain = FindStatCollection("Set Partitions") # optional -- internet
|
|
594
|
+
sage: _data_from_iterable(l, domain=domain) # optional -- internet
|
|
595
|
+
(lazy list [([[]], [0]), ([[(1, 2)]], [0]), ([[(1, 2), (3, 4)], [(1, 3), (2, 4)], [(1, 4), (2, 3)]], [0, 0, 1]), ...],
|
|
596
|
+
Cc0009: Set partitions)
|
|
597
|
+
|
|
598
|
+
Check that passing a list with a single ``(elements, values)`` pair works::
|
|
599
|
+
|
|
600
|
+
sage: l = [(PM(4), [0, 0, 1])]
|
|
601
|
+
sage: _data_from_iterable(l, domain=domain) # optional -- internet
|
|
602
|
+
(lazy list [([[(1, 2), (3, 4)], [(1, 3), (2, 4)], [(1, 4), (2, 3)]], [0, 0, 1])],
|
|
603
|
+
Cc0009: Set partitions)
|
|
604
|
+
"""
|
|
605
|
+
if isinstance(iterable, dict):
|
|
606
|
+
iterator = iter(iterable.items())
|
|
607
|
+
else:
|
|
608
|
+
iterator = iter(iterable)
|
|
609
|
+
query0 = next(iterator)
|
|
610
|
+
try:
|
|
611
|
+
query1 = next(iterator)
|
|
612
|
+
except StopIteration:
|
|
613
|
+
# query0 == (elts, vals), which we interpret as [(elts, vals)]
|
|
614
|
+
query0, query1 = query0[0], query0[1]
|
|
615
|
+
# next(iterator) will raise StopIteration
|
|
616
|
+
try:
|
|
617
|
+
query2 = next(iterator)
|
|
618
|
+
pre_data = [query0, query1, query2]
|
|
619
|
+
except StopIteration:
|
|
620
|
+
# (query0, query1) == (elts, vals) or
|
|
621
|
+
# (query0, query1) == [(elts, vals), (elts, vals)]
|
|
622
|
+
# in the former case, elts has at least 3 elements
|
|
623
|
+
# in both cases, query0 and query1 are not objects
|
|
624
|
+
# if query0 is a long list, we have to raise an error anyway
|
|
625
|
+
elts, vals = list(query0), list(query1)
|
|
626
|
+
if len(elts) == 2:
|
|
627
|
+
if len(vals) != 2:
|
|
628
|
+
raise ValueError("cannot interpret the given argument as a FindStat query")
|
|
629
|
+
pre_data = [elts, vals]
|
|
630
|
+
else:
|
|
631
|
+
if len(elts) != len(vals):
|
|
632
|
+
raise ValueError("FindStat expects the same number of objects (got %s) as values (got %s)" % (len(elts), len(vals)))
|
|
633
|
+
pre_data = [(elts, vals)]
|
|
634
|
+
|
|
635
|
+
# pre_data is a list of all elements of the iterator accessed so
|
|
636
|
+
# far, for each of its elements and also the remainder ot the
|
|
637
|
+
# iterator, each element is either a pair ``(object, value)`` or
|
|
638
|
+
# a pair ``(objects, values)``
|
|
639
|
+
elts, vals = pre_data[0]
|
|
640
|
+
if domain is None:
|
|
641
|
+
domain = FindStatCollection(elts)
|
|
642
|
+
if mapping and codomain is None:
|
|
643
|
+
codomain = FindStatCollection(vals)
|
|
644
|
+
|
|
645
|
+
all_elements = set()
|
|
646
|
+
|
|
647
|
+
def sanitize_pair(elts, vals):
|
|
648
|
+
if domain.is_element(elts):
|
|
649
|
+
elts = [elts]
|
|
650
|
+
if mapping:
|
|
651
|
+
vals = [vals]
|
|
652
|
+
else:
|
|
653
|
+
vals = [Integer(vals)]
|
|
654
|
+
else:
|
|
655
|
+
elts = list(elts)
|
|
656
|
+
if check:
|
|
657
|
+
bad = [elt for elt in elts if not domain.is_element(elt)]
|
|
658
|
+
assert not bad, "%s are not elements of %s" % (bad, domain)
|
|
659
|
+
if mapping:
|
|
660
|
+
vals = list(vals)
|
|
661
|
+
else:
|
|
662
|
+
vals = list(map(Integer, vals))
|
|
663
|
+
if len(elts) != len(vals):
|
|
664
|
+
raise ValueError("FindStat expects the same number of objects as values in each pair")
|
|
665
|
+
if check and mapping:
|
|
666
|
+
bad = [elt for elt in vals if not codomain.is_element(elt)]
|
|
667
|
+
assert not bad, "%s are not elements of %s" % (bad, codomain)
|
|
668
|
+
for elt in elts:
|
|
669
|
+
if elt in all_elements:
|
|
670
|
+
raise ValueError("FindStat expects that every object occurs at most once: %s" % elt)
|
|
671
|
+
all_elements.add(elt)
|
|
672
|
+
|
|
673
|
+
return elts, vals
|
|
674
|
+
|
|
675
|
+
lazy_data = lazy_list((sanitize_pair(elts, vals)
|
|
676
|
+
for elts, vals in iterator),
|
|
677
|
+
initial_values=[sanitize_pair(elts, vals)
|
|
678
|
+
for elts, vals in pre_data])
|
|
679
|
+
if mapping:
|
|
680
|
+
return lazy_data, domain, codomain
|
|
681
|
+
return lazy_data, domain
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
def _data_from_function(function, domain):
|
|
685
|
+
"""
|
|
686
|
+
Return a lazy list of pairs of singleton lists of the same size,
|
|
687
|
+
domain.
|
|
688
|
+
|
|
689
|
+
INPUT:
|
|
690
|
+
|
|
691
|
+
- ``function`` -- a callable
|
|
692
|
+
- ``domain`` -- a :class:`FindStatCollection`
|
|
693
|
+
|
|
694
|
+
If ``function`` returns the value ``None``, the pair is omitted.
|
|
695
|
+
|
|
696
|
+
EXAMPLES::
|
|
697
|
+
|
|
698
|
+
sage: from sage.databases.findstat import FindStatCollection, _data_from_function
|
|
699
|
+
sage: domain = FindStatCollection(1) # optional -- internet
|
|
700
|
+
sage: _data_from_function(lambda pi: pi[0], domain) # optional -- internet
|
|
701
|
+
lazy list [([[1]], [1]), ([[1, 2]], [1]), ([[2, 1]], [2]), ...]
|
|
702
|
+
"""
|
|
703
|
+
return lazy_list(([elt], [value])
|
|
704
|
+
for elt, value in domain.first_terms(function)
|
|
705
|
+
if value is not None)
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
def _data_from_data(data, max_values):
|
|
709
|
+
"""
|
|
710
|
+
Return the first few pairs (of lists of the same size) with a
|
|
711
|
+
total of at most ``max_values`` objects in the range of the
|
|
712
|
+
collection.
|
|
713
|
+
|
|
714
|
+
INPUT:
|
|
715
|
+
|
|
716
|
+
- ``data`` -- an iterable over pairs of lists of the same size
|
|
717
|
+
|
|
718
|
+
- ``max_values`` -- the maximal number of objects (and values) to
|
|
719
|
+
return
|
|
720
|
+
|
|
721
|
+
We assume that the number of elements in each pair weakly
|
|
722
|
+
increases, to decide when to stop.
|
|
723
|
+
|
|
724
|
+
TESTS::
|
|
725
|
+
|
|
726
|
+
sage: from sage.databases.findstat import _data_from_data
|
|
727
|
+
sage: PM = PerfectMatchings
|
|
728
|
+
sage: l = [(PM(2*n), (m.number_of_nestings() for m in PM(2*n))) for n in range(2,100)]
|
|
729
|
+
sage: [(a, list(b)) for a, b in _data_from_data(l, 35)]
|
|
730
|
+
[(Perfect matchings of {1, 2, 3, 4}, [0, 0, 1]),
|
|
731
|
+
(Perfect matchings of {1, 2, 3, 4, 5, 6},
|
|
732
|
+
[0, 0, 1, 1, 2, 2, 1, 0, 0, 0, 1, 1, 1, 2, 3])]
|
|
733
|
+
"""
|
|
734
|
+
query = []
|
|
735
|
+
total = min(max_values, FINDSTAT_MAX_VALUES)
|
|
736
|
+
iterator = iter(data)
|
|
737
|
+
while total > 0:
|
|
738
|
+
try:
|
|
739
|
+
elts, vals = next(iterator)
|
|
740
|
+
except StopIteration:
|
|
741
|
+
break
|
|
742
|
+
if total >= len(elts):
|
|
743
|
+
query.append((elts, vals))
|
|
744
|
+
total -= len(elts)
|
|
745
|
+
else:
|
|
746
|
+
break # assuming that the next pair is even larger
|
|
747
|
+
|
|
748
|
+
return query
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
def _distribution_from_data(data, domain, max_values, generating_functions=False):
|
|
752
|
+
"""
|
|
753
|
+
Return the first few pairs (of lists of the same size) with a
|
|
754
|
+
total of at most ``max_values`` objects in the range of the
|
|
755
|
+
collection, combined by level.
|
|
756
|
+
|
|
757
|
+
INPUT:
|
|
758
|
+
|
|
759
|
+
- ``data`` -- an iterable over pairs of lists of the same size
|
|
760
|
+
|
|
761
|
+
- ``domain`` -- a :class:`FindStatCollection`
|
|
762
|
+
|
|
763
|
+
- ``max_values`` -- the maximal number of objects (and values) to
|
|
764
|
+
return
|
|
765
|
+
|
|
766
|
+
TESTS::
|
|
767
|
+
|
|
768
|
+
sage: from sage.databases.findstat import _distribution_from_data, FindStatCollection
|
|
769
|
+
sage: n = 3; l = lambda i: [pi for pi in Permutations(n) if pi(1) == i]
|
|
770
|
+
sage: data = [([pi for pi in l(i)], [pi(1) for pi in l(i)]) for i in range(1,n+1)]
|
|
771
|
+
sage: cc = FindStatCollection(1) # optional -- internet
|
|
772
|
+
sage: _distribution_from_data(data, cc, 10) # optional -- internet
|
|
773
|
+
[([[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]],
|
|
774
|
+
[1, 1, 2, 2, 3, 3])]
|
|
775
|
+
"""
|
|
776
|
+
lvl_dict = {} # lvl: elts, vals
|
|
777
|
+
total = max_values
|
|
778
|
+
iterator = iter(data)
|
|
779
|
+
levels_with_sizes = domain.levels_with_sizes()
|
|
780
|
+
while total > 0:
|
|
781
|
+
try:
|
|
782
|
+
elts, vals = next(iterator)
|
|
783
|
+
except StopIteration:
|
|
784
|
+
break
|
|
785
|
+
if total < len(elts):
|
|
786
|
+
break
|
|
787
|
+
lvl = domain.element_level(elts[0])
|
|
788
|
+
if generating_functions and lvl not in levels_with_sizes:
|
|
789
|
+
continue
|
|
790
|
+
if levels_with_sizes[lvl] > total:
|
|
791
|
+
# we assume that from now on levels become even larger
|
|
792
|
+
break
|
|
793
|
+
if not all(domain.element_level(elt) == lvl for elt in elts[1:]):
|
|
794
|
+
raise ValueError("cannot combine %s into a distribution" % elts)
|
|
795
|
+
lvl_elts, lvl_vals = lvl_dict.get(lvl, [[], []])
|
|
796
|
+
lvl_dict[lvl] = (lvl_elts + elts, lvl_vals + vals)
|
|
797
|
+
if levels_with_sizes[lvl] == len(lvl_dict[lvl][0]):
|
|
798
|
+
total -= levels_with_sizes[lvl]
|
|
799
|
+
|
|
800
|
+
if generating_functions:
|
|
801
|
+
return {lvl: {val: vals.count(val) for val in set(vals)}
|
|
802
|
+
for lvl, (elts, vals) in lvl_dict.items()
|
|
803
|
+
if levels_with_sizes[lvl] == len(vals)}
|
|
804
|
+
|
|
805
|
+
return [(elts, vals) for lvl, (elts, vals) in lvl_dict.items()
|
|
806
|
+
if levels_with_sizes[lvl] == len(elts)]
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
def _generating_functions_from_dict(gfs, style):
|
|
810
|
+
"""
|
|
811
|
+
Convert the generating functions given as a dictionary into a
|
|
812
|
+
desired style.
|
|
813
|
+
|
|
814
|
+
INPUT:
|
|
815
|
+
|
|
816
|
+
- ``gfs`` -- dictionary whose keys are the levels and whose values
|
|
817
|
+
are dictionaries from values to multiplicities
|
|
818
|
+
|
|
819
|
+
- ``style`` -- one of ``'dictionary'``, ``'list'`` or
|
|
820
|
+
``'polynomial'``
|
|
821
|
+
|
|
822
|
+
.. SEEALSO::
|
|
823
|
+
|
|
824
|
+
- :meth:`FindStatCombinatorialStatistic.generating_functions()`
|
|
825
|
+
|
|
826
|
+
TESTS::
|
|
827
|
+
|
|
828
|
+
sage: from sage.databases.findstat import _generating_functions_from_dict
|
|
829
|
+
sage: d = {n: {2*i: n*i+1 for i in range(n, 2*n+1)} for n in range(3)}; d
|
|
830
|
+
{0: {0: 1}, 1: {2: 2, 4: 3}, 2: {4: 5, 6: 7, 8: 9}}
|
|
831
|
+
|
|
832
|
+
sage: _generating_functions_from_dict(d, "polynomial")
|
|
833
|
+
{0: 1, 1: 3*q^4 + 2*q^2, 2: 9*q^8 + 7*q^6 + 5*q^4}
|
|
834
|
+
|
|
835
|
+
sage: _generating_functions_from_dict(d, "list")
|
|
836
|
+
{0: [1], 1: [2, 0, 3], 2: [5, 0, 7, 0, 9]}
|
|
837
|
+
"""
|
|
838
|
+
if style == "dictionary":
|
|
839
|
+
return gfs
|
|
840
|
+
if style == "list":
|
|
841
|
+
return {level: [gen_dict.get(deg, 0)
|
|
842
|
+
for deg in range(min(gen_dict),
|
|
843
|
+
max(gen_dict)+1)]
|
|
844
|
+
for level, gen_dict in gfs.items() if gen_dict}
|
|
845
|
+
if style == "polynomial":
|
|
846
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
847
|
+
from sage.rings.integer_ring import ZZ
|
|
848
|
+
P = PolynomialRing(ZZ, "q", sparse=True)
|
|
849
|
+
q = P.gen()
|
|
850
|
+
return {level: sum(coefficient * q**exponent
|
|
851
|
+
for exponent, coefficient in gen_dict.items())
|
|
852
|
+
for level, gen_dict in gfs.items()}
|
|
853
|
+
|
|
854
|
+
raise ValueError("the argument 'style' (='%s') must be 'dictionary', 'polynomial', or 'list'" % style)
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
def _get_code_from_callable(function):
|
|
858
|
+
"""
|
|
859
|
+
Return code given a callable, if possible.
|
|
860
|
+
|
|
861
|
+
TESTS::
|
|
862
|
+
|
|
863
|
+
sage: from sage.databases.findstat import _get_code_from_callable
|
|
864
|
+
sage: @cached_function
|
|
865
|
+
....: def statistic(pi):
|
|
866
|
+
....: pi = Permutations(len(pi))(pi)
|
|
867
|
+
....: if pi.is_one():
|
|
868
|
+
....: return 1
|
|
869
|
+
....: return sum(statistic(pi.apply_simple_reflection_right(i))
|
|
870
|
+
....: for i in pi.descents())
|
|
871
|
+
sage: print(_get_code_from_callable(statistic))
|
|
872
|
+
@cached_function
|
|
873
|
+
def statistic(pi):
|
|
874
|
+
pi = Permutations(len(pi))(pi)
|
|
875
|
+
if pi.is_one():
|
|
876
|
+
return Integer(1)
|
|
877
|
+
return sum(statistic(pi.apply_simple_reflection_right(i))
|
|
878
|
+
for i in pi.descents())
|
|
879
|
+
"""
|
|
880
|
+
code = ""
|
|
881
|
+
if function is not None:
|
|
882
|
+
from sage.misc.cachefunc import CachedFunction
|
|
883
|
+
try:
|
|
884
|
+
if isinstance(function, CachedFunction):
|
|
885
|
+
code = inspect.getsource(function.f)
|
|
886
|
+
else:
|
|
887
|
+
code = inspect.getsource(function)
|
|
888
|
+
except (OSError, TypeError):
|
|
889
|
+
verbose("inspect.getsource could not get code from function provided",
|
|
890
|
+
caller_name='FindStat')
|
|
891
|
+
return code
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
def findstat(query=None, values=None, distribution=None, domain=None,
|
|
895
|
+
depth=FINDSTAT_DEFAULT_DEPTH, max_values=FINDSTAT_MAX_VALUES):
|
|
896
|
+
r"""
|
|
897
|
+
Return matching statistics.
|
|
898
|
+
|
|
899
|
+
INPUT:
|
|
900
|
+
|
|
901
|
+
One of the following:
|
|
902
|
+
|
|
903
|
+
- an integer or a string representing a valid FindStat identifier
|
|
904
|
+
(e.g. 45 or 'St000045'). The keyword arguments ``depth`` and
|
|
905
|
+
``max_values`` are ignored, ``values`` and ``distribution``
|
|
906
|
+
must be ``None``.
|
|
907
|
+
|
|
908
|
+
- a list of pairs of the form ``(object, value)``, or a
|
|
909
|
+
dictionary from Sage objects to integer values. The keyword
|
|
910
|
+
arguments ``depth`` and ``max_values`` are passed to the
|
|
911
|
+
finder, ``values`` and ``distribution`` must be ``None``.
|
|
912
|
+
|
|
913
|
+
- a list of pairs of the form (list of objects, list of values),
|
|
914
|
+
or a single pair of the form (list of objects, list of values).
|
|
915
|
+
In each pair there should be as many objects as values. The
|
|
916
|
+
keyword arguments ``depth`` and ``max_values`` are passed to
|
|
917
|
+
the finder.
|
|
918
|
+
|
|
919
|
+
- a collection and a list of pairs of the form (string, value),
|
|
920
|
+
or a dictionary from strings to integer values. The keyword
|
|
921
|
+
arguments ``depth`` and ``max_values`` are passed to the
|
|
922
|
+
finder. This should only be used if the collection is not yet
|
|
923
|
+
supported.
|
|
924
|
+
|
|
925
|
+
- a collection and a callable. The callable is used to generate
|
|
926
|
+
``max_values`` ``(object, value)`` pairs. The number of terms
|
|
927
|
+
generated may also be controlled by passing an iterable
|
|
928
|
+
collection, such as ``Permutations(3)``. The keyword arguments
|
|
929
|
+
``depth`` and ``max_values`` are passed to the finder.
|
|
930
|
+
|
|
931
|
+
OUTPUT: an instance of a :class:`FindStatStatistic`, represented by
|
|
932
|
+
|
|
933
|
+
- the FindStat identifier together with its name, or
|
|
934
|
+
|
|
935
|
+
- a list of triples, each consisting of
|
|
936
|
+
|
|
937
|
+
- the statistic
|
|
938
|
+
|
|
939
|
+
- a list of strings naming certain maps
|
|
940
|
+
|
|
941
|
+
- a number which says how many of the values submitted agree
|
|
942
|
+
with the values in the database, when applying the maps in
|
|
943
|
+
the given order to the object and then computing the
|
|
944
|
+
statistic on the result.
|
|
945
|
+
|
|
946
|
+
EXAMPLES:
|
|
947
|
+
|
|
948
|
+
A particular statistic can be retrieved by its St-identifier or
|
|
949
|
+
number::
|
|
950
|
+
|
|
951
|
+
sage: findstat('St000041') # optional -- internet
|
|
952
|
+
St000041: The number of nestings of a perfect matching.
|
|
953
|
+
|
|
954
|
+
sage: findstat(51) # optional -- internet
|
|
955
|
+
St000051: The size of the left subtree of a binary tree.
|
|
956
|
+
|
|
957
|
+
sage: findstat('St000042oMp00116') # optional -- internet
|
|
958
|
+
St000042oMp00116
|
|
959
|
+
|
|
960
|
+
The database can be searched by providing a list of pairs::
|
|
961
|
+
|
|
962
|
+
sage: l = [m for n in range(1, 4) for m in PerfectMatchings(2*n)]
|
|
963
|
+
sage: findstat([(m, m.number_of_nestings()) for m in l], depth=0) # optional -- internet
|
|
964
|
+
0: St000041 (quality [100, 100])
|
|
965
|
+
|
|
966
|
+
or a dictionary::
|
|
967
|
+
|
|
968
|
+
sage: findstat({m: m.number_of_nestings() for m in l}, depth=0) # optional -- internet
|
|
969
|
+
0: St000041 (quality [100, 100])
|
|
970
|
+
|
|
971
|
+
Note however, that the results of these two queries need not
|
|
972
|
+
compare equal, because we compare queries by the data
|
|
973
|
+
sent, and the ordering of the data might be different.
|
|
974
|
+
|
|
975
|
+
Another possibility is to send a collection and a function. In
|
|
976
|
+
this case, the function is applied to the first few objects of
|
|
977
|
+
the collection::
|
|
978
|
+
|
|
979
|
+
sage: findstat("Perfect Matchings", lambda m: m.number_of_nestings(), depth=0) # optional -- internet
|
|
980
|
+
0: St000041 (quality [20, 100])
|
|
981
|
+
|
|
982
|
+
To search for a distribution, send a list of lists, or a single pair::
|
|
983
|
+
|
|
984
|
+
sage: PM = PerfectMatchings(10); findstat((PM, [m.number_of_nestings() for m in PM]), depth=0) # optional -- internet
|
|
985
|
+
0: St000042 (quality [100, 100])
|
|
986
|
+
1: St000041 (quality [9, 100])
|
|
987
|
+
|
|
988
|
+
Alternatively, specify the ``distribution`` parameter::
|
|
989
|
+
|
|
990
|
+
sage: findstat(12, distribution=lambda m: m.number_of_nestings(), depth=0) # optional -- internet
|
|
991
|
+
0: St000041 (quality [100, 100])
|
|
992
|
+
1: St000042 (quality [100, 100])
|
|
993
|
+
|
|
994
|
+
Note that there is a limit, ``FINDSTAT_MAX_VALUES``, on the number
|
|
995
|
+
of elements that may be submitted to FindStat, which is currently
|
|
996
|
+
1000. Therefore, the interface tries to truncate queries
|
|
997
|
+
appropriately, but this may be impossible, especially with
|
|
998
|
+
distribution searches::
|
|
999
|
+
|
|
1000
|
+
sage: PM = PerfectMatchings(12); PM.cardinality() # optional -- internet
|
|
1001
|
+
10395
|
|
1002
|
+
sage: findstat((PM, [1 for m in PM])) # optional -- internet
|
|
1003
|
+
Traceback (most recent call last):
|
|
1004
|
+
...
|
|
1005
|
+
ValueError: E016: The statistic finder was unable to perform a search on your data. The following errors have occured:
|
|
1006
|
+
<BLANKLINE>
|
|
1007
|
+
You passed too few elements (0 < 3) to FindStat!
|
|
1008
|
+
|
|
1009
|
+
Finally, we can also retrieve all statistics with a given domain::
|
|
1010
|
+
|
|
1011
|
+
sage: findstat("Cc0024") # optional -- internet
|
|
1012
|
+
Set of combinatorial statistics with domain Cc0024: Binary words in FindStat
|
|
1013
|
+
|
|
1014
|
+
sage: findstat(domain='Cores') # optional -- internet
|
|
1015
|
+
Set of combinatorial statistics with domain Cc0013: Cores in FindStat
|
|
1016
|
+
|
|
1017
|
+
TESTS::
|
|
1018
|
+
|
|
1019
|
+
sage: findstat("Permutations", lambda x: 1, depth='x') # optional -- internet
|
|
1020
|
+
Traceback (most recent call last):
|
|
1021
|
+
...
|
|
1022
|
+
ValueError: E021: Depth should be a nonnegative integer at most 9, but is x.
|
|
1023
|
+
|
|
1024
|
+
sage: findstat("Permutations", lambda x: 1, depth=100) # optional -- internet
|
|
1025
|
+
Traceback (most recent call last):
|
|
1026
|
+
...
|
|
1027
|
+
ValueError: E021: Depth should be a nonnegative integer at most 9, but is 100.
|
|
1028
|
+
|
|
1029
|
+
sage: S = Permutation
|
|
1030
|
+
sage: findstat([(S([1,2]), 1), ([S([1,3,2]), S([1,2])], [2,3])]) # optional -- internet
|
|
1031
|
+
Traceback (most recent call last):
|
|
1032
|
+
...
|
|
1033
|
+
ValueError: FindStat expects that every object occurs at most once: [1, 2]
|
|
1034
|
+
|
|
1035
|
+
Check that values which can be converted to integers are supported::
|
|
1036
|
+
|
|
1037
|
+
sage: findstat([(m, m.number_of_nestings()/1) for m in PerfectMatchings(10)], depth=0) # optional -- internet
|
|
1038
|
+
0: St000041 (quality [9, 100])
|
|
1039
|
+
|
|
1040
|
+
Check that ``None`` values are omitted::
|
|
1041
|
+
|
|
1042
|
+
sage: findstat("graphs", lambda g: g.diameter() if g.is_connected() else None, max_values=100, depth=0) # optional -- internet
|
|
1043
|
+
0: St000259 (quality [100, 100])
|
|
1044
|
+
"""
|
|
1045
|
+
try:
|
|
1046
|
+
max_values = int(max_values)
|
|
1047
|
+
assert 0 <= max_values <= FINDSTAT_MAX_VALUES
|
|
1048
|
+
except (ValueError, AssertionError):
|
|
1049
|
+
raise ValueError("the maximal number of values for a FindStat query must be a nonnegative integer less than or equal to %i" % FINDSTAT_MAX_VALUES)
|
|
1050
|
+
|
|
1051
|
+
check_collection = True
|
|
1052
|
+
|
|
1053
|
+
def get_values(raw, domain=None):
|
|
1054
|
+
if callable(raw):
|
|
1055
|
+
known_terms = _data_from_function(raw, domain)
|
|
1056
|
+
function = raw
|
|
1057
|
+
else:
|
|
1058
|
+
known_terms, domain = _data_from_iterable(raw, domain=domain,
|
|
1059
|
+
mapping=False,
|
|
1060
|
+
check=check_collection)
|
|
1061
|
+
function = None
|
|
1062
|
+
data = _data_from_data(known_terms, max_values)
|
|
1063
|
+
return known_terms, data, domain, function
|
|
1064
|
+
|
|
1065
|
+
def get_distribution(raw, domain=None):
|
|
1066
|
+
if callable(raw):
|
|
1067
|
+
known_terms = _data_from_function(raw, domain)
|
|
1068
|
+
function = raw
|
|
1069
|
+
else:
|
|
1070
|
+
known_terms, domain = _data_from_iterable(raw, domain=domain,
|
|
1071
|
+
mapping=False,
|
|
1072
|
+
check=check_collection)
|
|
1073
|
+
function = None
|
|
1074
|
+
data = _distribution_from_data(known_terms, domain, max_values)
|
|
1075
|
+
return known_terms, data, domain, function
|
|
1076
|
+
|
|
1077
|
+
######################################################################
|
|
1078
|
+
if query is None and values is None and distribution is None and domain is None:
|
|
1079
|
+
return FindStat()
|
|
1080
|
+
|
|
1081
|
+
if values is not None and distribution is not None:
|
|
1082
|
+
raise ValueError("not both of `values` and `distribution` may be given for a FindStat query")
|
|
1083
|
+
|
|
1084
|
+
if values is None and distribution is None:
|
|
1085
|
+
if query is None:
|
|
1086
|
+
return FindStatStatistics(domain=domain)
|
|
1087
|
+
|
|
1088
|
+
if domain is None:
|
|
1089
|
+
if isinstance(query, (int, Integer, FindStatCombinatorialStatistic)):
|
|
1090
|
+
return FindStatStatistic(query)
|
|
1091
|
+
|
|
1092
|
+
if isinstance(query, str):
|
|
1093
|
+
if re.match(FINDSTAT_COLLECTION_REGEXP, query):
|
|
1094
|
+
return FindStatStatistics(domain=query)
|
|
1095
|
+
|
|
1096
|
+
return FindStatStatistic(query)
|
|
1097
|
+
|
|
1098
|
+
values, query = query, None
|
|
1099
|
+
|
|
1100
|
+
if query is not None:
|
|
1101
|
+
if domain is None:
|
|
1102
|
+
domain, query = query, None
|
|
1103
|
+
else:
|
|
1104
|
+
raise ValueError("the given arguments cannot be used for a FindStat search")
|
|
1105
|
+
|
|
1106
|
+
if domain is not None:
|
|
1107
|
+
domain = FindStatCollection(domain)
|
|
1108
|
+
|
|
1109
|
+
if values is not None:
|
|
1110
|
+
if isinstance(values, (int, Integer, str, FindStatCombinatorialStatistic)):
|
|
1111
|
+
if domain is not None:
|
|
1112
|
+
raise ValueError("the domain must not be provided if a statistic identifier is given")
|
|
1113
|
+
return FindStatStatisticQuery(values_of=values, depth=depth)
|
|
1114
|
+
|
|
1115
|
+
known_terms, data, domain, function = get_values(values, domain)
|
|
1116
|
+
return FindStatStatisticQuery(data=data, domain=domain, depth=depth,
|
|
1117
|
+
known_terms=known_terms, function=function)
|
|
1118
|
+
|
|
1119
|
+
if distribution is not None:
|
|
1120
|
+
if isinstance(distribution, (int, Integer, str, FindStatCombinatorialStatistic)):
|
|
1121
|
+
if domain is not None:
|
|
1122
|
+
raise ValueError("the domain must not be provided if a statistic identifier is given")
|
|
1123
|
+
return FindStatStatisticQuery(distribution_of=distribution, depth=depth)
|
|
1124
|
+
|
|
1125
|
+
known_terms, data, domain, function = get_distribution(distribution, domain)
|
|
1126
|
+
return FindStatStatisticQuery(data=data, domain=domain, depth=depth,
|
|
1127
|
+
known_terms=known_terms, function=function)
|
|
1128
|
+
|
|
1129
|
+
raise ValueError("the given arguments cannot be used for a FindStat search")
|
|
1130
|
+
|
|
1131
|
+
|
|
1132
|
+
def findmap(*args, **kwargs):
|
|
1133
|
+
r"""
|
|
1134
|
+
Return matching maps.
|
|
1135
|
+
|
|
1136
|
+
INPUT:
|
|
1137
|
+
|
|
1138
|
+
Valid keywords are: ``domain``, ``codomain``, ``values``,
|
|
1139
|
+
``distribution``, ``depth`` and ``max_values``. They have the
|
|
1140
|
+
following meanings:
|
|
1141
|
+
|
|
1142
|
+
- ``depth`` -- nonnegative integer (default: ``FINDSTAT_DEFAULT_DEPTH``);
|
|
1143
|
+
specifying how many maps to apply to generate the given map
|
|
1144
|
+
|
|
1145
|
+
- ``max_values`` -- integer (default: ``FINDSTAT_MAX_VALUES``); specifying
|
|
1146
|
+
how many values are sent to the finder
|
|
1147
|
+
|
|
1148
|
+
- ``domain``, ``codomain`` -- integer or string of the form
|
|
1149
|
+
``Cc1234``, designates the domain and codomain of the sought
|
|
1150
|
+
for maps
|
|
1151
|
+
|
|
1152
|
+
- ``values``, ``distribution`` -- data specifying the values or
|
|
1153
|
+
distribution of values of the sought for maps. The keyword
|
|
1154
|
+
arguments ``depth`` and ``max_values`` are passed to the
|
|
1155
|
+
finder. The data may be specified in one of the following
|
|
1156
|
+
forms:
|
|
1157
|
+
|
|
1158
|
+
- a list of pairs of the form ``(object, value)``, or a
|
|
1159
|
+
dictionary from Sage objects to Sage objects.
|
|
1160
|
+
|
|
1161
|
+
- a list of pairs of the form ``(list of objects, list of
|
|
1162
|
+
values)``, or a single pair of the form ``(list of objects,
|
|
1163
|
+
list of values)``. In each pair there should be as many
|
|
1164
|
+
objects as values.
|
|
1165
|
+
|
|
1166
|
+
- a callable. In this case, the domain must be specified,
|
|
1167
|
+
also. The callable is then used to generate ``max_values``
|
|
1168
|
+
``(object, value)`` pairs.
|
|
1169
|
+
|
|
1170
|
+
The number of terms generated may also be controlled by
|
|
1171
|
+
passing an iterable collection, such as
|
|
1172
|
+
``Permutations(3)``.
|
|
1173
|
+
|
|
1174
|
+
``findmap`` also accepts at most three positional arguments as
|
|
1175
|
+
follows:
|
|
1176
|
+
|
|
1177
|
+
- a single positional argument, if none of ``domain``,
|
|
1178
|
+
``codomain``, ``values`` or ``distribution`` are specified, is
|
|
1179
|
+
interpreted as a FindStat map identifier. If further arguments
|
|
1180
|
+
are given and it is a string, it is interpreted as a domain.
|
|
1181
|
+
If all this fails, it is interpreted as the specification of
|
|
1182
|
+
values.
|
|
1183
|
+
|
|
1184
|
+
- if two positional arguments are given, the first is interpreted
|
|
1185
|
+
as domain, and the second either as codomain or the
|
|
1186
|
+
specification of values.
|
|
1187
|
+
|
|
1188
|
+
- if three positional arguments are given, the first two are
|
|
1189
|
+
interpreted as domain and codomain, and the third as the
|
|
1190
|
+
specification of values.
|
|
1191
|
+
|
|
1192
|
+
OUTPUT:
|
|
1193
|
+
|
|
1194
|
+
An instance of a :class:`FindStatMap`, :class:`FindStatMapQuery`
|
|
1195
|
+
or :class:`FindStatMaps`.
|
|
1196
|
+
|
|
1197
|
+
EXAMPLES:
|
|
1198
|
+
|
|
1199
|
+
A particular map can be retrieved by its Mp-identifier or
|
|
1200
|
+
number::
|
|
1201
|
+
|
|
1202
|
+
sage: findmap('Mp00062') # optional -- internet
|
|
1203
|
+
Mp00062: Lehmer-code to major-code bijection
|
|
1204
|
+
|
|
1205
|
+
sage: findmap(62) # optional -- internet
|
|
1206
|
+
Mp00062: Lehmer-code to major-code bijection
|
|
1207
|
+
|
|
1208
|
+
sage: findmap("Mp00099oMp00127") # optional -- internet
|
|
1209
|
+
Mp00099oMp00127
|
|
1210
|
+
|
|
1211
|
+
The database can be searched by providing a list of pairs::
|
|
1212
|
+
|
|
1213
|
+
sage: l = [pi for n in range(5) for pi in Permutations(n)]
|
|
1214
|
+
sage: findmap([(pi, pi.complement().increasing_tree_shape()) for pi in l], depth=2) # optional -- internet
|
|
1215
|
+
0: Mp00061oMp00069 (quality [...])
|
|
1216
|
+
|
|
1217
|
+
or a dictionary::
|
|
1218
|
+
|
|
1219
|
+
sage: findmap({pi: pi.complement().increasing_tree_shape() for pi in l}, depth=2) # optional -- internet
|
|
1220
|
+
0: Mp00061oMp00069 (quality [...])
|
|
1221
|
+
|
|
1222
|
+
Note however, that the results of these two queries need not
|
|
1223
|
+
compare equal, because we compare queries by the data
|
|
1224
|
+
sent, and the ordering of the data might be different.
|
|
1225
|
+
|
|
1226
|
+
Another possibility is to send a collection and a function. In
|
|
1227
|
+
this case, the function is applied to the first few objects of
|
|
1228
|
+
the collection::
|
|
1229
|
+
|
|
1230
|
+
sage: findmap("Permutations", lambda pi: pi.increasing_tree_shape(), depth=1) # optional -- internet
|
|
1231
|
+
0: Mp00061 (quality [100])
|
|
1232
|
+
|
|
1233
|
+
In rare cases, it may not be possible to guess the codomain of a
|
|
1234
|
+
map, in which case it can be provided as second argument or
|
|
1235
|
+
keyword argument::
|
|
1236
|
+
|
|
1237
|
+
sage: findmap("Dyck paths", "Perfect matchings", lambda D: [(a+1, b) for a,b in D.tunnels()]) # optional -- internet
|
|
1238
|
+
0: Mp00146 (quality [100])
|
|
1239
|
+
|
|
1240
|
+
sage: findmap("Dyck paths", "Set partitions", lambda D: [(a+1, b) for a,b in D.tunnels()]) # optional -- internet
|
|
1241
|
+
0: Mp00092oMp00146 (quality [...])
|
|
1242
|
+
|
|
1243
|
+
Finally, we can also retrieve all maps with a given domain or codomain::
|
|
1244
|
+
|
|
1245
|
+
sage: findmap("Cc0024") # optional -- internet
|
|
1246
|
+
Set of combinatorial maps with domain Cc0024: Binary words used by FindStat
|
|
1247
|
+
|
|
1248
|
+
sage: findmap(codomain='Cores') # optional -- internet
|
|
1249
|
+
Set of combinatorial maps with codomain Cc0013: Cores used by FindStat
|
|
1250
|
+
"""
|
|
1251
|
+
if len(args) > 3:
|
|
1252
|
+
raise TypeError("findmap takes at most 3 positional arguments (%s given)" % len(args))
|
|
1253
|
+
|
|
1254
|
+
bad_args = set(kwargs).difference(["values", "distribution",
|
|
1255
|
+
"domain", "codomain",
|
|
1256
|
+
"depth", "max_values"])
|
|
1257
|
+
if bad_args:
|
|
1258
|
+
raise TypeError("findmap got unexpected keyword arguments '%s'" % bad_args)
|
|
1259
|
+
|
|
1260
|
+
max_values = kwargs.get("max_values", FINDSTAT_MAX_VALUES)
|
|
1261
|
+
depth = kwargs.get("depth", FINDSTAT_DEFAULT_DEPTH)
|
|
1262
|
+
values = kwargs.get("values", None)
|
|
1263
|
+
distribution = kwargs.get("distribution", None)
|
|
1264
|
+
domain = kwargs.get("domain", None)
|
|
1265
|
+
codomain = kwargs.get("codomain", None)
|
|
1266
|
+
|
|
1267
|
+
try:
|
|
1268
|
+
max_values = int(max_values)
|
|
1269
|
+
assert 0 <= max_values <= FINDSTAT_MAX_VALUES
|
|
1270
|
+
except (ValueError, AssertionError):
|
|
1271
|
+
raise ValueError("the maximal number of values for a FindStat query must be a nonnegative integer less than or equal to %i" % FINDSTAT_MAX_VALUES)
|
|
1272
|
+
|
|
1273
|
+
check_collection = True
|
|
1274
|
+
|
|
1275
|
+
def get_values(raw, domain=None, codomain=None):
|
|
1276
|
+
if callable(raw):
|
|
1277
|
+
known_terms = _data_from_function(raw, domain)
|
|
1278
|
+
if codomain is None:
|
|
1279
|
+
codomain = FindStatCollection(known_terms[0][1][0])
|
|
1280
|
+
function = raw
|
|
1281
|
+
else:
|
|
1282
|
+
known_terms, domain, codomain = _data_from_iterable(raw, domain=domain,
|
|
1283
|
+
codomain=codomain,
|
|
1284
|
+
mapping=True,
|
|
1285
|
+
check=check_collection)
|
|
1286
|
+
function = None
|
|
1287
|
+
data = _data_from_data(known_terms, max_values)
|
|
1288
|
+
return known_terms, data, domain, codomain, function
|
|
1289
|
+
|
|
1290
|
+
def get_distribution(raw, domain=None, codomain=None):
|
|
1291
|
+
if callable(raw):
|
|
1292
|
+
known_terms = _data_from_function(raw, domain)
|
|
1293
|
+
function = raw
|
|
1294
|
+
else:
|
|
1295
|
+
known_terms, domain, codomain = _data_from_iterable(raw, domain=domain,
|
|
1296
|
+
codomain=codomain,
|
|
1297
|
+
mapping=True,
|
|
1298
|
+
check=check_collection)
|
|
1299
|
+
function = None
|
|
1300
|
+
data = _distribution_from_data(known_terms, domain, max_values)
|
|
1301
|
+
return known_terms, data, domain, codomain, function
|
|
1302
|
+
|
|
1303
|
+
def is_collection(arg):
|
|
1304
|
+
try:
|
|
1305
|
+
FindStatCollection(arg)
|
|
1306
|
+
return True
|
|
1307
|
+
except ValueError:
|
|
1308
|
+
return False
|
|
1309
|
+
|
|
1310
|
+
def check_domain(arg, domain):
|
|
1311
|
+
if domain is not None:
|
|
1312
|
+
raise TypeError("the domain was specified twice, as positional argument (%s) and as keyword domain=%s" % (arg, domain))
|
|
1313
|
+
return arg
|
|
1314
|
+
|
|
1315
|
+
def check_codomain(arg, codomain):
|
|
1316
|
+
if codomain is not None:
|
|
1317
|
+
raise TypeError("the codomain was specified twice, as positional argument (%s) and as keyword codomain=%s" % (arg, codomain))
|
|
1318
|
+
return arg
|
|
1319
|
+
|
|
1320
|
+
def check_values(arg, values):
|
|
1321
|
+
if values is not None:
|
|
1322
|
+
raise TypeError("values were specified twice, as positional argument (%s) and as keyword values=%s" % (arg, values))
|
|
1323
|
+
return arg
|
|
1324
|
+
|
|
1325
|
+
######################################################################
|
|
1326
|
+
if values is not None and distribution is not None:
|
|
1327
|
+
raise ValueError("not both of `values` and `distribution` may be given for a FindStat query")
|
|
1328
|
+
|
|
1329
|
+
if len(args) == 1:
|
|
1330
|
+
if (values is None and distribution is None
|
|
1331
|
+
and domain is None and codomain is None
|
|
1332
|
+
and (isinstance(args[0], (int, Integer, FindStatCombinatorialMap))
|
|
1333
|
+
or (isinstance(args[0], str)
|
|
1334
|
+
and not is_collection(args[0])))):
|
|
1335
|
+
return FindStatMap(args[0])
|
|
1336
|
+
|
|
1337
|
+
if (isinstance(args[0], str) and
|
|
1338
|
+
is_collection(args[0])):
|
|
1339
|
+
domain = check_domain(args[0], domain)
|
|
1340
|
+
|
|
1341
|
+
else:
|
|
1342
|
+
values = check_values(args[0], values)
|
|
1343
|
+
|
|
1344
|
+
elif len(args) == 2:
|
|
1345
|
+
domain = check_domain(args[0], domain)
|
|
1346
|
+
if isinstance(args[1], (int, Integer, str)):
|
|
1347
|
+
codomain = check_codomain(args[1], codomain)
|
|
1348
|
+
else:
|
|
1349
|
+
values = check_values(args[1], values)
|
|
1350
|
+
|
|
1351
|
+
elif len(args) == 3:
|
|
1352
|
+
domain = check_domain(args[0], domain)
|
|
1353
|
+
codomain = check_codomain(args[1], codomain)
|
|
1354
|
+
values = check_values(args[2], values)
|
|
1355
|
+
|
|
1356
|
+
if domain is not None:
|
|
1357
|
+
domain = FindStatCollection(domain)
|
|
1358
|
+
if codomain is not None:
|
|
1359
|
+
codomain = FindStatCollection(codomain)
|
|
1360
|
+
|
|
1361
|
+
if (values is None and distribution is None
|
|
1362
|
+
and (domain is not None or codomain is not None)):
|
|
1363
|
+
return FindStatMaps(domain=domain, codomain=codomain)
|
|
1364
|
+
|
|
1365
|
+
if values is not None:
|
|
1366
|
+
if isinstance(values, (int, Integer, str, FindStatCombinatorialMap)):
|
|
1367
|
+
if domain is not None or codomain is not None:
|
|
1368
|
+
raise ValueError("domain and codomain must not be provided if a map identifier is given")
|
|
1369
|
+
return FindStatMapQuery(values_of=values, depth=depth)
|
|
1370
|
+
|
|
1371
|
+
known_terms, data, domain, codomain, function = get_values(values, domain, codomain)
|
|
1372
|
+
return FindStatMapQuery(data=data, domain=domain, codomain=codomain, depth=depth,
|
|
1373
|
+
known_terms=known_terms, function=function)
|
|
1374
|
+
|
|
1375
|
+
if distribution is not None:
|
|
1376
|
+
if isinstance(distribution, (int, Integer, str, FindStatCombinatorialMap)):
|
|
1377
|
+
if domain is not None or codomain is not None:
|
|
1378
|
+
raise ValueError("domain and codomain must not be provided if a map identifier is given")
|
|
1379
|
+
return FindStatMapQuery(distribution_of=distribution, depth=depth)
|
|
1380
|
+
|
|
1381
|
+
known_terms, data, domain, function = get_distribution(distribution, domain)
|
|
1382
|
+
return FindStatMapQuery(data=data, domain=domain, codomain=codomain, depth=depth,
|
|
1383
|
+
known_terms=known_terms, function=function)
|
|
1384
|
+
|
|
1385
|
+
raise ValueError("the given arguments cannot be used for a FindStat search")
|
|
1386
|
+
|
|
1387
|
+
|
|
1388
|
+
######################################################################
|
|
1389
|
+
# common methods for statistic and maps in FindStat
|
|
1390
|
+
######################################################################
|
|
1391
|
+
class FindStatFunction(SageObject):
|
|
1392
|
+
"""
|
|
1393
|
+
A class providing the common methods of :class:`FindStatMap` and
|
|
1394
|
+
:class:`FindStatStatistic`.
|
|
1395
|
+
|
|
1396
|
+
This class provides methods to access and modify properties of a
|
|
1397
|
+
single statistic or map of the FindStat database.
|
|
1398
|
+
"""
|
|
1399
|
+
def __init__(self, id, data=None, function=None):
|
|
1400
|
+
"""
|
|
1401
|
+
Initialize a statistic or map.
|
|
1402
|
+
|
|
1403
|
+
INPUT:
|
|
1404
|
+
|
|
1405
|
+
- ``id`` -- a padded identifier, with number 0 reserved for new
|
|
1406
|
+
statistics or maps.
|
|
1407
|
+
|
|
1408
|
+
- ``data`` -- dictionary with "Description", "Code", etc.
|
|
1409
|
+
|
|
1410
|
+
- ``function`` -- (optional) a callable implementing the
|
|
1411
|
+
statistic or map, or ``None``
|
|
1412
|
+
|
|
1413
|
+
``data`` should be provided if and only if ``id`` refers to a
|
|
1414
|
+
new statistic or map (with identifier 0).
|
|
1415
|
+
|
|
1416
|
+
TESTS::
|
|
1417
|
+
|
|
1418
|
+
sage: from sage.databases.findstat import FindStatFunction, FindStatCollection
|
|
1419
|
+
sage: FindStatFunction("St000000", # optional -- internet
|
|
1420
|
+
....: data={"Bibliography": {},
|
|
1421
|
+
....: "Code": "",
|
|
1422
|
+
....: "Description" : "",
|
|
1423
|
+
....: "Domain": FindStatCollection(1),
|
|
1424
|
+
....: "Name": "a new statistic",
|
|
1425
|
+
....: "References": "",
|
|
1426
|
+
....: "SageCode": ""})
|
|
1427
|
+
St000000: a new statistic
|
|
1428
|
+
"""
|
|
1429
|
+
self._id = id # as padded identifier, with number 0 reserved for new statistics or maps
|
|
1430
|
+
self._modified = False # set in every method modifying the data
|
|
1431
|
+
if callable(function):
|
|
1432
|
+
self._function = function
|
|
1433
|
+
else:
|
|
1434
|
+
self._function = False # determines that FindStat code may not be executed
|
|
1435
|
+
if self.id() != 0 and data is not None:
|
|
1436
|
+
raise ValueError("data (%s) may be provided if and only if id (%s) is %s or %s" %
|
|
1437
|
+
(data, id,
|
|
1438
|
+
FINDSTAT_STATISTIC_PADDED_IDENTIFIER % 0,
|
|
1439
|
+
FINDSTAT_MAP_PADDED_IDENTIFIER % 0))
|
|
1440
|
+
self._data_cache = data # a dictionary with "Description", "Code", etc.
|
|
1441
|
+
|
|
1442
|
+
def _data(self):
|
|
1443
|
+
"""
|
|
1444
|
+
Return a copy of the data defining the statistic or map.
|
|
1445
|
+
|
|
1446
|
+
The first terms of a statistic are provided separately, by
|
|
1447
|
+
:meth:`first_terms_raw`, to save bandwidth.
|
|
1448
|
+
|
|
1449
|
+
Any method modifying ``self._data_cache`` should first
|
|
1450
|
+
call - directly or indirectly - this method.
|
|
1451
|
+
|
|
1452
|
+
TESTS::
|
|
1453
|
+
|
|
1454
|
+
sage: from sage.databases.findstat import FindStatFunction, FindStatCollection
|
|
1455
|
+
sage: FindStatFunction("St000000", # optional -- internet
|
|
1456
|
+
....: data={"Bibliography": {},
|
|
1457
|
+
....: "Code": "",
|
|
1458
|
+
....: "Description" : "",
|
|
1459
|
+
....: "Domain": FindStatCollection(1),
|
|
1460
|
+
....: "Name": "a new statistic",
|
|
1461
|
+
....: "References": "",
|
|
1462
|
+
....: "SageCode": ""})._data()
|
|
1463
|
+
{'Bibliography': {},
|
|
1464
|
+
'Code': '',
|
|
1465
|
+
'Description': '',
|
|
1466
|
+
'Domain': Cc0001: Permutations,
|
|
1467
|
+
'Name': 'a new statistic',
|
|
1468
|
+
'References': '',
|
|
1469
|
+
'SageCode': ''}
|
|
1470
|
+
"""
|
|
1471
|
+
# initializes self._data_cache on first call
|
|
1472
|
+
if self._data_cache is None:
|
|
1473
|
+
self._data_cache = self._fetch_data()
|
|
1474
|
+
# some of the data are lists, so we need to deepcopy
|
|
1475
|
+
return deepcopy(self._data_cache)
|
|
1476
|
+
|
|
1477
|
+
def __call__(self, elt):
|
|
1478
|
+
"""
|
|
1479
|
+
Apply the function to a given element.
|
|
1480
|
+
|
|
1481
|
+
EXAMPLES::
|
|
1482
|
+
|
|
1483
|
+
sage: s = lambda g: g.diameter() if g.is_connected() else None
|
|
1484
|
+
sage: q = findstat("graphs", s, max_values=100) # optional -- internet
|
|
1485
|
+
sage: q(graphs.PetersenGraph().copy(immutable=True)) # optional -- internet
|
|
1486
|
+
2
|
|
1487
|
+
"""
|
|
1488
|
+
if self._function is False and FindStat()._allow_execution is False:
|
|
1489
|
+
raise ValueError("execution of verified code provided by FindStat is not enabled for %s" % self)
|
|
1490
|
+
if self._function is True or (self._function is False and FindStat()._allow_execution is True):
|
|
1491
|
+
if not self.sage_code():
|
|
1492
|
+
raise ValueError("there is no verified code available for %s" % self)
|
|
1493
|
+
from sage.repl.preparse import preparse
|
|
1494
|
+
try:
|
|
1495
|
+
l = {}
|
|
1496
|
+
environment = 'sage.all'
|
|
1497
|
+
code = f"from {environment} import *\n" + preparse(self.sage_code())
|
|
1498
|
+
exec(code, l)
|
|
1499
|
+
except SyntaxError:
|
|
1500
|
+
raise ValueError("could not execute verified code for %s" % self)
|
|
1501
|
+
if isinstance(self, FindStatStatistic):
|
|
1502
|
+
self._function = eval("statistic", l)
|
|
1503
|
+
elif isinstance(self, FindStatMap):
|
|
1504
|
+
self._function = eval("mapping", l)
|
|
1505
|
+
else:
|
|
1506
|
+
raise ValueError("cannot execute verified code for %s" % self)
|
|
1507
|
+
|
|
1508
|
+
return self._function(elt)
|
|
1509
|
+
|
|
1510
|
+
def __repr__(self):
|
|
1511
|
+
r"""
|
|
1512
|
+
Return the representation of the FindStat statistic or map.
|
|
1513
|
+
|
|
1514
|
+
OUTPUT:
|
|
1515
|
+
|
|
1516
|
+
A string, the identifier and the name of the statistic. If
|
|
1517
|
+
the statistic was modified (see :meth:`modified`) this is
|
|
1518
|
+
also indicated.
|
|
1519
|
+
|
|
1520
|
+
EXAMPLES::
|
|
1521
|
+
|
|
1522
|
+
sage: findstat(51) # optional -- internet
|
|
1523
|
+
St000051: The size of the left subtree of a binary tree.
|
|
1524
|
+
|
|
1525
|
+
sage: findstat(914) # optional -- internet
|
|
1526
|
+
St000914: The sum of the values of the Möbius function of a poset.
|
|
1527
|
+
|
|
1528
|
+
sage: findmap(85) # optional -- internet
|
|
1529
|
+
Mp00085: Schützenberger involution
|
|
1530
|
+
"""
|
|
1531
|
+
if self._modified:
|
|
1532
|
+
s = "%s(modified): %s" % (self.id_str(), self.name())
|
|
1533
|
+
else:
|
|
1534
|
+
s = "%s: %s" % (self.id_str(), self.name())
|
|
1535
|
+
return s
|
|
1536
|
+
|
|
1537
|
+
def reset(self):
|
|
1538
|
+
"""
|
|
1539
|
+
Discard all modification of the statistic or map.
|
|
1540
|
+
|
|
1541
|
+
EXAMPLES::
|
|
1542
|
+
|
|
1543
|
+
sage: s = findmap(62) # optional -- internet
|
|
1544
|
+
sage: s.set_name(u"Möbius"); s # optional -- internet
|
|
1545
|
+
Mp00062(modified): Möbius
|
|
1546
|
+
sage: s.reset(); s # optional -- internet
|
|
1547
|
+
Mp00062: Lehmer-code to major-code bijection
|
|
1548
|
+
|
|
1549
|
+
TESTS:
|
|
1550
|
+
|
|
1551
|
+
Check that new statistics and maps cannot be reset::
|
|
1552
|
+
|
|
1553
|
+
sage: # optional - internet
|
|
1554
|
+
sage: q = findstat([(d, randint(1, 1000)) for d in DyckWords(4)])
|
|
1555
|
+
sage: q.set_description("Random values on Dyck paths.")
|
|
1556
|
+
sage: print(q.description())
|
|
1557
|
+
Random values on Dyck paths.
|
|
1558
|
+
sage: q.reset()
|
|
1559
|
+
Traceback (most recent call last):
|
|
1560
|
+
...
|
|
1561
|
+
ValueError: cannot reset values of St000000: a new statistic on Dyck paths
|
|
1562
|
+
"""
|
|
1563
|
+
if isinstance(self, FindStatStatistic) and self.id_str() in _all_statistics:
|
|
1564
|
+
del _all_statistics[self.id_str()]
|
|
1565
|
+
elif isinstance(self, FindStatMap) and self.id_str() in _all_maps:
|
|
1566
|
+
del _all_maps[self.id_str()]
|
|
1567
|
+
else:
|
|
1568
|
+
raise ValueError("cannot reset values of %s" % self)
|
|
1569
|
+
self.__init__(self.parent(), self.id_str())
|
|
1570
|
+
|
|
1571
|
+
def id(self):
|
|
1572
|
+
r"""
|
|
1573
|
+
Return the FindStat identifier of the statistic or map.
|
|
1574
|
+
|
|
1575
|
+
OUTPUT: the FindStat identifier of the statistic or map, as an integer
|
|
1576
|
+
|
|
1577
|
+
EXAMPLES::
|
|
1578
|
+
|
|
1579
|
+
sage: findstat(51).id() # optional -- internet
|
|
1580
|
+
51
|
|
1581
|
+
"""
|
|
1582
|
+
return int(self._id[2:])
|
|
1583
|
+
|
|
1584
|
+
def id_str(self):
|
|
1585
|
+
r"""
|
|
1586
|
+
Return the FindStat identifier of the statistic or map.
|
|
1587
|
+
|
|
1588
|
+
OUTPUT: the FindStat identifier of the statistic or map, as a string
|
|
1589
|
+
|
|
1590
|
+
EXAMPLES::
|
|
1591
|
+
|
|
1592
|
+
sage: findstat(51).id_str() # optional -- internet
|
|
1593
|
+
'St000051'
|
|
1594
|
+
"""
|
|
1595
|
+
return self._id
|
|
1596
|
+
|
|
1597
|
+
def domain(self):
|
|
1598
|
+
r"""
|
|
1599
|
+
Return the FindStat domain of the statistic or map.
|
|
1600
|
+
|
|
1601
|
+
OUTPUT:
|
|
1602
|
+
|
|
1603
|
+
The domain of the statistic or map as an instance of
|
|
1604
|
+
:class:`FindStatCollection`.
|
|
1605
|
+
|
|
1606
|
+
EXAMPLES::
|
|
1607
|
+
|
|
1608
|
+
sage: findstat(51).domain() # optional -- internet
|
|
1609
|
+
Cc0010: Binary trees
|
|
1610
|
+
|
|
1611
|
+
sage: findmap(62).domain() # optional -- internet
|
|
1612
|
+
Cc0001: Permutations
|
|
1613
|
+
"""
|
|
1614
|
+
return FindStatCollection(self._data()["Domain"])
|
|
1615
|
+
|
|
1616
|
+
def description(self):
|
|
1617
|
+
r"""
|
|
1618
|
+
Return the description of the statistic or map.
|
|
1619
|
+
|
|
1620
|
+
OUTPUT: string; for statistics, the first line is used as name
|
|
1621
|
+
|
|
1622
|
+
EXAMPLES::
|
|
1623
|
+
|
|
1624
|
+
sage: print(findstat(51).description()) # optional -- internet
|
|
1625
|
+
The size of the left subtree of a binary tree.
|
|
1626
|
+
"""
|
|
1627
|
+
return self._data()["Description"]
|
|
1628
|
+
|
|
1629
|
+
def set_description(self, value):
|
|
1630
|
+
r"""
|
|
1631
|
+
Set the description of the statistic or map.
|
|
1632
|
+
|
|
1633
|
+
INPUT:
|
|
1634
|
+
|
|
1635
|
+
- ``value`` -- string; for statistics, this is the name of the
|
|
1636
|
+
statistic followed by its description on a separate line
|
|
1637
|
+
|
|
1638
|
+
This information is used when submitting the statistic or map with
|
|
1639
|
+
:meth:`submit`.
|
|
1640
|
+
|
|
1641
|
+
EXAMPLES::
|
|
1642
|
+
|
|
1643
|
+
sage: q = findstat([(d, randint(1, 1000)) for d in DyckWords(4)]) # optional -- internet
|
|
1644
|
+
sage: q.set_description("Random values on Dyck paths.\nNot for submission.") # optional -- internet
|
|
1645
|
+
sage: print(q.description()) # optional -- internet
|
|
1646
|
+
Random values on Dyck paths.
|
|
1647
|
+
Not for submission.
|
|
1648
|
+
"""
|
|
1649
|
+
if value != self.description():
|
|
1650
|
+
self._modified = True
|
|
1651
|
+
self._data_cache["Description"] = value
|
|
1652
|
+
|
|
1653
|
+
def name(self):
|
|
1654
|
+
r"""
|
|
1655
|
+
Return the name of the statistic or map.
|
|
1656
|
+
|
|
1657
|
+
OUTPUT:
|
|
1658
|
+
|
|
1659
|
+
A string. For statistics, this is just the first line of the
|
|
1660
|
+
description.
|
|
1661
|
+
|
|
1662
|
+
EXAMPLES::
|
|
1663
|
+
|
|
1664
|
+
sage: findstat(51).name() # optional -- internet
|
|
1665
|
+
'The size of the left subtree of a binary tree.'
|
|
1666
|
+
"""
|
|
1667
|
+
return self._data()["Name"]
|
|
1668
|
+
|
|
1669
|
+
def references(self):
|
|
1670
|
+
r"""
|
|
1671
|
+
Return the references associated with the statistic or map.
|
|
1672
|
+
|
|
1673
|
+
OUTPUT:
|
|
1674
|
+
|
|
1675
|
+
An instance of :class:`sage.databases.oeis.FancyTuple`, each
|
|
1676
|
+
item corresponds to a reference.
|
|
1677
|
+
|
|
1678
|
+
EXAMPLES::
|
|
1679
|
+
|
|
1680
|
+
sage: findstat(41).references() # optional -- internet
|
|
1681
|
+
0: [1] de Médicis, A., Viennot, X. G., Moments des $q$-polynômes de Laguerre et la bijection de Foata-Zeilberger [[MathSciNet:1288802]]
|
|
1682
|
+
1: [2] Simion, R., Stanton, D., Octabasic Laguerre polynomials and permutation statistics [[MathSciNet:1418763]]
|
|
1683
|
+
"""
|
|
1684
|
+
result = []
|
|
1685
|
+
refs = self.references_raw()
|
|
1686
|
+
if refs:
|
|
1687
|
+
refs = refs.splitlines()
|
|
1688
|
+
else:
|
|
1689
|
+
return FancyTuple([])
|
|
1690
|
+
bibs = self._data()["Bibliography"]
|
|
1691
|
+
for ref in refs:
|
|
1692
|
+
parts = ref.partition("[[")
|
|
1693
|
+
parts = parts[:-1] + parts[2].partition("]]")
|
|
1694
|
+
comment = parts[0]
|
|
1695
|
+
link = parts[2]
|
|
1696
|
+
if link == "":
|
|
1697
|
+
result.append(ref)
|
|
1698
|
+
else:
|
|
1699
|
+
try:
|
|
1700
|
+
bibitem = bibs[link]
|
|
1701
|
+
except KeyError:
|
|
1702
|
+
# this means that the link is unhandled
|
|
1703
|
+
result.append(ref)
|
|
1704
|
+
else:
|
|
1705
|
+
author_title = ", ".join(e for e in [bibitem["Author"], bibitem["Title"]]
|
|
1706
|
+
if e)
|
|
1707
|
+
result.append(comment + author_title + " " + "".join(parts[1:]))
|
|
1708
|
+
|
|
1709
|
+
return FancyTuple(result)
|
|
1710
|
+
|
|
1711
|
+
def references_raw(self):
|
|
1712
|
+
r"""
|
|
1713
|
+
Return the unrendered references associated with the statistic or map.
|
|
1714
|
+
|
|
1715
|
+
EXAMPLES::
|
|
1716
|
+
|
|
1717
|
+
sage: print(findstat(41).references_raw()) # optional -- internet
|
|
1718
|
+
[1] [[MathSciNet:1288802]]
|
|
1719
|
+
[2] [[MathSciNet:1418763]]
|
|
1720
|
+
"""
|
|
1721
|
+
return self._data()["References"]
|
|
1722
|
+
|
|
1723
|
+
def set_references_raw(self, value):
|
|
1724
|
+
r"""
|
|
1725
|
+
Set the references associated with the statistic or map.
|
|
1726
|
+
|
|
1727
|
+
INPUT:
|
|
1728
|
+
|
|
1729
|
+
- ``value`` -- string; each reference should be on a single line, and
|
|
1730
|
+
consist of one or more links to the same item
|
|
1731
|
+
|
|
1732
|
+
FindStat will automatically resolve the links, if possible.
|
|
1733
|
+
A complete list of supported services can be found at
|
|
1734
|
+
<https://findstat.org/NewStatistic>.
|
|
1735
|
+
|
|
1736
|
+
This information is used when submitting the statistic with
|
|
1737
|
+
:meth:`submit`.
|
|
1738
|
+
|
|
1739
|
+
EXAMPLES::
|
|
1740
|
+
|
|
1741
|
+
sage: q = findstat([(d, randint(1, 1000)) for d in DyckWords(4)]) # optional -- internet
|
|
1742
|
+
sage: q.set_references_raw("[[arXiv:1102.4226]]\n[[oeis:A000001]]") # optional -- internet
|
|
1743
|
+
sage: q.references() # optional -- internet
|
|
1744
|
+
0: [[arXiv:1102.4226]]
|
|
1745
|
+
1: [[oeis:A000001]]
|
|
1746
|
+
"""
|
|
1747
|
+
if value != self.references_raw():
|
|
1748
|
+
self._modified = True
|
|
1749
|
+
self._data_cache["References"] = value
|
|
1750
|
+
|
|
1751
|
+
def sage_code(self):
|
|
1752
|
+
r"""
|
|
1753
|
+
Return the Sage code associated with the statistic or map.
|
|
1754
|
+
|
|
1755
|
+
OUTPUT: an empty string or a string of the form::
|
|
1756
|
+
|
|
1757
|
+
def statistic(x):
|
|
1758
|
+
...
|
|
1759
|
+
|
|
1760
|
+
or::
|
|
1761
|
+
|
|
1762
|
+
def mapping(x):
|
|
1763
|
+
...
|
|
1764
|
+
|
|
1765
|
+
EXAMPLES::
|
|
1766
|
+
|
|
1767
|
+
sage: print(findstat(51).sage_code()) # optional -- internet
|
|
1768
|
+
def statistic(T):
|
|
1769
|
+
return T[0].node_number()
|
|
1770
|
+
"""
|
|
1771
|
+
return self._data()["SageCode"]
|
|
1772
|
+
|
|
1773
|
+
def set_sage_code(self, value):
|
|
1774
|
+
r"""
|
|
1775
|
+
Set the code associated with the statistic or map.
|
|
1776
|
+
|
|
1777
|
+
INPUT:
|
|
1778
|
+
|
|
1779
|
+
- ``value`` -- string; SageMath code producing the values of the
|
|
1780
|
+
statistic or map
|
|
1781
|
+
|
|
1782
|
+
Contributors are encouraged to submit code for statistics
|
|
1783
|
+
using :meth:`FindStatStatistic.set_code`. Modifying the
|
|
1784
|
+
"verified" SageMath code using this method is restricted to
|
|
1785
|
+
members of the FindStatCrew, for all other contributors this
|
|
1786
|
+
method has no effect.
|
|
1787
|
+
|
|
1788
|
+
EXAMPLES::
|
|
1789
|
+
|
|
1790
|
+
sage: q = findstat([(d, randint(1,1000)) for d in DyckWords(4)]) # optional -- internet
|
|
1791
|
+
sage: q.set_sage_code("def statistic(x):\n return randint(1, 1000)") # optional -- internet
|
|
1792
|
+
sage: print(q.sage_code()) # optional -- internet
|
|
1793
|
+
def statistic(x):
|
|
1794
|
+
return randint(1,1000)
|
|
1795
|
+
"""
|
|
1796
|
+
if value != self.sage_code():
|
|
1797
|
+
self._modified = True
|
|
1798
|
+
self._data_cache["SageCode"] = value
|
|
1799
|
+
|
|
1800
|
+
######################################################################
|
|
1801
|
+
# statistics
|
|
1802
|
+
######################################################################
|
|
1803
|
+
|
|
1804
|
+
|
|
1805
|
+
class FindStatCombinatorialStatistic(SageObject):
|
|
1806
|
+
"""
|
|
1807
|
+
A class providing methods to retrieve the first terms of a statistic.
|
|
1808
|
+
|
|
1809
|
+
This class provides methods applicable to instances of
|
|
1810
|
+
:class:`FindStatStatistic`, :class:`FindStatCompoundStatistic`
|
|
1811
|
+
and :class:`FindStatStatisticQuery`.
|
|
1812
|
+
"""
|
|
1813
|
+
def __init__(self):
|
|
1814
|
+
"""
|
|
1815
|
+
Initialize the combinatorial statistic.
|
|
1816
|
+
|
|
1817
|
+
TESTS::
|
|
1818
|
+
|
|
1819
|
+
sage: from sage.databases.findstat import FindStatCombinatorialStatistic
|
|
1820
|
+
sage: FindStatCombinatorialStatistic()
|
|
1821
|
+
<sage.databases.findstat.FindStatCombinatorialStatistic object at 0x...>
|
|
1822
|
+
"""
|
|
1823
|
+
self._first_terms_cache = None
|
|
1824
|
+
self._first_terms_raw_cache = None
|
|
1825
|
+
|
|
1826
|
+
def first_terms(self):
|
|
1827
|
+
r"""
|
|
1828
|
+
Return the first terms of the (compound) statistic as a
|
|
1829
|
+
dictionary.
|
|
1830
|
+
|
|
1831
|
+
OUTPUT:
|
|
1832
|
+
|
|
1833
|
+
A dictionary from Sage objects representing an element of the
|
|
1834
|
+
appropriate collection to integers.
|
|
1835
|
+
|
|
1836
|
+
This method is overridden in :class:`FindStatStatisticQuery`.
|
|
1837
|
+
|
|
1838
|
+
EXAMPLES::
|
|
1839
|
+
|
|
1840
|
+
sage: findstat(41).first_terms()[PerfectMatching([(1,6),(2,5),(3,4)])] # optional -- internet
|
|
1841
|
+
3
|
|
1842
|
+
"""
|
|
1843
|
+
# initialize self._first_terms_cache and
|
|
1844
|
+
# self._first_terms_raw_cache on first call
|
|
1845
|
+
if self._first_terms_cache is None:
|
|
1846
|
+
self._first_terms_cache = self._fetch_first_terms()
|
|
1847
|
+
# a shallow copy suffices - tuples are immutable
|
|
1848
|
+
return dict(self._first_terms_cache)
|
|
1849
|
+
|
|
1850
|
+
def _first_terms_raw(self, max_values):
|
|
1851
|
+
"""
|
|
1852
|
+
Return the first terms of the (compound) statistic as a
|
|
1853
|
+
list of ``(string, value)`` pairs.
|
|
1854
|
+
|
|
1855
|
+
INPUT:
|
|
1856
|
+
|
|
1857
|
+
- ``max_values`` -- integer determining how many terms to
|
|
1858
|
+
return at most
|
|
1859
|
+
|
|
1860
|
+
OUTPUT: list of ``(string, value)`` pairs
|
|
1861
|
+
|
|
1862
|
+
This method is overridden in :class:`FindStatStatisticQuery`.
|
|
1863
|
+
|
|
1864
|
+
TESTS::
|
|
1865
|
+
|
|
1866
|
+
sage: findstat(41)._first_terms_raw(4) # optional -- internet
|
|
1867
|
+
[('[(1,2)]', 0),
|
|
1868
|
+
('[(1,2),(3,4)]', 0),
|
|
1869
|
+
('[(1,3),(2,4)]', 0),
|
|
1870
|
+
('[(1,4),(2,3)]', 1)]
|
|
1871
|
+
"""
|
|
1872
|
+
# initialize self._first_terms_raw_cache on first call
|
|
1873
|
+
if self._first_terms_raw_cache is None:
|
|
1874
|
+
self._first_terms_raw_cache = self._fetch_first_terms_raw()
|
|
1875
|
+
# a shallow copy suffices - tuples are immutable
|
|
1876
|
+
return self._first_terms_raw_cache[:max_values]
|
|
1877
|
+
|
|
1878
|
+
def first_terms_str(self, max_values=FINDSTAT_MAX_SUBMISSION_VALUES):
|
|
1879
|
+
r"""
|
|
1880
|
+
Return the first terms of the statistic in the format needed
|
|
1881
|
+
for a FindStat query.
|
|
1882
|
+
|
|
1883
|
+
OUTPUT:
|
|
1884
|
+
|
|
1885
|
+
A string, where each line is of the form ``object => value``,
|
|
1886
|
+
where ``object`` is the string representation of an element
|
|
1887
|
+
of the appropriate collection as used by FindStat and value
|
|
1888
|
+
is an integer.
|
|
1889
|
+
|
|
1890
|
+
EXAMPLES::
|
|
1891
|
+
|
|
1892
|
+
sage: print(findstat(41).first_terms_str(max_values=4)) # optional -- internet
|
|
1893
|
+
[(1,2)] => 0
|
|
1894
|
+
[(1,2),(3,4)] => 0
|
|
1895
|
+
[(1,3),(2,4)] => 0
|
|
1896
|
+
[(1,4),(2,3)] => 1
|
|
1897
|
+
|
|
1898
|
+
TESTS:
|
|
1899
|
+
|
|
1900
|
+
Check that no more terms than asked for are computed::
|
|
1901
|
+
|
|
1902
|
+
sage: st = cached_function(lambda d: randint(1,1000))
|
|
1903
|
+
sage: s = findstat("Dyck paths", st, max_values=100, depth=0) # optional -- internet
|
|
1904
|
+
sage: len(st.cache) # optional -- internet
|
|
1905
|
+
100
|
|
1906
|
+
sage: _ = s.first_terms_str(max_values=100) # optional -- internet
|
|
1907
|
+
sage: len(st.cache) # optional -- internet
|
|
1908
|
+
100
|
|
1909
|
+
"""
|
|
1910
|
+
return "\n".join(key + " => " + str(val)
|
|
1911
|
+
for key, val in self._first_terms_raw(max_values=max_values))
|
|
1912
|
+
|
|
1913
|
+
def _fetch_first_terms(self):
|
|
1914
|
+
r"""
|
|
1915
|
+
Return the first terms of the statistic as a list of ``(object,
|
|
1916
|
+
value)`` pairs.
|
|
1917
|
+
|
|
1918
|
+
TESTS::
|
|
1919
|
+
|
|
1920
|
+
sage: findstat(41)._fetch_first_terms()[:4] # optional -- internet
|
|
1921
|
+
[([(1, 2)], 0),
|
|
1922
|
+
([(1, 2), (3, 4)], 0),
|
|
1923
|
+
([(1, 3), (2, 4)], 0),
|
|
1924
|
+
([(1, 4), (2, 3)], 1)]
|
|
1925
|
+
"""
|
|
1926
|
+
from_str = self.domain().from_string()
|
|
1927
|
+
if self._first_terms_raw_cache is None:
|
|
1928
|
+
self._first_terms_raw_cache = self._fetch_first_terms_raw()
|
|
1929
|
+
return [(from_str(obj), Integer(val))
|
|
1930
|
+
for obj, val in self._first_terms_raw_cache]
|
|
1931
|
+
|
|
1932
|
+
def _generating_functions_dict(self,
|
|
1933
|
+
max_values=FINDSTAT_MAX_SUBMISSION_VALUES):
|
|
1934
|
+
r"""
|
|
1935
|
+
Return the generating functions of ``self`` as dictionary of
|
|
1936
|
+
dictionaries, computed from ``self.first_terms``.
|
|
1937
|
+
|
|
1938
|
+
TESTS:
|
|
1939
|
+
|
|
1940
|
+
sage: q = findstat((BinaryTrees(5), list(range(0,24,4))*7)) # optional -- internet
|
|
1941
|
+
sage: q._generating_functions_dict() # optional -- internet
|
|
1942
|
+
{5: {0: 7, 4: 7, 8: 7, 12: 7, 16: 7, 20: 7}}
|
|
1943
|
+
"""
|
|
1944
|
+
gfs = {}
|
|
1945
|
+
lvls = {}
|
|
1946
|
+
domain = self.domain()
|
|
1947
|
+
levels_with_sizes = domain.levels_with_sizes()
|
|
1948
|
+
total = 0
|
|
1949
|
+
for elt, val in self.first_terms().items():
|
|
1950
|
+
if total == max_values:
|
|
1951
|
+
break
|
|
1952
|
+
lvl = domain.element_level(elt)
|
|
1953
|
+
if lvl not in levels_with_sizes:
|
|
1954
|
+
continue
|
|
1955
|
+
total += 1
|
|
1956
|
+
if lvl not in gfs:
|
|
1957
|
+
gfs[lvl] = {}
|
|
1958
|
+
gfs[lvl][val] = gfs[lvl].get(val, 0) + 1
|
|
1959
|
+
lvls[lvl] = lvls.get(lvl, 0) + 1
|
|
1960
|
+
|
|
1961
|
+
for lvl, size in lvls.items():
|
|
1962
|
+
if size < levels_with_sizes[lvl]:
|
|
1963
|
+
del gfs[lvl]
|
|
1964
|
+
return gfs
|
|
1965
|
+
|
|
1966
|
+
def generating_functions(self, style='polynomial',
|
|
1967
|
+
max_values=FINDSTAT_MAX_SUBMISSION_VALUES):
|
|
1968
|
+
r"""
|
|
1969
|
+
Return the generating functions of the statistic as a dictionary.
|
|
1970
|
+
|
|
1971
|
+
The keys of this dictionary are the levels for which the
|
|
1972
|
+
generating function of the statistic can be computed from
|
|
1973
|
+
the known data. Each value represents a generating function
|
|
1974
|
+
for one level, as a polynomial, as a dictionary, or as a list
|
|
1975
|
+
of coefficients.
|
|
1976
|
+
|
|
1977
|
+
INPUT:
|
|
1978
|
+
|
|
1979
|
+
- ``style`` -- string (default: ``'polynomial'``); can be
|
|
1980
|
+
``'polynomial'``, ``'dictionary'``, or ``'list'``
|
|
1981
|
+
|
|
1982
|
+
OUTPUT:
|
|
1983
|
+
|
|
1984
|
+
- if ``style`` is ``'polynomial'``, the generating function is
|
|
1985
|
+
returned as a polynomial
|
|
1986
|
+
|
|
1987
|
+
- if ``style`` is ``'dictionary'``, the generating function is
|
|
1988
|
+
returned as a dictionary representing the monomials of the
|
|
1989
|
+
generating function
|
|
1990
|
+
|
|
1991
|
+
- if ``style`` is ``'list'``, the generating function is
|
|
1992
|
+
returned as a list of coefficients of the generating
|
|
1993
|
+
function. In this case, leading and trailing zeros are
|
|
1994
|
+
omitted.
|
|
1995
|
+
|
|
1996
|
+
EXAMPLES::
|
|
1997
|
+
|
|
1998
|
+
sage: st = findstat(41) # optional -- internet
|
|
1999
|
+
sage: st.generating_functions() # optional -- internet
|
|
2000
|
+
{2: 1,
|
|
2001
|
+
4: q + 2,
|
|
2002
|
+
6: q^3 + 3*q^2 + 6*q + 5,
|
|
2003
|
+
8: q^6 + 4*q^5 + 10*q^4 + 20*q^3 + 28*q^2 + 28*q + 14}
|
|
2004
|
+
|
|
2005
|
+
sage: st.generating_functions(style='dictionary') # optional -- internet
|
|
2006
|
+
{2: {0: 1},
|
|
2007
|
+
4: {0: 2, 1: 1},
|
|
2008
|
+
6: {0: 5, 1: 6, 2: 3, 3: 1},
|
|
2009
|
+
8: {0: 14, 1: 28, 2: 28, 3: 20, 4: 10, 5: 4, 6: 1}}
|
|
2010
|
+
|
|
2011
|
+
sage: st.generating_functions(style='list') # optional -- internet
|
|
2012
|
+
{2: [1], 4: [2, 1], 6: [5, 6, 3, 1], 8: [14, 28, 28, 20, 10, 4, 1]}
|
|
2013
|
+
|
|
2014
|
+
TESTS::
|
|
2015
|
+
|
|
2016
|
+
sage: st = findstat(41) # optional -- internet
|
|
2017
|
+
sage: st.generating_functions(max_values=19) # optional -- internet
|
|
2018
|
+
{2: 1, 4: q + 2, 6: q^3 + 3*q^2 + 6*q + 5}
|
|
2019
|
+
|
|
2020
|
+
sage: st = findstat("graphs", lambda G: G.size(), max_values=100) # optional -- internet
|
|
2021
|
+
sage: st.generating_functions(max_values=18) # optional -- internet
|
|
2022
|
+
{1: 1,
|
|
2023
|
+
2: q + 1,
|
|
2024
|
+
3: q^3 + q^2 + q + 1,
|
|
2025
|
+
4: q^6 + q^5 + 2*q^4 + 3*q^3 + 2*q^2 + q + 1}
|
|
2026
|
+
sage: st.generating_functions(max_values=1252) # optional -- internet
|
|
2027
|
+
{1: 1,
|
|
2028
|
+
2: q + 1,
|
|
2029
|
+
3: q^3 + q^2 + q + 1,
|
|
2030
|
+
4: q^6 + q^5 + 2*q^4 + 3*q^3 + 2*q^2 + q + 1,
|
|
2031
|
+
5: q^10 + q^9 + 2*q^8 + 4*q^7 + 6*q^6 + 6*q^5 + 6*q^4 + 4*q^3 + 2*q^2 + q + 1,
|
|
2032
|
+
6: q^15 + q^14 + 2*q^13 + 5*q^12 + 9*q^11 + 15*q^10 + 21*q^9 + 24*q^8 + 24*q^7 + 21*q^6 + 15*q^5 + 9*q^4 + 5*q^3 + 2*q^2 + q + 1,
|
|
2033
|
+
7: q^21 + q^20 + 2*q^19 + 5*q^18 + 10*q^17 + 21*q^16 + 41*q^15 + 65*q^14 + 97*q^13 + 131*q^12 + 148*q^11 + 148*q^10 + 131*q^9 + 97*q^8 + 65*q^7 + 41*q^6 + 21*q^5 + 10*q^4 + 5*q^3 + 2*q^2 + q + 1}
|
|
2034
|
+
"""
|
|
2035
|
+
d = self._generating_functions_dict(max_values=max_values)
|
|
2036
|
+
return _generating_functions_from_dict(d, style)
|
|
2037
|
+
|
|
2038
|
+
def oeis_search(self, search_size=32, verbose=True):
|
|
2039
|
+
r"""
|
|
2040
|
+
Search the OEIS for the generating function of the statistic.
|
|
2041
|
+
|
|
2042
|
+
INPUT:
|
|
2043
|
+
|
|
2044
|
+
- ``search_size`` -- (default: 32) the number of integers in the
|
|
2045
|
+
sequence. If this is chosen too big, the OEIS result may be
|
|
2046
|
+
corrupted.
|
|
2047
|
+
|
|
2048
|
+
- ``verbose`` -- boolean (default: ``True``); if ``True``, some
|
|
2049
|
+
information about the search are printed
|
|
2050
|
+
|
|
2051
|
+
OUTPUT: a tuple of OEIS sequences, see
|
|
2052
|
+
:meth:`sage.databases.oeis.OEIS.find_by_description` for more
|
|
2053
|
+
information
|
|
2054
|
+
|
|
2055
|
+
EXAMPLES::
|
|
2056
|
+
|
|
2057
|
+
sage: st = findstat(41) # optional -- internet
|
|
2058
|
+
|
|
2059
|
+
sage: st.oeis_search() # optional -- internet
|
|
2060
|
+
Searching the OEIS for "1 2,1 5,6,3,1 14,28,28,20,10,4,1"
|
|
2061
|
+
0: A067311: Triangle read by rows: T(n,k) gives number of ways of arranging n chords on a circle with k simple intersections ...
|
|
2062
|
+
"""
|
|
2063
|
+
from sage.databases.oeis import oeis
|
|
2064
|
+
gen_funcs = self.generating_functions(style='list')
|
|
2065
|
+
|
|
2066
|
+
OEIS_string = ""
|
|
2067
|
+
keys = sorted(gen_funcs.keys())
|
|
2068
|
+
counter = 0
|
|
2069
|
+
for key in keys:
|
|
2070
|
+
gen_func = gen_funcs[key]
|
|
2071
|
+
while gen_func[0] == 0:
|
|
2072
|
+
gen_func.pop(0)
|
|
2073
|
+
# we strip the result according to the search size. -- stumpc5, 2015-09-27
|
|
2074
|
+
gen_func = gen_func[:search_size]
|
|
2075
|
+
counter += len(gen_func)
|
|
2076
|
+
if search_size > 0:
|
|
2077
|
+
search_size -= len(gen_func)
|
|
2078
|
+
OEIS_func_string = ",".join(str(coefficient) for coefficient in gen_func)
|
|
2079
|
+
OEIS_string += OEIS_func_string + " "
|
|
2080
|
+
OEIS_string = OEIS_string.strip()
|
|
2081
|
+
if counter >= 4:
|
|
2082
|
+
if verbose:
|
|
2083
|
+
print('Searching the OEIS for "%s"' % OEIS_string)
|
|
2084
|
+
return oeis(OEIS_string)
|
|
2085
|
+
|
|
2086
|
+
if verbose:
|
|
2087
|
+
print("Too little information to search the OEIS for this statistic (only %s values given)." % counter)
|
|
2088
|
+
|
|
2089
|
+
|
|
2090
|
+
class FindStatStatistic(Element,
|
|
2091
|
+
FindStatFunction,
|
|
2092
|
+
FindStatCombinatorialStatistic,
|
|
2093
|
+
metaclass=InheritComparisonClasscallMetaclass):
|
|
2094
|
+
r"""
|
|
2095
|
+
A FindStat statistic.
|
|
2096
|
+
|
|
2097
|
+
:class:`FindStatStatistic` is a class representing a
|
|
2098
|
+
combinatorial statistic available in the FindStat database.
|
|
2099
|
+
|
|
2100
|
+
This class provides methods to inspect and update various
|
|
2101
|
+
properties of these statistics.
|
|
2102
|
+
|
|
2103
|
+
EXAMPLES::
|
|
2104
|
+
|
|
2105
|
+
sage: from sage.databases.findstat import FindStatStatistic
|
|
2106
|
+
sage: FindStatStatistic(41) # optional -- internet
|
|
2107
|
+
St000041: The number of nestings of a perfect matching.
|
|
2108
|
+
|
|
2109
|
+
.. SEEALSO::
|
|
2110
|
+
|
|
2111
|
+
:class:`FindStatStatistics`
|
|
2112
|
+
"""
|
|
2113
|
+
@staticmethod
|
|
2114
|
+
def __classcall_private__(cls, entry):
|
|
2115
|
+
"""
|
|
2116
|
+
Retrieve a statistic from the database.
|
|
2117
|
+
|
|
2118
|
+
TESTS::
|
|
2119
|
+
|
|
2120
|
+
sage: from sage.databases.findstat import FindStatStatistic
|
|
2121
|
+
sage: FindStatStatistic("abcdefgh") # optional -- internet
|
|
2122
|
+
Traceback (most recent call last):
|
|
2123
|
+
...
|
|
2124
|
+
ValueError: the value 'abcdefgh' is not a valid FindStat statistic identifier
|
|
2125
|
+
"""
|
|
2126
|
+
return FindStatStatistics()(entry)
|
|
2127
|
+
|
|
2128
|
+
def __init__(self, parent, id):
|
|
2129
|
+
"""
|
|
2130
|
+
Initialize a FindStat statistic from an identifier.
|
|
2131
|
+
|
|
2132
|
+
INPUT:
|
|
2133
|
+
|
|
2134
|
+
- ``parent`` -- :class:`FindStatStatistics`
|
|
2135
|
+
|
|
2136
|
+
- ``id`` -- the (padded) FindStat identifier of the statistic
|
|
2137
|
+
|
|
2138
|
+
EXAMPLES::
|
|
2139
|
+
|
|
2140
|
+
sage: findstat(41) # optional -- internet, indirect doctest
|
|
2141
|
+
St000041: The number of nestings of a perfect matching.
|
|
2142
|
+
"""
|
|
2143
|
+
FindStatFunction.__init__(self, id)
|
|
2144
|
+
FindStatCombinatorialStatistic.__init__(self)
|
|
2145
|
+
Element.__init__(self, parent)
|
|
2146
|
+
|
|
2147
|
+
def __call__(self, elt):
|
|
2148
|
+
"""
|
|
2149
|
+
Apply the statistic to a given element.
|
|
2150
|
+
|
|
2151
|
+
EXAMPLES::
|
|
2152
|
+
|
|
2153
|
+
sage: s = lambda g: g.diameter() if g.is_connected() else None
|
|
2154
|
+
sage: q = findstat("graphs", s, max_values=100) # optional -- internet
|
|
2155
|
+
sage: q(graphs.PetersenGraph().copy(immutable=True)) # optional -- internet
|
|
2156
|
+
2
|
|
2157
|
+
"""
|
|
2158
|
+
val = self.first_terms().get(elt, None)
|
|
2159
|
+
if val is None:
|
|
2160
|
+
return FindStatFunction.__call__(self, elt)
|
|
2161
|
+
return val
|
|
2162
|
+
|
|
2163
|
+
def __reduce__(self):
|
|
2164
|
+
"""
|
|
2165
|
+
Return a function and its arguments needed to create the
|
|
2166
|
+
statistic.
|
|
2167
|
+
|
|
2168
|
+
TESTS::
|
|
2169
|
+
|
|
2170
|
+
sage: from sage.databases.findstat import FindStatStatistic
|
|
2171
|
+
sage: c = FindStatStatistic(41) # optional -- internet
|
|
2172
|
+
sage: loads(dumps(c)) == c # optional -- internet
|
|
2173
|
+
True
|
|
2174
|
+
"""
|
|
2175
|
+
return FindStatStatistic, (self.id(),)
|
|
2176
|
+
|
|
2177
|
+
def _richcmp_(self, other, op):
|
|
2178
|
+
"""
|
|
2179
|
+
Compare two statistics by identifier.
|
|
2180
|
+
|
|
2181
|
+
TESTS::
|
|
2182
|
+
|
|
2183
|
+
sage: findstat(41) != findstat(42) # optional -- internet
|
|
2184
|
+
True
|
|
2185
|
+
sage: findstat(41) == findstat(41) # optional -- internet
|
|
2186
|
+
True
|
|
2187
|
+
"""
|
|
2188
|
+
return richcmp(self.id(), other.id(), op)
|
|
2189
|
+
|
|
2190
|
+
def _fetch_data(self):
|
|
2191
|
+
r"""
|
|
2192
|
+
Return a dictionary containing the data of the statistic, except
|
|
2193
|
+
for the values, fetched from FindStat.
|
|
2194
|
+
|
|
2195
|
+
TESTS::
|
|
2196
|
+
|
|
2197
|
+
sage: findstat(41)._data() # optional -- internet, indirect doctest
|
|
2198
|
+
{'Bibliography': {'MathSciNet:1288802': {'Author': 'de Médicis, A., Viennot, X. G.',
|
|
2199
|
+
'Title': 'Moments des $q$-polynômes de Laguerre et la bijection de Foata-Zeilberger'},
|
|
2200
|
+
'MathSciNet:1418763': {'Author': 'Simion, R., Stanton, D.',
|
|
2201
|
+
'Title': 'Octabasic Laguerre polynomials and permutation statistics'}},
|
|
2202
|
+
'Code': 'def statistic(x):\r\n return len(x.nestings())',
|
|
2203
|
+
'Description': 'The number of nestings of a perfect matching.\r\n\r\nThis is the number of pairs of edges $((a,b), (c,d))$ such that $a\\le c\\le d\\le b$. i.e., the edge $(c,d)$ is nested inside $(a,b)$.',
|
|
2204
|
+
'Domain': 'Cc0012',
|
|
2205
|
+
'Name': 'The number of nestings of a perfect matching.',
|
|
2206
|
+
'References': '[1] [[MathSciNet:1288802]]\r\n[2] [[MathSciNet:1418763]]',
|
|
2207
|
+
'SageCode': 'def statistic(x):\r\n return len(x.nestings())'}
|
|
2208
|
+
"""
|
|
2209
|
+
fields = "Bibliography,Code,Description,Domain,Name,References,SageCode"
|
|
2210
|
+
fields_Bibliography = "Author,Title"
|
|
2211
|
+
url = (FINDSTAT_API_STATISTICS + self.id_str()
|
|
2212
|
+
+ "?fields=" + fields
|
|
2213
|
+
+ "&fields[Bibliography]=" + fields_Bibliography)
|
|
2214
|
+
verbose("fetching statistic data %s" % url, caller_name='FindStatStatistic')
|
|
2215
|
+
|
|
2216
|
+
included = _get_json(url)["included"]
|
|
2217
|
+
# slightly simplify the representation
|
|
2218
|
+
data = dict(included["Statistics"][self.id_str()].items())
|
|
2219
|
+
# we replace the list of identifiers in Bibliography with the dictionary
|
|
2220
|
+
data["Bibliography"] = included["References"]
|
|
2221
|
+
return data
|
|
2222
|
+
|
|
2223
|
+
def _fetch_first_terms_raw(self):
|
|
2224
|
+
r"""
|
|
2225
|
+
Return the first terms of the statistic, as ``(string,
|
|
2226
|
+
value)`` pairs, fetched from FindStat
|
|
2227
|
+
|
|
2228
|
+
TESTS::
|
|
2229
|
+
|
|
2230
|
+
sage: findstat(41)._first_terms_raw(4) # optional -- internet, indirect doctest
|
|
2231
|
+
[('[(1,2)]', 0),
|
|
2232
|
+
('[(1,2),(3,4)]', 0),
|
|
2233
|
+
('[(1,3),(2,4)]', 0),
|
|
2234
|
+
('[(1,4),(2,3)]', 1)]
|
|
2235
|
+
"""
|
|
2236
|
+
fields = "Values"
|
|
2237
|
+
url = FINDSTAT_API_STATISTICS + self.id_str() + "?fields=" + fields
|
|
2238
|
+
values = _get_json(url)["included"]["Statistics"][self.id_str()]["Values"]
|
|
2239
|
+
return [tuple(pair) for pair in values]
|
|
2240
|
+
|
|
2241
|
+
def set_first_terms(self, values):
|
|
2242
|
+
r"""
|
|
2243
|
+
Update the first terms of the statistic.
|
|
2244
|
+
|
|
2245
|
+
INPUT:
|
|
2246
|
+
|
|
2247
|
+
- ``values`` -- list of pairs of the form ``(object, value)`` where
|
|
2248
|
+
``object`` is a Sage object representing an element of the
|
|
2249
|
+
appropriate collection and ``value`` is an integer
|
|
2250
|
+
|
|
2251
|
+
This information is used when submitting the statistic with
|
|
2252
|
+
:meth:`submit`.
|
|
2253
|
+
|
|
2254
|
+
.. WARNING::
|
|
2255
|
+
|
|
2256
|
+
This method cannot check whether the given values are
|
|
2257
|
+
actually correct. Moreover, it does not even perform any
|
|
2258
|
+
sanity checks.
|
|
2259
|
+
|
|
2260
|
+
TESTS::
|
|
2261
|
+
|
|
2262
|
+
sage: s = findstat(41) # optional -- internet
|
|
2263
|
+
sage: l = [([(1,2)], 1), ([(1,2),(3,4)], 7), ([(1,3),(2,4)], 8), ([(1,4),(2,3)], 3)]
|
|
2264
|
+
sage: s.set_first_terms(l) # optional -- internet
|
|
2265
|
+
sage: print(s.first_terms_str()) # optional -- internet
|
|
2266
|
+
[(1, 2)] => 1
|
|
2267
|
+
[(1, 2), (3, 4)] => 7
|
|
2268
|
+
[(1, 3), (2, 4)] => 8
|
|
2269
|
+
[(1, 4), (2, 3)] => 3
|
|
2270
|
+
sage: s.reset() # optional -- internet
|
|
2271
|
+
"""
|
|
2272
|
+
to_str = self.domain().to_string()
|
|
2273
|
+
new = [(to_str(obj), value) for obj, value in values]
|
|
2274
|
+
if sorted(new) != sorted(self.first_terms_str()):
|
|
2275
|
+
self._modified = True
|
|
2276
|
+
self._first_terms_raw_cache = new
|
|
2277
|
+
self._first_terms_cache = values
|
|
2278
|
+
|
|
2279
|
+
def code(self):
|
|
2280
|
+
r"""
|
|
2281
|
+
Return the code associated with the statistic or map.
|
|
2282
|
+
|
|
2283
|
+
OUTPUT: string
|
|
2284
|
+
|
|
2285
|
+
Contributors are encouraged to submit Sage code in the form::
|
|
2286
|
+
|
|
2287
|
+
def statistic(x):
|
|
2288
|
+
...
|
|
2289
|
+
|
|
2290
|
+
but the string may also contain code for other computer
|
|
2291
|
+
algebra systems.
|
|
2292
|
+
|
|
2293
|
+
EXAMPLES::
|
|
2294
|
+
|
|
2295
|
+
sage: print(findstat(41).code()) # optional -- internet
|
|
2296
|
+
def statistic(x):
|
|
2297
|
+
return len(x.nestings())
|
|
2298
|
+
|
|
2299
|
+
sage: print(findstat(118).code()) # optional -- internet
|
|
2300
|
+
(* in Mathematica *)
|
|
2301
|
+
tree = {{{{}, {}}, {{}, {}}}, {{{}, {}}, {{}, {}}}};
|
|
2302
|
+
Count[tree, {{___}, {{___}, {{___}, {___}}}}, {0, Infinity}]
|
|
2303
|
+
"""
|
|
2304
|
+
return self._data()["Code"]
|
|
2305
|
+
|
|
2306
|
+
def set_code(self, value):
|
|
2307
|
+
r"""
|
|
2308
|
+
Set the code associated with the statistic.
|
|
2309
|
+
|
|
2310
|
+
INPUT:
|
|
2311
|
+
|
|
2312
|
+
- ``value`` -- string; code producing the values of the statistic
|
|
2313
|
+
|
|
2314
|
+
Contributors are encouraged to submit SageMath code in the form::
|
|
2315
|
+
|
|
2316
|
+
def statistic(x):
|
|
2317
|
+
...
|
|
2318
|
+
|
|
2319
|
+
However, code for any other platform is accepted also.
|
|
2320
|
+
|
|
2321
|
+
This information is used when submitting the statistic with
|
|
2322
|
+
:meth:`submit`.
|
|
2323
|
+
|
|
2324
|
+
EXAMPLES::
|
|
2325
|
+
|
|
2326
|
+
sage: q = findstat([(d, randint(1,1000)) for d in DyckWords(4)]) # optional -- internet
|
|
2327
|
+
sage: q.set_code("def statistic(x):\n return randint(1,1000)") # optional -- internet
|
|
2328
|
+
sage: print(q.code()) # optional -- internet
|
|
2329
|
+
def statistic(x):
|
|
2330
|
+
return randint(1,1000)
|
|
2331
|
+
"""
|
|
2332
|
+
if value != self.code():
|
|
2333
|
+
self._modified = True
|
|
2334
|
+
self._data_cache["Code"] = value
|
|
2335
|
+
|
|
2336
|
+
def browse(self):
|
|
2337
|
+
r"""
|
|
2338
|
+
Open the FindStat web page of the statistic in a browser.
|
|
2339
|
+
|
|
2340
|
+
EXAMPLES::
|
|
2341
|
+
|
|
2342
|
+
sage: findstat(41).browse() # optional -- webbrowser
|
|
2343
|
+
"""
|
|
2344
|
+
if self.id() == 0:
|
|
2345
|
+
self.submit()
|
|
2346
|
+
else:
|
|
2347
|
+
webbrowser.open(FINDSTAT_URL_STATISTICS + self.id_str())
|
|
2348
|
+
|
|
2349
|
+
def submit(self, max_values=FINDSTAT_MAX_SUBMISSION_VALUES):
|
|
2350
|
+
r"""
|
|
2351
|
+
Open the FindStat web page for editing the statistic or
|
|
2352
|
+
submitting a new statistic in a browser.
|
|
2353
|
+
|
|
2354
|
+
TESTS::
|
|
2355
|
+
|
|
2356
|
+
sage: s = findstat([(d, randint(1,1000)) for d in DyckWords(4)]) # optional -- internet
|
|
2357
|
+
sage: s.set_description(u"Möbius") # optional -- internet
|
|
2358
|
+
sage: s.submit() # optional -- webbrowser
|
|
2359
|
+
"""
|
|
2360
|
+
args = {}
|
|
2361
|
+
args["OriginalStatistic"] = self.id_str()
|
|
2362
|
+
args["Domain"] = self.domain().id_str()
|
|
2363
|
+
args["Values"] = self.first_terms_str(max_values=max_values)
|
|
2364
|
+
args["Description"] = self.description()
|
|
2365
|
+
args["References"] = self.references_raw()
|
|
2366
|
+
args["Code"] = self.code()
|
|
2367
|
+
args["SageCode"] = self.sage_code()
|
|
2368
|
+
args["CurrentAuthor"] = FindStat().user_name()
|
|
2369
|
+
args["CurrentEmail"] = FindStat().user_email()
|
|
2370
|
+
|
|
2371
|
+
if not self.id():
|
|
2372
|
+
url = FINDSTAT_NEWSTATISTIC_FORM_HEADER % FINDSTAT_URL_NEW_STATISTIC
|
|
2373
|
+
else:
|
|
2374
|
+
url = FINDSTAT_NEWSTATISTIC_FORM_HEADER % (FINDSTAT_URL_EDIT_STATISTIC + self.id_str())
|
|
2375
|
+
_submit(args, url)
|
|
2376
|
+
|
|
2377
|
+
# editing and submitting is really the same thing
|
|
2378
|
+
edit = submit
|
|
2379
|
+
|
|
2380
|
+
def __hash__(self):
|
|
2381
|
+
"""
|
|
2382
|
+
Return a hash value for the statistic.
|
|
2383
|
+
|
|
2384
|
+
EXAMPLES::
|
|
2385
|
+
|
|
2386
|
+
sage: from sage.databases.findstat import FindStatMaps
|
|
2387
|
+
sage: list(FindStatMaps(domain=1, codomain=10)) # optional -- internet
|
|
2388
|
+
[Mp00061: to increasing tree, Mp00072: binary search tree: left to right]
|
|
2389
|
+
"""
|
|
2390
|
+
return self.id()
|
|
2391
|
+
|
|
2392
|
+
def info(self):
|
|
2393
|
+
"""
|
|
2394
|
+
Print a detailed description of the statistic.
|
|
2395
|
+
|
|
2396
|
+
EXAMPLES::
|
|
2397
|
+
|
|
2398
|
+
sage: findstat("St000042").info() # optional -- internet
|
|
2399
|
+
St000042: The number of crossings of a perfect matching.
|
|
2400
|
+
"""
|
|
2401
|
+
print(" %s" % self)
|
|
2402
|
+
|
|
2403
|
+
|
|
2404
|
+
_all_statistics = {}
|
|
2405
|
+
|
|
2406
|
+
|
|
2407
|
+
class FindStatStatistics(UniqueRepresentation, Parent):
|
|
2408
|
+
r"""
|
|
2409
|
+
The class of FindStat statistics.
|
|
2410
|
+
|
|
2411
|
+
The elements of this class are combinatorial statistics currently
|
|
2412
|
+
in FindStat.
|
|
2413
|
+
|
|
2414
|
+
EXAMPLES:
|
|
2415
|
+
|
|
2416
|
+
We can print a list of the first few statistics currently in
|
|
2417
|
+
FindStat in a given domain::
|
|
2418
|
+
|
|
2419
|
+
sage: from sage.databases.findstat import FindStatStatistics
|
|
2420
|
+
sage: for st, _ in zip(FindStatStatistics("Perfect Matchings"), range(3)): # optional -- internet
|
|
2421
|
+
....: print(" " + st.name())
|
|
2422
|
+
The number of nestings of a perfect matching.
|
|
2423
|
+
The number of crossings of a perfect matching.
|
|
2424
|
+
The number of crossings plus two-nestings of a perfect matching.
|
|
2425
|
+
"""
|
|
2426
|
+
def __init__(self, domain=None):
|
|
2427
|
+
"""
|
|
2428
|
+
TESTS::
|
|
2429
|
+
|
|
2430
|
+
sage: from sage.databases.findstat import FindStatStatistics
|
|
2431
|
+
sage: M = FindStatStatistics() # optional -- internet
|
|
2432
|
+
sage: TestSuite(M).run() # optional -- internet
|
|
2433
|
+
"""
|
|
2434
|
+
if domain is None:
|
|
2435
|
+
self._domain = None
|
|
2436
|
+
else:
|
|
2437
|
+
self._domain = FindStatCollection(domain)
|
|
2438
|
+
self._identifiers = None
|
|
2439
|
+
Parent.__init__(self, category=Sets())
|
|
2440
|
+
|
|
2441
|
+
def _element_constructor_(self, id):
|
|
2442
|
+
"""
|
|
2443
|
+
Initialize a FindStat statistic.
|
|
2444
|
+
|
|
2445
|
+
INPUT:
|
|
2446
|
+
|
|
2447
|
+
- ``id`` -- string containing the FindStat identifier of
|
|
2448
|
+
the statistic, or the corresponding integer
|
|
2449
|
+
|
|
2450
|
+
EXAMPLES::
|
|
2451
|
+
|
|
2452
|
+
sage: from sage.databases.findstat import FindStatStatistic
|
|
2453
|
+
sage: FindStatStatistic(41) # optional -- internet
|
|
2454
|
+
St000041: The number of nestings of a perfect matching.
|
|
2455
|
+
"""
|
|
2456
|
+
if isinstance(id, self.Element):
|
|
2457
|
+
return id
|
|
2458
|
+
if isinstance(id, (int, Integer)):
|
|
2459
|
+
id = FINDSTAT_STATISTIC_PADDED_IDENTIFIER % id
|
|
2460
|
+
elif isinstance(id, FindStatCombinatorialStatistic):
|
|
2461
|
+
id = id.id_str()
|
|
2462
|
+
if not isinstance(id, str):
|
|
2463
|
+
raise TypeError("the value '%s' is not a valid FindStat statistic identifier, nor a FindStat statistic query" % id)
|
|
2464
|
+
else:
|
|
2465
|
+
id = id.strip()
|
|
2466
|
+
if FINDSTAT_MAP_SEPARATOR in id:
|
|
2467
|
+
return FindStatCompoundStatistic(id)
|
|
2468
|
+
if not re.match(FINDSTAT_STATISTIC_REGEXP, id) or int(id[2:]) <= 0:
|
|
2469
|
+
raise ValueError("the value '%s' is not a valid FindStat statistic identifier" % id)
|
|
2470
|
+
if id not in _all_statistics or _all_statistics[id] is None:
|
|
2471
|
+
_all_statistics[id] = self.element_class(self, id)
|
|
2472
|
+
|
|
2473
|
+
return _all_statistics[id]
|
|
2474
|
+
|
|
2475
|
+
def _repr_(self):
|
|
2476
|
+
"""
|
|
2477
|
+
Return a short description of the set of FindStat statistics.
|
|
2478
|
+
|
|
2479
|
+
EXAMPLES::
|
|
2480
|
+
|
|
2481
|
+
sage: from sage.databases.findstat import FindStatStatistics
|
|
2482
|
+
sage: FindStatStatistics() # optional -- internet
|
|
2483
|
+
Set of combinatorial statistics in FindStat
|
|
2484
|
+
|
|
2485
|
+
sage: FindStatStatistics(12) # optional -- internet
|
|
2486
|
+
Set of combinatorial statistics with domain Cc0012: Perfect matchings in FindStat
|
|
2487
|
+
"""
|
|
2488
|
+
if self._domain is None:
|
|
2489
|
+
return "Set of combinatorial statistics in FindStat"
|
|
2490
|
+
return "Set of combinatorial statistics with domain %s in FindStat" % self._domain
|
|
2491
|
+
|
|
2492
|
+
def __iter__(self):
|
|
2493
|
+
"""
|
|
2494
|
+
Return an iterator over all FindStat statistics.
|
|
2495
|
+
|
|
2496
|
+
EXAMPLES::
|
|
2497
|
+
|
|
2498
|
+
sage: from sage.databases.findstat import FindStatStatistics
|
|
2499
|
+
sage: next(iter(FindStatStatistics("Perfect Matchings"))) # optional -- internet
|
|
2500
|
+
St000041: The number of nestings of a perfect matching.
|
|
2501
|
+
"""
|
|
2502
|
+
if self._identifiers is None:
|
|
2503
|
+
if self._domain is None:
|
|
2504
|
+
url = FINDSTAT_API_STATISTICS
|
|
2505
|
+
else:
|
|
2506
|
+
url = FINDSTAT_API_STATISTICS + "?Domain=%s" % self._domain.id_str()
|
|
2507
|
+
|
|
2508
|
+
self._identifiers = _get_json(url)["data"]
|
|
2509
|
+
|
|
2510
|
+
for st in self._identifiers:
|
|
2511
|
+
yield FindStatStatistic(st)
|
|
2512
|
+
|
|
2513
|
+
def _an_element_(self):
|
|
2514
|
+
"""
|
|
2515
|
+
Return a FindStat statistic.
|
|
2516
|
+
|
|
2517
|
+
EXAMPLES::
|
|
2518
|
+
|
|
2519
|
+
sage: findstat(domain='Permutations').an_element() # optional -- internet
|
|
2520
|
+
St000001: The number of reduced words for a permutation.
|
|
2521
|
+
"""
|
|
2522
|
+
try:
|
|
2523
|
+
return next(iter(self))
|
|
2524
|
+
except StopIteration:
|
|
2525
|
+
from sage.categories.sets_cat import EmptySetError
|
|
2526
|
+
raise EmptySetError
|
|
2527
|
+
|
|
2528
|
+
Element = FindStatStatistic
|
|
2529
|
+
|
|
2530
|
+
|
|
2531
|
+
class FindStatStatisticQuery(FindStatStatistic):
|
|
2532
|
+
"""
|
|
2533
|
+
A class representing a query for FindStat (compound) statistics.
|
|
2534
|
+
"""
|
|
2535
|
+
def __init__(self, data=None, values_of=None, distribution_of=None,
|
|
2536
|
+
domain=None, known_terms=None, function=None,
|
|
2537
|
+
depth=FINDSTAT_DEFAULT_DEPTH,
|
|
2538
|
+
debug=False):
|
|
2539
|
+
"""
|
|
2540
|
+
Initialize a query for FindStat (compound) statistics.
|
|
2541
|
+
|
|
2542
|
+
INPUT:
|
|
2543
|
+
|
|
2544
|
+
- ``data`` -- (optional), a list of pairs ``(objects,
|
|
2545
|
+
values)``, where ``objects`` and ``values`` are all lists
|
|
2546
|
+
of the same length, the former are elements in the FindStat
|
|
2547
|
+
collection, the latter are integers
|
|
2548
|
+
|
|
2549
|
+
- ``known_terms`` -- (optional), a lazy list in the same format
|
|
2550
|
+
as ``data``, which agrees with ``data``, and may be used
|
|
2551
|
+
for submission
|
|
2552
|
+
|
|
2553
|
+
- ``values_of`` -- (optional), anything accepted by
|
|
2554
|
+
:class:`FindStatCompoundStatistic`
|
|
2555
|
+
|
|
2556
|
+
- ``distribution_of`` -- (optional), anything accepted by
|
|
2557
|
+
:class:`FindStatCompoundStatistic`
|
|
2558
|
+
|
|
2559
|
+
- ``domain`` -- (optional), anything accepted by
|
|
2560
|
+
:class:`FindStatCollection`
|
|
2561
|
+
|
|
2562
|
+
- ``depth`` -- (optional), the number of maps to apply before
|
|
2563
|
+
applying the statistic
|
|
2564
|
+
|
|
2565
|
+
|
|
2566
|
+
Only one of ``data``, ``values_of`` and ``distribution_of``
|
|
2567
|
+
may be provided. The parameter ``domain`` must be provided
|
|
2568
|
+
if and only if ``data`` is provided, or ``values_of`` or
|
|
2569
|
+
``distribution_of`` are given as a function.
|
|
2570
|
+
|
|
2571
|
+
The parameter ``known_terms`` is only allowed, if ``data`` is
|
|
2572
|
+
provided. It defaults to ``data``.
|
|
2573
|
+
|
|
2574
|
+
EXAMPLES::
|
|
2575
|
+
|
|
2576
|
+
sage: from sage.databases.findstat import FindStatStatisticQuery
|
|
2577
|
+
sage: data = [[[m], [m.number_of_nestings()]] for n in range(5) for m in PerfectMatchings(2*n)]
|
|
2578
|
+
sage: FindStatStatisticQuery(domain=12, data=data, depth=1) # optional -- internet
|
|
2579
|
+
0: St000041 (quality [99, 100])
|
|
2580
|
+
1: St000041oMp00113 (quality [99, 100])
|
|
2581
|
+
2: St000042oMp00116 (quality [99, 100])
|
|
2582
|
+
...
|
|
2583
|
+
"""
|
|
2584
|
+
self._first_terms = data
|
|
2585
|
+
if data is not None and known_terms is None:
|
|
2586
|
+
self._known_terms = data
|
|
2587
|
+
else:
|
|
2588
|
+
self._known_terms = known_terms
|
|
2589
|
+
self._values_of = None
|
|
2590
|
+
self._distribution_of = None
|
|
2591
|
+
self._depth = depth
|
|
2592
|
+
|
|
2593
|
+
if data is not None:
|
|
2594
|
+
assert all(param is None for param in [distribution_of, values_of])
|
|
2595
|
+
|
|
2596
|
+
domain = FindStatCollection(domain)
|
|
2597
|
+
query = {"Domain": domain.id_str(),
|
|
2598
|
+
"Data": _data_to_str(self._first_terms, domain)}
|
|
2599
|
+
|
|
2600
|
+
elif distribution_of is not None:
|
|
2601
|
+
assert all(param is None for param in [data, known_terms, values_of])
|
|
2602
|
+
|
|
2603
|
+
self._distribution_of = FindStatCompoundStatistic(distribution_of)
|
|
2604
|
+
domain = self._distribution_of.domain()
|
|
2605
|
+
query = {"DistributionOf": self._distribution_of.id_str()}
|
|
2606
|
+
|
|
2607
|
+
elif values_of is not None:
|
|
2608
|
+
assert all(param is None for param in [data, known_terms, distribution_of])
|
|
2609
|
+
|
|
2610
|
+
self._values_of = FindStatCompoundStatistic(values_of)
|
|
2611
|
+
domain = self._values_of.domain()
|
|
2612
|
+
query = {"ValuesOf": self._values_of.id_str()}
|
|
2613
|
+
|
|
2614
|
+
else:
|
|
2615
|
+
raise ValueError("incompatible set of parameters: data: %s, distribution_of: %s, values_of: %s" % ((data, distribution_of, values_of)))
|
|
2616
|
+
|
|
2617
|
+
if depth is not None:
|
|
2618
|
+
query["Depth"] = depth
|
|
2619
|
+
|
|
2620
|
+
query["fields"] = "MatchingStatistic,Offset,Quality"
|
|
2621
|
+
if debug:
|
|
2622
|
+
print(query)
|
|
2623
|
+
verbose("querying FindStat %s" % query, caller_name='FindStatStatisticQuery')
|
|
2624
|
+
response = _post_json(FINDSTAT_API_STATISTICS, query)
|
|
2625
|
+
|
|
2626
|
+
if debug:
|
|
2627
|
+
print(response)
|
|
2628
|
+
if "data" not in response:
|
|
2629
|
+
raise ValueError(response["error"])
|
|
2630
|
+
|
|
2631
|
+
result = []
|
|
2632
|
+
for match in response["data"]:
|
|
2633
|
+
entry = response["included"]["MatchingStatistics"][match]
|
|
2634
|
+
result.append(FindStatMatchingStatistic(entry["MatchingStatistic"],
|
|
2635
|
+
entry["Offset"],
|
|
2636
|
+
entry["Quality"],
|
|
2637
|
+
domain=domain))
|
|
2638
|
+
|
|
2639
|
+
self._result = FancyTuple(result)
|
|
2640
|
+
|
|
2641
|
+
FindStatFunction.__init__(self, FINDSTAT_STATISTIC_PADDED_IDENTIFIER % 0,
|
|
2642
|
+
data={"Bibliography": {},
|
|
2643
|
+
"Code": _get_code_from_callable(function),
|
|
2644
|
+
"Description": "",
|
|
2645
|
+
"Domain": domain,
|
|
2646
|
+
"Name": "a new statistic on %s" % domain.name("plural"),
|
|
2647
|
+
"References": "",
|
|
2648
|
+
"SageCode": ""},
|
|
2649
|
+
function=function)
|
|
2650
|
+
Element.__init__(self, FindStatStatistics()) # this is not completely correct, but it works
|
|
2651
|
+
|
|
2652
|
+
def first_terms(self, max_values=FINDSTAT_MAX_SUBMISSION_VALUES):
|
|
2653
|
+
"""
|
|
2654
|
+
Return the pairs of the known terms which contain singletons as a dictionary.
|
|
2655
|
+
|
|
2656
|
+
EXAMPLES::
|
|
2657
|
+
|
|
2658
|
+
sage: PM = PerfectMatchings
|
|
2659
|
+
sage: l = [(PM(2*n), [m.number_of_nestings() for m in PM(2*n)]) for n in range(5)]
|
|
2660
|
+
sage: r = findstat(l, depth=0); r # optional -- internet
|
|
2661
|
+
0: St000041 (quality [99, 100])
|
|
2662
|
+
1: St000042 (quality [99, 100])
|
|
2663
|
+
sage: r.first_terms() # optional -- internet
|
|
2664
|
+
{[]: 0, [(1, 2)]: 0}
|
|
2665
|
+
"""
|
|
2666
|
+
return dict(itertools.islice(((objs[0], vals[0])
|
|
2667
|
+
for objs, vals in self._known_terms
|
|
2668
|
+
if len(vals) == 1), max_values))
|
|
2669
|
+
|
|
2670
|
+
def _first_terms_raw(self, max_values):
|
|
2671
|
+
"""
|
|
2672
|
+
Return the first terms as ``(string, value)`` pairs.
|
|
2673
|
+
|
|
2674
|
+
EXAMPLES::
|
|
2675
|
+
|
|
2676
|
+
sage: PM = PerfectMatchings
|
|
2677
|
+
sage: l = [(PM(2*n), [m.number_of_nestings() for m in PM(2*n)]) for n in range(5)]
|
|
2678
|
+
sage: r = findstat(l, depth=0); r # optional -- internet
|
|
2679
|
+
0: St000041 (quality [99, 100])
|
|
2680
|
+
1: St000042 (quality [99, 100])
|
|
2681
|
+
sage: r._first_terms_raw(100) # optional -- internet
|
|
2682
|
+
[('[]', 0), ('[(1, 2)]', 0)]
|
|
2683
|
+
"""
|
|
2684
|
+
to_str = self.domain().to_string()
|
|
2685
|
+
return [(to_str(obj), val)
|
|
2686
|
+
for obj, val in self.first_terms(max_values=max_values).items()]
|
|
2687
|
+
|
|
2688
|
+
def _generating_functions_dict(self,
|
|
2689
|
+
max_values=FINDSTAT_MAX_SUBMISSION_VALUES):
|
|
2690
|
+
"""
|
|
2691
|
+
Return the generating functions of the levels where all values
|
|
2692
|
+
can be determined.
|
|
2693
|
+
|
|
2694
|
+
TESTS::
|
|
2695
|
+
|
|
2696
|
+
sage: n = 3; l = lambda i: [pi for pi in Permutations(n) if pi(1) == i]
|
|
2697
|
+
sage: data = [([pi for pi in l(i)], [pi(1) for pi in l(i)]) for i in range(1,n+1)]
|
|
2698
|
+
sage: data.append((Permutation([1,2]), 1))
|
|
2699
|
+
sage: q = findstat(data, depth=0); q # optional -- internet
|
|
2700
|
+
0: St000054 (quality [100, 100])
|
|
2701
|
+
sage: q.first_terms() # optional -- internet
|
|
2702
|
+
{[1, 2]: 1}
|
|
2703
|
+
sage: q.generating_functions() # optional -- internet, indirect doctest
|
|
2704
|
+
{3: 2*q^3 + 2*q^2 + 2*q}
|
|
2705
|
+
"""
|
|
2706
|
+
return _distribution_from_data(self._known_terms,
|
|
2707
|
+
self.domain(),
|
|
2708
|
+
max_values,
|
|
2709
|
+
generating_functions=True)
|
|
2710
|
+
|
|
2711
|
+
def __repr__(self):
|
|
2712
|
+
"""
|
|
2713
|
+
Return a string representation of the query.
|
|
2714
|
+
|
|
2715
|
+
EXAMPLES::
|
|
2716
|
+
|
|
2717
|
+
sage: PM = PerfectMatchings
|
|
2718
|
+
sage: data = [(m, m.number_of_nestings()) for n in range(6) for m in PM(2*n)]
|
|
2719
|
+
sage: findstat(data, depth=1) # optional -- internet
|
|
2720
|
+
0: St000042oMp00116 (quality [100, 100])
|
|
2721
|
+
1: St000041 (quality [20, 100])
|
|
2722
|
+
...
|
|
2723
|
+
"""
|
|
2724
|
+
if self._result:
|
|
2725
|
+
return repr(self._result)
|
|
2726
|
+
return "%s: %s" % (self.id_str(), self.name())
|
|
2727
|
+
|
|
2728
|
+
def __getitem__(self, i):
|
|
2729
|
+
"""
|
|
2730
|
+
Return the `t`-th result in the query.
|
|
2731
|
+
|
|
2732
|
+
EXAMPLES::
|
|
2733
|
+
|
|
2734
|
+
sage: PM = PerfectMatchings
|
|
2735
|
+
sage: data = [(m, m.number_of_nestings()) for n in range(6) for m in PM(2*n)]
|
|
2736
|
+
sage: r = findstat(data, depth=1) # optional -- internet
|
|
2737
|
+
sage: r[1] # optional -- internet
|
|
2738
|
+
St000041 (quality [20, 100])
|
|
2739
|
+
"""
|
|
2740
|
+
return self._result[i]
|
|
2741
|
+
|
|
2742
|
+
def __len__(self):
|
|
2743
|
+
"""
|
|
2744
|
+
Return the number of results in the query.
|
|
2745
|
+
|
|
2746
|
+
EXAMPLES::
|
|
2747
|
+
|
|
2748
|
+
sage: r = findstat(Permutations, lambda pi: pi.saliances()[0], depth=1) # optional -- internet
|
|
2749
|
+
sage: len(r) > 4 # optional -- internet
|
|
2750
|
+
True
|
|
2751
|
+
"""
|
|
2752
|
+
return len(self._result)
|
|
2753
|
+
|
|
2754
|
+
|
|
2755
|
+
class FindStatCompoundStatistic(Element, FindStatCombinatorialStatistic):
|
|
2756
|
+
def __init__(self, id, domain=None, check=True):
|
|
2757
|
+
"""
|
|
2758
|
+
Initialize a compound statistic.
|
|
2759
|
+
|
|
2760
|
+
A compound statistic is a sequence of maps followed by a statistic.
|
|
2761
|
+
|
|
2762
|
+
INPUT:
|
|
2763
|
+
|
|
2764
|
+
- ``id`` -- a padded identifier
|
|
2765
|
+
|
|
2766
|
+
- ``domain`` -- (optional), the domain of the compound statistic
|
|
2767
|
+
|
|
2768
|
+
- ``check`` -- whether to check that domains and codomains fit
|
|
2769
|
+
|
|
2770
|
+
If the domain is given and ``check`` is ``False``, it is not
|
|
2771
|
+
fetched from FindStat.
|
|
2772
|
+
|
|
2773
|
+
TESTS::
|
|
2774
|
+
|
|
2775
|
+
sage: findstat("St000041oMp00127") # optional -- internet
|
|
2776
|
+
Traceback (most recent call last):
|
|
2777
|
+
...
|
|
2778
|
+
ValueError: the statistic St000041: The number of nestings of a perfect matching. cannot be composed with the map Mp00127
|
|
2779
|
+
"""
|
|
2780
|
+
if isinstance(id, (int, Integer)):
|
|
2781
|
+
id = FINDSTAT_STATISTIC_PADDED_IDENTIFIER % id
|
|
2782
|
+
elif isinstance(id, FindStatCombinatorialStatistic):
|
|
2783
|
+
id = id.id_str()
|
|
2784
|
+
if domain is not None:
|
|
2785
|
+
self._domain = FindStatCollection(domain)
|
|
2786
|
+
else:
|
|
2787
|
+
self._domain = None
|
|
2788
|
+
composition = id.partition(FINDSTAT_MAP_SEPARATOR)
|
|
2789
|
+
self._statistic = FindStatStatistic(composition[0])
|
|
2790
|
+
if composition[2]:
|
|
2791
|
+
self._maps = FindStatCompoundMap(composition[2], domain=self._domain)
|
|
2792
|
+
self._id = self._statistic.id_str() + FINDSTAT_MAP_SEPARATOR + self._maps.id_str()
|
|
2793
|
+
if self._domain is None:
|
|
2794
|
+
self._domain = self._maps.domain()
|
|
2795
|
+
else:
|
|
2796
|
+
if self._domain is None:
|
|
2797
|
+
self._domain = self._statistic.domain()
|
|
2798
|
+
self._maps = FindStatCompoundMap("", domain=self._domain, codomain=self._domain)
|
|
2799
|
+
self._id = self._statistic.id_str()
|
|
2800
|
+
if (check
|
|
2801
|
+
and self._maps.codomain() != self._statistic.domain()):
|
|
2802
|
+
raise ValueError("the statistic %s cannot be composed with the map %s" % (self._statistic, self._maps))
|
|
2803
|
+
|
|
2804
|
+
FindStatCombinatorialStatistic.__init__(self)
|
|
2805
|
+
Element.__init__(self, FindStatStatistics()) # this is not completely correct, but it works
|
|
2806
|
+
|
|
2807
|
+
def _fetch_first_terms_raw(self):
|
|
2808
|
+
r"""
|
|
2809
|
+
Return the first terms of the compound statistic, as ``(string,
|
|
2810
|
+
value)`` pairs, fetched from FindStat.
|
|
2811
|
+
|
|
2812
|
+
TESTS::
|
|
2813
|
+
|
|
2814
|
+
sage: findstat("St000042oMp00116")._first_terms_raw(4) # optional -- internet, indirect doctest
|
|
2815
|
+
[('[(1,2)]', 0),
|
|
2816
|
+
('[(1,2),(3,4)]', 0),
|
|
2817
|
+
('[(1,3),(2,4)]', 0),
|
|
2818
|
+
('[(1,4),(2,3)]', 1)]
|
|
2819
|
+
"""
|
|
2820
|
+
fields = "Values"
|
|
2821
|
+
url = FINDSTAT_API_STATISTICS + self.id_str() + "?fields=" + fields
|
|
2822
|
+
if len(self._maps):
|
|
2823
|
+
values = _get_json(url)["included"]["CompoundStatistics"][self.id_str()]["Values"]
|
|
2824
|
+
else:
|
|
2825
|
+
values = _get_json(url)["included"]["Statistics"][self.id_str()]["Values"]
|
|
2826
|
+
return [(sequence[0], sequence[-1]) for sequence in values]
|
|
2827
|
+
|
|
2828
|
+
def domain(self):
|
|
2829
|
+
"""
|
|
2830
|
+
Return the domain of the compound statistic.
|
|
2831
|
+
|
|
2832
|
+
EXAMPLES::
|
|
2833
|
+
|
|
2834
|
+
sage: findstat("St000042oMp00116").domain() # optional -- internet
|
|
2835
|
+
Cc0012: Perfect matchings
|
|
2836
|
+
"""
|
|
2837
|
+
return self._domain
|
|
2838
|
+
|
|
2839
|
+
def __call__(self, elt):
|
|
2840
|
+
"""
|
|
2841
|
+
Apply the compound statistic to the given element.
|
|
2842
|
+
|
|
2843
|
+
Note that this is only possible if execution of code is
|
|
2844
|
+
enabled, by setting the attribute ``_function`` of each map
|
|
2845
|
+
and the statistic to ``True``.
|
|
2846
|
+
|
|
2847
|
+
EXAMPLES::
|
|
2848
|
+
|
|
2849
|
+
sage: findstat("St000042oMp00116")(PerfectMatching([(1,2)])) # optional -- internet
|
|
2850
|
+
Traceback (most recent call last):
|
|
2851
|
+
...
|
|
2852
|
+
ValueError: execution of verified code provided by FindStat is not enabled for Mp00116: Kasraoui-Zeng
|
|
2853
|
+
"""
|
|
2854
|
+
return self.statistic()(self.compound_map()(elt))
|
|
2855
|
+
|
|
2856
|
+
def id_str(self):
|
|
2857
|
+
"""
|
|
2858
|
+
Return the padded identifier of the compound statistic.
|
|
2859
|
+
|
|
2860
|
+
EXAMPLES::
|
|
2861
|
+
|
|
2862
|
+
sage: findstat("St000042oMp00116").id_str() # optional -- internet
|
|
2863
|
+
'St000042oMp00116'
|
|
2864
|
+
"""
|
|
2865
|
+
return self._id
|
|
2866
|
+
|
|
2867
|
+
def _repr_(self):
|
|
2868
|
+
"""
|
|
2869
|
+
Return a string representation of the compound statistic.
|
|
2870
|
+
|
|
2871
|
+
EXAMPLES::
|
|
2872
|
+
|
|
2873
|
+
sage: findstat("St000042oMp00116") # optional -- internet
|
|
2874
|
+
St000042oMp00116
|
|
2875
|
+
"""
|
|
2876
|
+
return self.id_str()
|
|
2877
|
+
|
|
2878
|
+
def statistic(self):
|
|
2879
|
+
"""
|
|
2880
|
+
Return the statistic of the compound statistic.
|
|
2881
|
+
|
|
2882
|
+
EXAMPLES::
|
|
2883
|
+
|
|
2884
|
+
sage: findstat("St000041oMp00116").statistic() # optional -- internet
|
|
2885
|
+
St000041: The number of nestings of a perfect matching.
|
|
2886
|
+
"""
|
|
2887
|
+
return self._statistic
|
|
2888
|
+
|
|
2889
|
+
def compound_map(self):
|
|
2890
|
+
"""
|
|
2891
|
+
Return the compound map which is part of the compound statistic.
|
|
2892
|
+
|
|
2893
|
+
EXAMPLES::
|
|
2894
|
+
|
|
2895
|
+
sage: findstat("St000051oMp00061oMp00069").compound_map() # optional -- internet
|
|
2896
|
+
Mp00061oMp00069
|
|
2897
|
+
"""
|
|
2898
|
+
return self._maps
|
|
2899
|
+
|
|
2900
|
+
def browse(self):
|
|
2901
|
+
r"""
|
|
2902
|
+
Open the FindStat web page of the compound statistic in a browser.
|
|
2903
|
+
|
|
2904
|
+
EXAMPLES::
|
|
2905
|
+
|
|
2906
|
+
sage: from sage.databases.findstat import FindStatCompoundStatistic
|
|
2907
|
+
sage: FindStatCompoundStatistic("St000042oMp00116").browse() # optional -- webbrowser
|
|
2908
|
+
"""
|
|
2909
|
+
webbrowser.open(FINDSTAT_URL_STATISTICS + self.id_str())
|
|
2910
|
+
|
|
2911
|
+
def info(self):
|
|
2912
|
+
"""
|
|
2913
|
+
Print a detailed description of the compound statistic.
|
|
2914
|
+
|
|
2915
|
+
EXAMPLES::
|
|
2916
|
+
|
|
2917
|
+
sage: findstat("St000042oMp00116").info() # optional -- internet
|
|
2918
|
+
Mp00116: Kasraoui-Zeng: Perfect matchings -> Perfect matchings
|
|
2919
|
+
St000042: The number of crossings of a perfect matching.
|
|
2920
|
+
"""
|
|
2921
|
+
if len(self.compound_map()):
|
|
2922
|
+
self.compound_map().info()
|
|
2923
|
+
self.statistic().info()
|
|
2924
|
+
|
|
2925
|
+
|
|
2926
|
+
class FindStatMatchingStatistic(FindStatCompoundStatistic):
|
|
2927
|
+
def __init__(self, matching_statistic, offset, quality, domain=None):
|
|
2928
|
+
"""
|
|
2929
|
+
Initialize a FindStat statistic match.
|
|
2930
|
+
|
|
2931
|
+
INPUT:
|
|
2932
|
+
|
|
2933
|
+
- ``matching_statistic`` -- a compound statistic identifier
|
|
2934
|
+
|
|
2935
|
+
- ``offset`` -- the offset of the values, as provided by FindStat
|
|
2936
|
+
|
|
2937
|
+
- ``quality`` -- the quality of the match, as provided by FindStat
|
|
2938
|
+
|
|
2939
|
+
- ``domain`` -- (optional) the domain of the compound statistic
|
|
2940
|
+
|
|
2941
|
+
EXAMPLES::
|
|
2942
|
+
|
|
2943
|
+
sage: from sage.databases.findstat import FindStatMatchingStatistic
|
|
2944
|
+
sage: FindStatMatchingStatistic("St000042oMp00116", 1, [17, 83]) # optional -- internet
|
|
2945
|
+
St000042oMp00116 with offset 1 (quality [17, 83])
|
|
2946
|
+
"""
|
|
2947
|
+
self._quality = quality
|
|
2948
|
+
self._offset = offset
|
|
2949
|
+
# we can trust that matches have fitting domain / codomain sequence
|
|
2950
|
+
FindStatCompoundStatistic.__init__(self, matching_statistic, domain=domain, check=False)
|
|
2951
|
+
|
|
2952
|
+
def _repr_(self):
|
|
2953
|
+
"""
|
|
2954
|
+
Return a string representation of the match.
|
|
2955
|
+
|
|
2956
|
+
EXAMPLES::
|
|
2957
|
+
|
|
2958
|
+
sage: from sage.databases.findstat import FindStatMatchingStatistic
|
|
2959
|
+
sage: FindStatMatchingStatistic("St000042oMp00116", 1, [17, 83]) # optional -- internet
|
|
2960
|
+
St000042oMp00116 with offset 1 (quality [17, 83])
|
|
2961
|
+
"""
|
|
2962
|
+
if self._offset:
|
|
2963
|
+
return "%s with offset %s (quality %s)" % (self.id_str(), self._offset, self._quality)
|
|
2964
|
+
return "%s (quality %s)" % (self.id_str(), self.quality())
|
|
2965
|
+
|
|
2966
|
+
def offset(self):
|
|
2967
|
+
"""
|
|
2968
|
+
Return the offset which has to be added to each value of the
|
|
2969
|
+
compound statistic to obtain the desired value.
|
|
2970
|
+
|
|
2971
|
+
EXAMPLES::
|
|
2972
|
+
|
|
2973
|
+
sage: from sage.databases.findstat import FindStatMatchingStatistic
|
|
2974
|
+
sage: r = FindStatMatchingStatistic("St000042oMp00116", 1, [17, 83]) # optional -- internet
|
|
2975
|
+
sage: r.offset() # optional -- internet
|
|
2976
|
+
1
|
|
2977
|
+
"""
|
|
2978
|
+
return self._offset
|
|
2979
|
+
|
|
2980
|
+
def quality(self):
|
|
2981
|
+
"""
|
|
2982
|
+
Return the quality of the match, as provided by FindStat.
|
|
2983
|
+
|
|
2984
|
+
The quality of a statistic match is a pair of percentages
|
|
2985
|
+
`(q_a, q_d)`, where `q_a` is the percentage of ``(object,
|
|
2986
|
+
value)`` pairs that are in the database among those which
|
|
2987
|
+
were sent to FindStat, and `q_d` is the percentage of
|
|
2988
|
+
``(object, value)`` pairs with distinct values in the
|
|
2989
|
+
database among those which were sent to FindStat.
|
|
2990
|
+
|
|
2991
|
+
EXAMPLES::
|
|
2992
|
+
|
|
2993
|
+
sage: from sage.databases.findstat import FindStatMatchingStatistic
|
|
2994
|
+
sage: r = FindStatMatchingStatistic("St000042oMp00116", 1, [17, 83]) # optional -- internet
|
|
2995
|
+
sage: r.quality() # optional -- internet
|
|
2996
|
+
[17, 83]
|
|
2997
|
+
"""
|
|
2998
|
+
return self._quality[:]
|
|
2999
|
+
|
|
3000
|
+
def info(self):
|
|
3001
|
+
"""
|
|
3002
|
+
Print a detailed explanation of the match.
|
|
3003
|
+
|
|
3004
|
+
EXAMPLES::
|
|
3005
|
+
|
|
3006
|
+
sage: from sage.databases.findstat import FindStatMatchingStatistic
|
|
3007
|
+
sage: r = FindStatMatchingStatistic("St000042oMp00116", 1, [17, 83]) # optional -- internet
|
|
3008
|
+
sage: r.info() # optional -- internet
|
|
3009
|
+
after adding 1 to every value
|
|
3010
|
+
and applying
|
|
3011
|
+
Mp00116: Kasraoui-Zeng: Perfect matchings -> Perfect matchings
|
|
3012
|
+
to the objects (see `.compound_map()` for details)
|
|
3013
|
+
<BLANKLINE>
|
|
3014
|
+
your input matches
|
|
3015
|
+
St000042: The number of crossings of a perfect matching.
|
|
3016
|
+
<BLANKLINE>
|
|
3017
|
+
among the values you sent, 17 percent are actually in the database,
|
|
3018
|
+
among the distinct values you sent, 83 percent are actually in the database
|
|
3019
|
+
|
|
3020
|
+
sage: r = FindStatMatchingStatistic("St000042", 1, [17, 83]) # optional -- internet
|
|
3021
|
+
sage: r.info() # optional -- internet
|
|
3022
|
+
after adding 1 to every value
|
|
3023
|
+
<BLANKLINE>
|
|
3024
|
+
your input matches
|
|
3025
|
+
St000042: The number of crossings of a perfect matching.
|
|
3026
|
+
<BLANKLINE>
|
|
3027
|
+
among the values you sent, 17 percent are actually in the database,
|
|
3028
|
+
among the distinct values you sent, 83 percent are actually in the database
|
|
3029
|
+
"""
|
|
3030
|
+
if self.offset() < 0:
|
|
3031
|
+
print("after subtracting %s from every value" % (-self.offset()))
|
|
3032
|
+
if self.offset() > 0:
|
|
3033
|
+
print("after adding %s to every value" % self.offset())
|
|
3034
|
+
if len(self.compound_map()):
|
|
3035
|
+
if self.offset():
|
|
3036
|
+
print("and applying")
|
|
3037
|
+
else:
|
|
3038
|
+
print("after applying")
|
|
3039
|
+
self.compound_map().info()
|
|
3040
|
+
print("to the objects (see `.compound_map()` for details)")
|
|
3041
|
+
print()
|
|
3042
|
+
print("your input matches")
|
|
3043
|
+
self.statistic().info()
|
|
3044
|
+
print()
|
|
3045
|
+
print("among the values you sent, %s percent are actually in the database," % self.quality()[0])
|
|
3046
|
+
print("among the distinct values you sent, %s percent are actually in the database" % self.quality()[1])
|
|
3047
|
+
|
|
3048
|
+
######################################################################
|
|
3049
|
+
# maps
|
|
3050
|
+
######################################################################
|
|
3051
|
+
|
|
3052
|
+
|
|
3053
|
+
class FindStatCombinatorialMap(SageObject):
|
|
3054
|
+
"""
|
|
3055
|
+
A class serving as common ancestor of :class:`FindStatStatistic`
|
|
3056
|
+
and :class:`FindStatCompoundStatistic`.
|
|
3057
|
+
"""
|
|
3058
|
+
pass
|
|
3059
|
+
|
|
3060
|
+
|
|
3061
|
+
class FindStatMap(Element,
|
|
3062
|
+
FindStatFunction,
|
|
3063
|
+
FindStatCombinatorialMap,
|
|
3064
|
+
metaclass=InheritComparisonClasscallMetaclass):
|
|
3065
|
+
r"""
|
|
3066
|
+
A FindStat map.
|
|
3067
|
+
|
|
3068
|
+
:class:`FindStatMap` is a class representing a combinatorial
|
|
3069
|
+
map available in the FindStat database.
|
|
3070
|
+
|
|
3071
|
+
This class provides methods to inspect various properties of
|
|
3072
|
+
these maps, in particular :meth:`code`.
|
|
3073
|
+
|
|
3074
|
+
EXAMPLES::
|
|
3075
|
+
|
|
3076
|
+
sage: from sage.databases.findstat import FindStatMap
|
|
3077
|
+
sage: FindStatMap(116) # optional -- internet
|
|
3078
|
+
Mp00116: Kasraoui-Zeng
|
|
3079
|
+
|
|
3080
|
+
.. SEEALSO::
|
|
3081
|
+
|
|
3082
|
+
:class:`FindStatMaps`
|
|
3083
|
+
"""
|
|
3084
|
+
@staticmethod
|
|
3085
|
+
def __classcall_private__(cls, entry):
|
|
3086
|
+
"""
|
|
3087
|
+
Retrieve a map from the database.
|
|
3088
|
+
|
|
3089
|
+
TESTS::
|
|
3090
|
+
|
|
3091
|
+
sage: from sage.databases.findstat import FindStatMap
|
|
3092
|
+
sage: FindStatMap("abcdefgh") # optional -- internet
|
|
3093
|
+
Traceback (most recent call last):
|
|
3094
|
+
...
|
|
3095
|
+
ValueError: the value 'abcdefgh' is not a valid FindStat map identifier
|
|
3096
|
+
"""
|
|
3097
|
+
return FindStatMaps()(entry)
|
|
3098
|
+
|
|
3099
|
+
def __init__(self, parent, id):
|
|
3100
|
+
"""
|
|
3101
|
+
Initialize the map.
|
|
3102
|
+
|
|
3103
|
+
This should only be called in
|
|
3104
|
+
:meth:`FindStatMaps()._element_constructor_` via
|
|
3105
|
+
:meth:`FindStatMaps().element_class`.
|
|
3106
|
+
|
|
3107
|
+
INPUT:
|
|
3108
|
+
|
|
3109
|
+
- ``parent`` -- :class:`FindStatMaps`
|
|
3110
|
+
|
|
3111
|
+
- ``id`` -- the (padded) FindStat identifier of the statistic
|
|
3112
|
+
|
|
3113
|
+
TESTS::
|
|
3114
|
+
|
|
3115
|
+
sage: from sage.databases.findstat import FindStatMap
|
|
3116
|
+
sage: FindStatMap(116).parent() # optional -- internet
|
|
3117
|
+
Set of combinatorial maps used by FindStat
|
|
3118
|
+
"""
|
|
3119
|
+
FindStatFunction.__init__(self, id)
|
|
3120
|
+
Element.__init__(self, parent)
|
|
3121
|
+
|
|
3122
|
+
def __reduce__(self):
|
|
3123
|
+
"""
|
|
3124
|
+
Return a function and its arguments needed to create the map.
|
|
3125
|
+
|
|
3126
|
+
TESTS::
|
|
3127
|
+
|
|
3128
|
+
sage: from sage.databases.findstat import FindStatMap
|
|
3129
|
+
sage: c = FindStatMap(116) # optional -- internet
|
|
3130
|
+
sage: loads(dumps(c)) == c # optional -- internet
|
|
3131
|
+
True
|
|
3132
|
+
"""
|
|
3133
|
+
return (FindStatMap, (self.id(),))
|
|
3134
|
+
|
|
3135
|
+
def _richcmp_(self, other, op):
|
|
3136
|
+
"""
|
|
3137
|
+
Compare two maps by identifier.
|
|
3138
|
+
|
|
3139
|
+
TESTS::
|
|
3140
|
+
|
|
3141
|
+
sage: findmap(61) != findmap(62) # optional -- internet
|
|
3142
|
+
True
|
|
3143
|
+
sage: findmap(61) == findstat(61) # optional -- internet
|
|
3144
|
+
False
|
|
3145
|
+
"""
|
|
3146
|
+
return richcmp(self.id(), other.id(), op)
|
|
3147
|
+
|
|
3148
|
+
def _fetch_data(self):
|
|
3149
|
+
r"""
|
|
3150
|
+
Return a dictionary containing the data of the map, fetched from
|
|
3151
|
+
FindStat.
|
|
3152
|
+
|
|
3153
|
+
TESTS::
|
|
3154
|
+
|
|
3155
|
+
sage: findmap(64)._data() # optional -- internet, indirect doctest
|
|
3156
|
+
{'Bibliography': {},
|
|
3157
|
+
'Codomain': 'Cc0001',
|
|
3158
|
+
'Description': 'Sends a permutation to its reverse.\r\n\r\nThe reverse of a permutation $\\sigma$ of length $n$ is given by $\\tau$ with $\\tau(i) = \\sigma(n+1-i)$.',
|
|
3159
|
+
'Domain': 'Cc0001',
|
|
3160
|
+
'Name': 'reverse',
|
|
3161
|
+
'Properties': 'bijective, graded, involutive',
|
|
3162
|
+
'References': '',
|
|
3163
|
+
'SageCode': 'def mapping(sigma):\r\n return sigma.reverse()'}
|
|
3164
|
+
"""
|
|
3165
|
+
fields = "Bibliography,Codomain,Description,Domain,Name,Properties,References,SageCode"
|
|
3166
|
+
fields_Bibliography = "Author,Title"
|
|
3167
|
+
url = (FINDSTAT_API_MAPS + self.id_str()
|
|
3168
|
+
+ "?fields=" + fields
|
|
3169
|
+
+ "&fields[Bibliography]=" + fields_Bibliography)
|
|
3170
|
+
verbose("fetching map data %s" % url, caller_name='FindStatMap')
|
|
3171
|
+
included = _get_json(url)["included"]
|
|
3172
|
+
# slightly simplify the representation
|
|
3173
|
+
data = included["Maps"][self.id_str()]
|
|
3174
|
+
# we replace the list of identifiers in Bibliography with the dictionary
|
|
3175
|
+
data["Bibliography"] = included["References"]
|
|
3176
|
+
return data
|
|
3177
|
+
|
|
3178
|
+
def browse(self):
|
|
3179
|
+
r"""
|
|
3180
|
+
Open the FindStat web page of the map in a browser.
|
|
3181
|
+
|
|
3182
|
+
EXAMPLES::
|
|
3183
|
+
|
|
3184
|
+
sage: from sage.databases.findstat import FindStatMap
|
|
3185
|
+
sage: FindStatMap(116).browse() # optional -- webbrowser
|
|
3186
|
+
"""
|
|
3187
|
+
if self.id() == 0:
|
|
3188
|
+
self.submit()
|
|
3189
|
+
else:
|
|
3190
|
+
webbrowser.open(FINDSTAT_URL_MAPS + self.id_str())
|
|
3191
|
+
|
|
3192
|
+
def submit(self):
|
|
3193
|
+
r"""
|
|
3194
|
+
Open the FindStat web page for editing the map in a browser.
|
|
3195
|
+
|
|
3196
|
+
TESTS::
|
|
3197
|
+
|
|
3198
|
+
sage: s = findmap(62) # optional -- internet
|
|
3199
|
+
sage: s.set_name(u"Möbius") # optional -- internet
|
|
3200
|
+
sage: s.submit() # optional -- webbrowser
|
|
3201
|
+
sage: s.reset() # optional -- internet
|
|
3202
|
+
"""
|
|
3203
|
+
args = dict()
|
|
3204
|
+
args["OriginalMap"] = self.id_str()
|
|
3205
|
+
args["Domain"] = self.domain().id_str()
|
|
3206
|
+
args["Codomain"] = self.codomain().id_str()
|
|
3207
|
+
args["Name"] = self.name()
|
|
3208
|
+
args["Description"] = self.description()
|
|
3209
|
+
args["References"] = self.references_raw()
|
|
3210
|
+
args["Properties"] = self.properties_raw()
|
|
3211
|
+
args["SageCode"] = self.sage_code()
|
|
3212
|
+
args["CurrentAuthor"] = FindStat().user_name()
|
|
3213
|
+
args["CurrentEmail"] = FindStat().user_email()
|
|
3214
|
+
|
|
3215
|
+
if not self.id():
|
|
3216
|
+
url = FINDSTAT_NEWMAP_FORM_HEADER % FINDSTAT_URL_NEW_MAP
|
|
3217
|
+
else:
|
|
3218
|
+
url = FINDSTAT_NEWMAP_FORM_HEADER % (FINDSTAT_URL_EDIT_MAP + self.id_str())
|
|
3219
|
+
_submit(args, url)
|
|
3220
|
+
|
|
3221
|
+
# editing and submitting is really the same thing
|
|
3222
|
+
edit = submit
|
|
3223
|
+
|
|
3224
|
+
def __hash__(self):
|
|
3225
|
+
"""
|
|
3226
|
+
Return a hash value for the map.
|
|
3227
|
+
|
|
3228
|
+
EXAMPLES::
|
|
3229
|
+
|
|
3230
|
+
sage: from sage.databases.findstat import FindStatMaps
|
|
3231
|
+
sage: sorted(list(FindStatMaps(domain=1, codomain=10))) # optional -- internet, indirect doctest
|
|
3232
|
+
[Mp00061: to increasing tree, Mp00072: binary search tree: left to right]
|
|
3233
|
+
"""
|
|
3234
|
+
return self.id()
|
|
3235
|
+
|
|
3236
|
+
def codomain(self):
|
|
3237
|
+
r"""
|
|
3238
|
+
Return the FindStat collection which is the codomain of the map.
|
|
3239
|
+
|
|
3240
|
+
OUTPUT: the codomain of the map as a :class:`FindStatCollection`
|
|
3241
|
+
|
|
3242
|
+
EXAMPLES::
|
|
3243
|
+
|
|
3244
|
+
sage: from sage.databases.findstat import FindStatMap # optional -- internet
|
|
3245
|
+
sage: FindStatMap(27).codomain() # optional -- internet
|
|
3246
|
+
Cc0002: Integer partitions
|
|
3247
|
+
"""
|
|
3248
|
+
return FindStatCollection(self._data()["Codomain"])
|
|
3249
|
+
|
|
3250
|
+
def properties_raw(self):
|
|
3251
|
+
r"""
|
|
3252
|
+
Return the properties of the map.
|
|
3253
|
+
|
|
3254
|
+
OUTPUT: the properties as a string
|
|
3255
|
+
|
|
3256
|
+
EXAMPLES::
|
|
3257
|
+
|
|
3258
|
+
sage: from sage.databases.findstat import FindStatMap # optional -- internet
|
|
3259
|
+
sage: FindStatMap(61).properties_raw() # optional -- internet
|
|
3260
|
+
'surjective, graded'
|
|
3261
|
+
"""
|
|
3262
|
+
return self._data()["Properties"]
|
|
3263
|
+
|
|
3264
|
+
def set_properties_raw(self, value):
|
|
3265
|
+
r"""
|
|
3266
|
+
Set the properties of the map.
|
|
3267
|
+
|
|
3268
|
+
EXAMPLES::
|
|
3269
|
+
|
|
3270
|
+
sage: # optional - internet
|
|
3271
|
+
sage: from sage.databases.findstat import FindStatMap
|
|
3272
|
+
sage: FindStatMap(61).set_properties_raw('surjective')
|
|
3273
|
+
sage: FindStatMap(61).properties_raw()
|
|
3274
|
+
'surjective'
|
|
3275
|
+
sage: FindStatMap(61)
|
|
3276
|
+
Mp00061(modified): to increasing tree
|
|
3277
|
+
sage: FindStatMap(61).reset()
|
|
3278
|
+
sage: FindStatMap(61)
|
|
3279
|
+
Mp00061: to increasing tree
|
|
3280
|
+
"""
|
|
3281
|
+
if value != self.properties_raw():
|
|
3282
|
+
self._modified = True
|
|
3283
|
+
self._data_cache["Properties"] = value
|
|
3284
|
+
|
|
3285
|
+
def set_name(self, value):
|
|
3286
|
+
r"""
|
|
3287
|
+
Set the name of the map.
|
|
3288
|
+
|
|
3289
|
+
INPUT:
|
|
3290
|
+
|
|
3291
|
+
- ``value`` -- string; the new name of the map
|
|
3292
|
+
|
|
3293
|
+
This information is used when submitting the map with
|
|
3294
|
+
:meth:`submit`.
|
|
3295
|
+
|
|
3296
|
+
TESTS::
|
|
3297
|
+
|
|
3298
|
+
sage: s = findmap(62) # optional -- internet
|
|
3299
|
+
sage: s.set_name(u"Möbius"); s # optional -- internet
|
|
3300
|
+
Mp00062(modified): Möbius
|
|
3301
|
+
sage: s.reset(); s # optional -- internet
|
|
3302
|
+
Mp00062: Lehmer-code to major-code bijection
|
|
3303
|
+
"""
|
|
3304
|
+
if value != self.name():
|
|
3305
|
+
self._modified = True
|
|
3306
|
+
self._data_cache["Name"] = value
|
|
3307
|
+
|
|
3308
|
+
def info(self):
|
|
3309
|
+
"""
|
|
3310
|
+
Print a detailed description of the map.
|
|
3311
|
+
|
|
3312
|
+
EXAMPLES::
|
|
3313
|
+
|
|
3314
|
+
sage: findmap("Mp00116").info() # optional -- internet
|
|
3315
|
+
Mp00116: Kasraoui-Zeng: Perfect matchings -> Perfect matchings
|
|
3316
|
+
"""
|
|
3317
|
+
print(" %s: %s -> %s" % (self,
|
|
3318
|
+
self.domain().name("plural"),
|
|
3319
|
+
self.codomain().name("plural")))
|
|
3320
|
+
|
|
3321
|
+
|
|
3322
|
+
_all_maps = {}
|
|
3323
|
+
|
|
3324
|
+
|
|
3325
|
+
class FindStatMaps(UniqueRepresentation, Parent):
|
|
3326
|
+
r"""
|
|
3327
|
+
The class of FindStat maps.
|
|
3328
|
+
|
|
3329
|
+
The elements of this class are combinatorial maps currently in
|
|
3330
|
+
FindStat.
|
|
3331
|
+
|
|
3332
|
+
EXAMPLES:
|
|
3333
|
+
|
|
3334
|
+
We can print a sample map for each domain and codomain::
|
|
3335
|
+
|
|
3336
|
+
sage: from sage.databases.findstat import FindStatCollections, FindStatMaps
|
|
3337
|
+
sage: ccs = sorted(FindStatCollections())[:3] # optional -- internet
|
|
3338
|
+
sage: for cc_dom in ccs: # optional -- internet
|
|
3339
|
+
....: for cc_codom in ccs:
|
|
3340
|
+
....: print(cc_dom.name(style='plural') + " -> " + cc_codom.name(style='plural'))
|
|
3341
|
+
....: try:
|
|
3342
|
+
....: print(" " + next(iter(FindStatMaps(cc_dom, cc_codom))).name())
|
|
3343
|
+
....: except StopIteration:
|
|
3344
|
+
....: pass
|
|
3345
|
+
Permutations -> Permutations
|
|
3346
|
+
Lehmer-code to major-code bijection
|
|
3347
|
+
Permutations -> Integer partitions
|
|
3348
|
+
Robinson-Schensted tableau shape
|
|
3349
|
+
Permutations -> Dyck paths
|
|
3350
|
+
left-to-right-maxima to Dyck path
|
|
3351
|
+
Integer partitions -> Permutations
|
|
3352
|
+
Integer partitions -> Integer partitions
|
|
3353
|
+
conjugate
|
|
3354
|
+
Integer partitions -> Dyck paths
|
|
3355
|
+
to Dyck path
|
|
3356
|
+
Dyck paths -> Permutations
|
|
3357
|
+
to non-crossing permutation
|
|
3358
|
+
Dyck paths -> Integer partitions
|
|
3359
|
+
to partition
|
|
3360
|
+
Dyck paths -> Dyck paths
|
|
3361
|
+
reverse
|
|
3362
|
+
"""
|
|
3363
|
+
def __init__(self, domain=None, codomain=None):
|
|
3364
|
+
"""
|
|
3365
|
+
TESTS::
|
|
3366
|
+
|
|
3367
|
+
sage: from sage.databases.findstat import FindStatMaps
|
|
3368
|
+
sage: M = FindStatMaps() # optional -- internet
|
|
3369
|
+
sage: TestSuite(M).run() # optional -- internet
|
|
3370
|
+
"""
|
|
3371
|
+
if domain is None:
|
|
3372
|
+
self._domain = None
|
|
3373
|
+
else:
|
|
3374
|
+
self._domain = FindStatCollection(domain)
|
|
3375
|
+
if codomain is None:
|
|
3376
|
+
self._codomain = None
|
|
3377
|
+
else:
|
|
3378
|
+
self._codomain = FindStatCollection(codomain)
|
|
3379
|
+
self._identifiers = None
|
|
3380
|
+
Parent.__init__(self, category=Sets())
|
|
3381
|
+
|
|
3382
|
+
def _element_constructor_(self, id):
|
|
3383
|
+
"""
|
|
3384
|
+
Initialize a FindStat map.
|
|
3385
|
+
|
|
3386
|
+
INPUT:
|
|
3387
|
+
|
|
3388
|
+
- ``id`` -- string containing the FindStat identifier of
|
|
3389
|
+
the map, or an integer giving its id
|
|
3390
|
+
|
|
3391
|
+
EXAMPLES::
|
|
3392
|
+
|
|
3393
|
+
sage: from sage.databases.findstat import FindStatMap
|
|
3394
|
+
sage: FindStatMap(61) # optional -- internet, indirect doctest
|
|
3395
|
+
Mp00061: to increasing tree
|
|
3396
|
+
"""
|
|
3397
|
+
if isinstance(id, self.Element):
|
|
3398
|
+
return id
|
|
3399
|
+
if isinstance(id, (int, Integer)):
|
|
3400
|
+
id = FINDSTAT_MAP_PADDED_IDENTIFIER % id
|
|
3401
|
+
elif isinstance(id, FindStatCombinatorialMap):
|
|
3402
|
+
id = id.id_str()
|
|
3403
|
+
if not isinstance(id, str):
|
|
3404
|
+
raise TypeError("the value %s is neither an integer nor a string" % id)
|
|
3405
|
+
else:
|
|
3406
|
+
id = id.strip()
|
|
3407
|
+
if FINDSTAT_MAP_SEPARATOR in id:
|
|
3408
|
+
return FindStatCompoundMap(id)
|
|
3409
|
+
if not re.match(FINDSTAT_MAP_REGEXP, id) or id == FINDSTAT_MAP_PADDED_IDENTIFIER % 0:
|
|
3410
|
+
raise ValueError("the value '%s' is not a valid FindStat map identifier" % id)
|
|
3411
|
+
if id not in _all_maps or _all_maps[id] is None:
|
|
3412
|
+
_all_maps[id] = self.element_class(self, id)
|
|
3413
|
+
|
|
3414
|
+
return _all_maps[id]
|
|
3415
|
+
|
|
3416
|
+
def _repr_(self):
|
|
3417
|
+
"""
|
|
3418
|
+
Return the representation of the set of FindStat maps.
|
|
3419
|
+
|
|
3420
|
+
EXAMPLES::
|
|
3421
|
+
|
|
3422
|
+
sage: from sage.databases.findstat import FindStatMaps
|
|
3423
|
+
sage: FindStatMaps() # optional -- internet
|
|
3424
|
+
Set of combinatorial maps used by FindStat
|
|
3425
|
+
"""
|
|
3426
|
+
if self._domain is None:
|
|
3427
|
+
text = []
|
|
3428
|
+
else:
|
|
3429
|
+
text = ["domain %s" % self._domain]
|
|
3430
|
+
if self._codomain is not None:
|
|
3431
|
+
text.append("codomain %s" % self._codomain)
|
|
3432
|
+
|
|
3433
|
+
if text:
|
|
3434
|
+
return "Set of combinatorial maps with " + " and ".join(text) + " used by FindStat"
|
|
3435
|
+
return "Set of combinatorial maps used by FindStat"
|
|
3436
|
+
|
|
3437
|
+
def __iter__(self):
|
|
3438
|
+
"""
|
|
3439
|
+
Return an iterator over all FindStat maps.
|
|
3440
|
+
|
|
3441
|
+
EXAMPLES::
|
|
3442
|
+
|
|
3443
|
+
sage: from sage.databases.findstat import FindStatMaps
|
|
3444
|
+
sage: next(iter(FindStatMaps(domain=1, codomain=10))) # optional -- internet
|
|
3445
|
+
Mp00061: to increasing tree
|
|
3446
|
+
"""
|
|
3447
|
+
if self._identifiers is None:
|
|
3448
|
+
query = []
|
|
3449
|
+
if self._domain is not None:
|
|
3450
|
+
query.append("Domain=%s" % self._domain.id_str())
|
|
3451
|
+
if self._codomain is not None:
|
|
3452
|
+
query.append("Codomain=%s" % self._codomain.id_str())
|
|
3453
|
+
if query:
|
|
3454
|
+
url = FINDSTAT_API_MAPS + "?" + "&".join(query)
|
|
3455
|
+
else:
|
|
3456
|
+
url = FINDSTAT_API_MAPS
|
|
3457
|
+
self._identifiers = _get_json(url)["data"]
|
|
3458
|
+
|
|
3459
|
+
for mp in self._identifiers:
|
|
3460
|
+
yield FindStatMap(mp)
|
|
3461
|
+
|
|
3462
|
+
def _an_element_(self):
|
|
3463
|
+
"""
|
|
3464
|
+
Return a FindStat map.
|
|
3465
|
+
|
|
3466
|
+
EXAMPLES::
|
|
3467
|
+
|
|
3468
|
+
sage: findmap(domain="Dyck paths", codomain='Posets').an_element() # optional -- internet
|
|
3469
|
+
Mp00232: parallelogram poset
|
|
3470
|
+
"""
|
|
3471
|
+
try:
|
|
3472
|
+
return next(iter(self))
|
|
3473
|
+
except StopIteration:
|
|
3474
|
+
from sage.categories.sets_cat import EmptySetError
|
|
3475
|
+
raise EmptySetError
|
|
3476
|
+
|
|
3477
|
+
Element = FindStatMap
|
|
3478
|
+
|
|
3479
|
+
|
|
3480
|
+
class FindStatMapQuery(FindStatMap):
|
|
3481
|
+
"""
|
|
3482
|
+
A class representing a query for FindStat (compound) maps.
|
|
3483
|
+
"""
|
|
3484
|
+
def __init__(self, data=None, values_of=None, distribution_of=None,
|
|
3485
|
+
domain=None, codomain=None, known_terms=None, function=None,
|
|
3486
|
+
depth=FINDSTAT_DEFAULT_DEPTH,
|
|
3487
|
+
debug=False):
|
|
3488
|
+
"""
|
|
3489
|
+
Initialize a query for FindStat (compound) maps.
|
|
3490
|
+
|
|
3491
|
+
INPUT:
|
|
3492
|
+
|
|
3493
|
+
- ``data`` -- (optional), a list of pairs ``(objects,
|
|
3494
|
+
values)``, where ``objects`` and ``values`` are all lists
|
|
3495
|
+
of the same length, the former are elements in the domain
|
|
3496
|
+
and the latter in the codomain
|
|
3497
|
+
|
|
3498
|
+
- ``known_terms`` -- (optional), a lazy list in the same format
|
|
3499
|
+
as ``data``, which agrees with ``data``, and may be used
|
|
3500
|
+
for submission
|
|
3501
|
+
|
|
3502
|
+
- ``values_of`` -- (optional), anything accepted by
|
|
3503
|
+
:class:`FindStatCompoundStatistic`
|
|
3504
|
+
|
|
3505
|
+
- ``distribution_of`` -- (optional), anything accepted by
|
|
3506
|
+
:class:`FindStatCompoundStatistic`
|
|
3507
|
+
|
|
3508
|
+
- ``domain``, ``codomain`` -- (optional), anything accepted by
|
|
3509
|
+
:class:`FindStatCollection`
|
|
3510
|
+
|
|
3511
|
+
- ``depth`` -- (optional), the number of maps to apply before
|
|
3512
|
+
applying the statistic
|
|
3513
|
+
|
|
3514
|
+
- ``function`` -- (optional), a callable producing the terms
|
|
3515
|
+
|
|
3516
|
+
Only one of ``data``, ``values_of`` and ``distribution_of``
|
|
3517
|
+
may be provided. The parameter ``domain`` must be provided
|
|
3518
|
+
if and only if ``data`` is provided, or ``values_of`` or
|
|
3519
|
+
``distribution_of`` are given as a function.
|
|
3520
|
+
|
|
3521
|
+
The parameter ``known_terms`` is only allowed, if ``data`` is
|
|
3522
|
+
provided. It defaults to ``data``.
|
|
3523
|
+
|
|
3524
|
+
EXAMPLES::
|
|
3525
|
+
|
|
3526
|
+
sage: from sage.databases.findstat import FindStatMapQuery
|
|
3527
|
+
sage: data = [[[pi], [pi.complement().increasing_tree_shape()]] for pi in Permutations(4)]
|
|
3528
|
+
sage: FindStatMapQuery(domain=1, codomain=10, data=data) # optional -- internet
|
|
3529
|
+
0: Mp00061oMp00069 (quality [100])
|
|
3530
|
+
"""
|
|
3531
|
+
self._first_terms = data
|
|
3532
|
+
if data is not None and known_terms is None:
|
|
3533
|
+
self._known_terms = data
|
|
3534
|
+
else:
|
|
3535
|
+
self._known_terms = known_terms
|
|
3536
|
+
self._values_of = None
|
|
3537
|
+
self._distribution_of = None
|
|
3538
|
+
self._depth = depth
|
|
3539
|
+
|
|
3540
|
+
if data is not None:
|
|
3541
|
+
assert all(param is None for param in [distribution_of, values_of])
|
|
3542
|
+
|
|
3543
|
+
domain = FindStatCollection(domain)
|
|
3544
|
+
codomain = FindStatCollection(codomain)
|
|
3545
|
+
query = {"Domain": domain.id_str(),
|
|
3546
|
+
"Codomain": codomain.id_str(),
|
|
3547
|
+
"Data": _data_to_str(self._first_terms, domain, codomain)}
|
|
3548
|
+
|
|
3549
|
+
elif distribution_of is not None:
|
|
3550
|
+
assert all(param is None for param in [data, known_terms, values_of])
|
|
3551
|
+
|
|
3552
|
+
self._distribution_of = FindStatCompoundMap(distribution_of)
|
|
3553
|
+
domain = self._distribution_of.domain()
|
|
3554
|
+
codomain = self._distribution_of.codomain()
|
|
3555
|
+
query = {"DistributionOf": self._distribution_of.id_str()}
|
|
3556
|
+
|
|
3557
|
+
elif values_of is not None:
|
|
3558
|
+
assert all(param is None for param in [data, known_terms, distribution_of])
|
|
3559
|
+
|
|
3560
|
+
self._values_of = FindStatCompoundMap(values_of)
|
|
3561
|
+
domain = self._values_of.domain()
|
|
3562
|
+
codomain = self._values_of.codomain()
|
|
3563
|
+
query = {"ValuesOf": self._values_of.id_str()}
|
|
3564
|
+
|
|
3565
|
+
else:
|
|
3566
|
+
raise ValueError("incompatible set of parameters: data: %s, distribution_of: %s, values_of: %s" % ((data, distribution_of, values_of)))
|
|
3567
|
+
|
|
3568
|
+
if depth is not None:
|
|
3569
|
+
query["Depth"] = depth
|
|
3570
|
+
|
|
3571
|
+
query["fields"] = "MatchingMap,Quality"
|
|
3572
|
+
if debug:
|
|
3573
|
+
print(query)
|
|
3574
|
+
verbose("querying FindStat %s" % query, caller_name='FindStatMapQuery')
|
|
3575
|
+
response = _post_json(FINDSTAT_API_MAPS, query)
|
|
3576
|
+
|
|
3577
|
+
if debug:
|
|
3578
|
+
print(response)
|
|
3579
|
+
if "data" not in response:
|
|
3580
|
+
raise ValueError(response["error"])
|
|
3581
|
+
|
|
3582
|
+
result = []
|
|
3583
|
+
for match in response["data"]:
|
|
3584
|
+
entry = response["included"]["MatchingMaps"][match]
|
|
3585
|
+
result.append(FindStatMatchingMap(entry["MatchingMap"],
|
|
3586
|
+
entry["Quality"]))
|
|
3587
|
+
self._result = FancyTuple(result)
|
|
3588
|
+
|
|
3589
|
+
FindStatFunction.__init__(self, FINDSTAT_MAP_PADDED_IDENTIFIER % 0,
|
|
3590
|
+
data={"Bibliography": {},
|
|
3591
|
+
"Code": _get_code_from_callable(function),
|
|
3592
|
+
"Description": "",
|
|
3593
|
+
"Domain": domain,
|
|
3594
|
+
"Codomain": codomain,
|
|
3595
|
+
"Name": "a new map from %s to %s" % (domain.name("plural"), codomain.name("plural")),
|
|
3596
|
+
"References": "",
|
|
3597
|
+
"Properties": "",
|
|
3598
|
+
"SageCode": ""},
|
|
3599
|
+
function=function)
|
|
3600
|
+
Element.__init__(self, FindStatMaps()) # this is not completely correct, but it works
|
|
3601
|
+
|
|
3602
|
+
def __repr__(self):
|
|
3603
|
+
"""
|
|
3604
|
+
Return a string representation of the query.
|
|
3605
|
+
|
|
3606
|
+
EXAMPLES::
|
|
3607
|
+
|
|
3608
|
+
sage: from sage.databases.findstat import FindStatMapQuery
|
|
3609
|
+
sage: data = [[[pi],[pi.complement().increasing_tree_shape()]] for pi in Permutations(4)]
|
|
3610
|
+
sage: FindStatMapQuery(domain=1, codomain=10, data=data) # optional -- internet
|
|
3611
|
+
0: Mp00061oMp00069 (quality [100])
|
|
3612
|
+
"""
|
|
3613
|
+
if self._result:
|
|
3614
|
+
return repr(self._result)
|
|
3615
|
+
return "%s: %s" % (self.id_str(), self.name())
|
|
3616
|
+
|
|
3617
|
+
def __getitem__(self, i):
|
|
3618
|
+
"""
|
|
3619
|
+
Return the `i`-th result in the query.
|
|
3620
|
+
|
|
3621
|
+
EXAMPLES::
|
|
3622
|
+
|
|
3623
|
+
sage: from sage.databases.findstat import FindStatMapQuery
|
|
3624
|
+
sage: data = [[[pi],[pi.complement().increasing_tree_shape()]] for pi in Permutations(4)]
|
|
3625
|
+
sage: r = FindStatMapQuery(domain=1, codomain=10, data=data) # optional -- internet
|
|
3626
|
+
sage: r[0] # optional -- internet
|
|
3627
|
+
Mp00061oMp00069 (quality [100])
|
|
3628
|
+
"""
|
|
3629
|
+
return self._result[i]
|
|
3630
|
+
|
|
3631
|
+
def __len__(self):
|
|
3632
|
+
"""
|
|
3633
|
+
Return the number of results in the query.
|
|
3634
|
+
|
|
3635
|
+
EXAMPLES::
|
|
3636
|
+
|
|
3637
|
+
sage: r = findmap("Permutations", lambda pi: pi.descents_composition()) # optional -- internet
|
|
3638
|
+
sage: len(r) > 0 # optional -- internet
|
|
3639
|
+
True
|
|
3640
|
+
"""
|
|
3641
|
+
return len(self._result)
|
|
3642
|
+
|
|
3643
|
+
|
|
3644
|
+
class FindStatCompoundMap(Element, FindStatCombinatorialMap):
|
|
3645
|
+
def __init__(self, id, domain=None, codomain=None, check=True):
|
|
3646
|
+
"""
|
|
3647
|
+
Initialize a compound statistic.
|
|
3648
|
+
|
|
3649
|
+
INPUT:
|
|
3650
|
+
|
|
3651
|
+
- ``id`` -- a padded identifier
|
|
3652
|
+
|
|
3653
|
+
- ``domain`` -- (optional), the domain of the compound map
|
|
3654
|
+
|
|
3655
|
+
- ``codomain`` -- (optional), the codomain of the compound map
|
|
3656
|
+
|
|
3657
|
+
- ``check`` -- whether to check that domains and codomains fit
|
|
3658
|
+
|
|
3659
|
+
If domain and codomain are given and ``check`` is ``False``,
|
|
3660
|
+
they are not fetched from FindStat.
|
|
3661
|
+
|
|
3662
|
+
If ``id`` is the empty string, ``domain`` must be provided,
|
|
3663
|
+
and the identity map on this collection is returned.
|
|
3664
|
+
|
|
3665
|
+
TESTS::
|
|
3666
|
+
|
|
3667
|
+
sage: findmap("Mp00146oMp00127").domain() # optional -- internet
|
|
3668
|
+
Cc0001: Permutations
|
|
3669
|
+
|
|
3670
|
+
sage: findmap("Mp00146oMp00127").codomain() # optional -- internet
|
|
3671
|
+
Cc0012: Perfect matchings
|
|
3672
|
+
|
|
3673
|
+
sage: findmap("Mp00127oMp00146") # optional -- internet
|
|
3674
|
+
Traceback (most recent call last):
|
|
3675
|
+
...
|
|
3676
|
+
ValueError: the sequence of maps [Mp00146: to tunnel matching, Mp00127: left-to-right-maxima to Dyck path] cannot be composed
|
|
3677
|
+
"""
|
|
3678
|
+
if isinstance(id, (int, Integer)):
|
|
3679
|
+
id = FINDSTAT_MAP_PADDED_IDENTIFIER % id
|
|
3680
|
+
elif isinstance(id, FindStatCombinatorialMap):
|
|
3681
|
+
id = id.id_str()
|
|
3682
|
+
if id == "":
|
|
3683
|
+
self._id = "id"
|
|
3684
|
+
self._maps = []
|
|
3685
|
+
self._domain = FindStatCollection(domain)
|
|
3686
|
+
self._codomain = self._domain
|
|
3687
|
+
else:
|
|
3688
|
+
self._maps = [FindStatMap(m) for m in id.split(FINDSTAT_MAP_SEPARATOR)][::-1]
|
|
3689
|
+
if (check
|
|
3690
|
+
and not all(self._maps[i].codomain() == self._maps[i+1].domain()
|
|
3691
|
+
for i in range(len(self._maps)-1))):
|
|
3692
|
+
raise ValueError("the sequence of maps %s cannot be composed" % self._maps)
|
|
3693
|
+
if domain is None:
|
|
3694
|
+
self._domain = self._maps[0].domain()
|
|
3695
|
+
else:
|
|
3696
|
+
self._domain = FindStatCollection(domain)
|
|
3697
|
+
if codomain is None:
|
|
3698
|
+
self._codomain = self._maps[-1].codomain()
|
|
3699
|
+
else:
|
|
3700
|
+
self._codomain = FindStatCollection(codomain)
|
|
3701
|
+
self._id = FINDSTAT_MAP_SEPARATOR.join(m.id_str() for m in reversed(self._maps))
|
|
3702
|
+
|
|
3703
|
+
Element.__init__(self, FindStatMaps()) # this is not completely correct, but it works
|
|
3704
|
+
|
|
3705
|
+
def domain(self):
|
|
3706
|
+
"""
|
|
3707
|
+
Return the domain of the compound map.
|
|
3708
|
+
|
|
3709
|
+
EXAMPLES::
|
|
3710
|
+
|
|
3711
|
+
sage: findmap("Mp00099oMp00127").domain() # optional -- internet
|
|
3712
|
+
Cc0001: Permutations
|
|
3713
|
+
"""
|
|
3714
|
+
return self._domain
|
|
3715
|
+
|
|
3716
|
+
def codomain(self):
|
|
3717
|
+
"""
|
|
3718
|
+
Return the codomain of the compound map.
|
|
3719
|
+
|
|
3720
|
+
EXAMPLES::
|
|
3721
|
+
|
|
3722
|
+
sage: findmap("Mp00099oMp00127").codomain() # optional -- internet
|
|
3723
|
+
Cc0005: Dyck paths
|
|
3724
|
+
"""
|
|
3725
|
+
return self._codomain
|
|
3726
|
+
|
|
3727
|
+
def __call__(self, elt):
|
|
3728
|
+
"""
|
|
3729
|
+
Apply the compound map to the given element.
|
|
3730
|
+
|
|
3731
|
+
Note that this is only possible if execution of code is
|
|
3732
|
+
enabled, by setting the attribute ``_function`` of each map
|
|
3733
|
+
to ``True``.
|
|
3734
|
+
|
|
3735
|
+
EXAMPLES::
|
|
3736
|
+
|
|
3737
|
+
sage: findmap("Mp00099oMp00127")(Permutation([1,3,2])) # optional -- internet
|
|
3738
|
+
Traceback (most recent call last):
|
|
3739
|
+
...
|
|
3740
|
+
ValueError: execution of verified code provided by FindStat is not enabled for Mp00127: left-to-right-maxima to Dyck path
|
|
3741
|
+
"""
|
|
3742
|
+
for m in self.maps():
|
|
3743
|
+
elt = m(elt)
|
|
3744
|
+
return elt
|
|
3745
|
+
|
|
3746
|
+
def __getitem__(self, i):
|
|
3747
|
+
"""
|
|
3748
|
+
Return the `i`-th map in the compound map.
|
|
3749
|
+
|
|
3750
|
+
EXAMPLES::
|
|
3751
|
+
|
|
3752
|
+
sage: findmap("Mp00099oMp00127")[1] # optional -- internet
|
|
3753
|
+
Mp00099: bounce path
|
|
3754
|
+
"""
|
|
3755
|
+
return self._maps[i]
|
|
3756
|
+
|
|
3757
|
+
def id_str(self):
|
|
3758
|
+
"""
|
|
3759
|
+
Return the padded identifier of the compound map.
|
|
3760
|
+
|
|
3761
|
+
EXAMPLES::
|
|
3762
|
+
|
|
3763
|
+
sage: findmap("Mp00099oMp00127").id_str() # optional -- internet
|
|
3764
|
+
'Mp00099oMp00127'
|
|
3765
|
+
"""
|
|
3766
|
+
return self._id
|
|
3767
|
+
|
|
3768
|
+
def _repr_(self):
|
|
3769
|
+
"""
|
|
3770
|
+
Return a string representation of the compound statistic.
|
|
3771
|
+
|
|
3772
|
+
EXAMPLES::
|
|
3773
|
+
|
|
3774
|
+
sage: findmap("Mp00099oMp00127") # optional -- internet
|
|
3775
|
+
Mp00099oMp00127
|
|
3776
|
+
"""
|
|
3777
|
+
return self.id_str()
|
|
3778
|
+
|
|
3779
|
+
def maps(self):
|
|
3780
|
+
"""
|
|
3781
|
+
Return the maps occurring in the compound map as a list.
|
|
3782
|
+
|
|
3783
|
+
EXAMPLES::
|
|
3784
|
+
|
|
3785
|
+
sage: findmap("Mp00099oMp00127").maps() # optional -- internet
|
|
3786
|
+
[Mp00127: left-to-right-maxima to Dyck path, Mp00099: bounce path]
|
|
3787
|
+
"""
|
|
3788
|
+
return self._maps
|
|
3789
|
+
|
|
3790
|
+
def __len__(self):
|
|
3791
|
+
"""
|
|
3792
|
+
Return the number of maps occurring in the compound map.
|
|
3793
|
+
|
|
3794
|
+
.. WARNING::
|
|
3795
|
+
|
|
3796
|
+
Do not confuse this with the number of results in a query.
|
|
3797
|
+
|
|
3798
|
+
EXAMPLES::
|
|
3799
|
+
|
|
3800
|
+
sage: len(findmap("Mp00099oMp00127")) # optional -- internet
|
|
3801
|
+
2
|
|
3802
|
+
"""
|
|
3803
|
+
return len(self._maps)
|
|
3804
|
+
|
|
3805
|
+
def browse(self):
|
|
3806
|
+
r"""
|
|
3807
|
+
Open the FindStat web page of the compound map in a browser.
|
|
3808
|
+
|
|
3809
|
+
EXAMPLES::
|
|
3810
|
+
|
|
3811
|
+
sage: findmap(62).browse() # optional -- webbrowser
|
|
3812
|
+
"""
|
|
3813
|
+
webbrowser.open(FINDSTAT_URL_MAPS + self.id_str())
|
|
3814
|
+
|
|
3815
|
+
def info(self):
|
|
3816
|
+
"""
|
|
3817
|
+
Print a detailed explanation of the compound map.
|
|
3818
|
+
|
|
3819
|
+
EXAMPLES::
|
|
3820
|
+
|
|
3821
|
+
sage: findmap("Mp00099oMp00127").info() # optional -- internet
|
|
3822
|
+
Mp00127: left-to-right-maxima to Dyck path: Permutations -> Dyck paths
|
|
3823
|
+
Mp00099: bounce path: Dyck paths -> Dyck paths
|
|
3824
|
+
"""
|
|
3825
|
+
for mp in self:
|
|
3826
|
+
mp.info()
|
|
3827
|
+
|
|
3828
|
+
|
|
3829
|
+
class FindStatMatchingMap(FindStatCompoundMap):
|
|
3830
|
+
def __init__(self, matching_map, quality, domain=None, codomain=None):
|
|
3831
|
+
"""
|
|
3832
|
+
Initialize a FindStat map match.
|
|
3833
|
+
|
|
3834
|
+
INPUT:
|
|
3835
|
+
|
|
3836
|
+
- ``matching_map`` -- a compound map identifier
|
|
3837
|
+
|
|
3838
|
+
- ``quality`` -- the quality of the match, as provided by FindStat
|
|
3839
|
+
|
|
3840
|
+
- ``domain`` -- (optional) the domain of the compound map
|
|
3841
|
+
|
|
3842
|
+
- ``codomain`` -- (optional), the codomain of the compound map
|
|
3843
|
+
|
|
3844
|
+
EXAMPLES::
|
|
3845
|
+
|
|
3846
|
+
sage: from sage.databases.findstat import FindStatMatchingMap
|
|
3847
|
+
sage: FindStatMatchingMap("Mp00099oMp00127", [83]) # optional -- internet
|
|
3848
|
+
Mp00099oMp00127 (quality [83])
|
|
3849
|
+
"""
|
|
3850
|
+
self._quality = quality
|
|
3851
|
+
# we can trust that matches have fitting domain / codomain sequence
|
|
3852
|
+
FindStatCompoundMap.__init__(self, matching_map, domain=domain, codomain=codomain, check=False)
|
|
3853
|
+
|
|
3854
|
+
def _repr_(self):
|
|
3855
|
+
"""
|
|
3856
|
+
Return a string representation of the match.
|
|
3857
|
+
|
|
3858
|
+
EXAMPLES::
|
|
3859
|
+
|
|
3860
|
+
sage: from sage.databases.findstat import FindStatMatchingMap
|
|
3861
|
+
sage: FindStatMatchingMap("Mp00099oMp00127", [83]) # optional -- internet
|
|
3862
|
+
Mp00099oMp00127 (quality [83])
|
|
3863
|
+
"""
|
|
3864
|
+
return "%s (quality %s)" % (self.id_str(), self.quality())
|
|
3865
|
+
|
|
3866
|
+
def quality(self):
|
|
3867
|
+
"""
|
|
3868
|
+
Return the quality of the match, as provided by FindStat.
|
|
3869
|
+
|
|
3870
|
+
EXAMPLES::
|
|
3871
|
+
|
|
3872
|
+
sage: from sage.databases.findstat import FindStatMatchingMap
|
|
3873
|
+
sage: FindStatMatchingMap("Mp00099oMp00127", [83]).quality() # optional -- internet
|
|
3874
|
+
[83]
|
|
3875
|
+
"""
|
|
3876
|
+
return self._quality[:]
|
|
3877
|
+
|
|
3878
|
+
def info(self):
|
|
3879
|
+
"""
|
|
3880
|
+
Print a detailed explanation of the match.
|
|
3881
|
+
|
|
3882
|
+
EXAMPLES::
|
|
3883
|
+
|
|
3884
|
+
sage: from sage.databases.findstat import FindStatMatchingMap
|
|
3885
|
+
sage: FindStatMatchingMap("Mp00099oMp00127", [83]).info() # optional -- internet
|
|
3886
|
+
your input matches
|
|
3887
|
+
Mp00127: left-to-right-maxima to Dyck path: Permutations -> Dyck paths
|
|
3888
|
+
Mp00099: bounce path: Dyck paths -> Dyck paths
|
|
3889
|
+
<BLANKLINE>
|
|
3890
|
+
among the values you sent, 83 percent are actually in the database
|
|
3891
|
+
"""
|
|
3892
|
+
print("your input matches")
|
|
3893
|
+
super().info()
|
|
3894
|
+
print()
|
|
3895
|
+
print("among the values you sent, %s percent are actually in the database" % self.quality()[0])
|
|
3896
|
+
|
|
3897
|
+
|
|
3898
|
+
######################################################################
|
|
3899
|
+
# collections
|
|
3900
|
+
######################################################################
|
|
3901
|
+
|
|
3902
|
+
# helper for generation of CartanTypes
|
|
3903
|
+
def _finite_irreducible_cartan_types_by_rank(n):
|
|
3904
|
+
"""
|
|
3905
|
+
Return the Cartan types of rank `n`.
|
|
3906
|
+
|
|
3907
|
+
INPUT:
|
|
3908
|
+
|
|
3909
|
+
- ``n`` -- integer
|
|
3910
|
+
|
|
3911
|
+
OUTPUT: the list of Cartan types of rank `n`
|
|
3912
|
+
|
|
3913
|
+
TESTS::
|
|
3914
|
+
|
|
3915
|
+
sage: from sage.databases.findstat import _finite_irreducible_cartan_types_by_rank
|
|
3916
|
+
sage: _finite_irreducible_cartan_types_by_rank(2)
|
|
3917
|
+
[['A', 2], ['B', 2], ['G', 2]]
|
|
3918
|
+
"""
|
|
3919
|
+
cartan_types = [CartanType(['A',n])]
|
|
3920
|
+
if n >= 2:
|
|
3921
|
+
cartan_types += [CartanType(['B',n])]
|
|
3922
|
+
if n >= 3:
|
|
3923
|
+
cartan_types += [CartanType(['C',n])]
|
|
3924
|
+
if n >= 4:
|
|
3925
|
+
cartan_types += [CartanType(['D',n])]
|
|
3926
|
+
if 6 <= n <= 8:
|
|
3927
|
+
cartan_types += [CartanType(['E',n])]
|
|
3928
|
+
if n == 4:
|
|
3929
|
+
cartan_types += [CartanType(['F',n])]
|
|
3930
|
+
if n == 2:
|
|
3931
|
+
cartan_types += [CartanType(['G',n])]
|
|
3932
|
+
return cartan_types
|
|
3933
|
+
|
|
3934
|
+
# helper for generation of PlanePartitions
|
|
3935
|
+
|
|
3936
|
+
|
|
3937
|
+
def _plane_partitions_by_size_aux(n, outer=None):
|
|
3938
|
+
"""
|
|
3939
|
+
Iterate over the plane partitions with `n` boxes, as lists.
|
|
3940
|
+
|
|
3941
|
+
INPUT:
|
|
3942
|
+
|
|
3943
|
+
- ``n`` -- integer
|
|
3944
|
+
|
|
3945
|
+
OUTPUT: the plane partitions with `n` boxes as lists
|
|
3946
|
+
|
|
3947
|
+
TESTS::
|
|
3948
|
+
|
|
3949
|
+
sage: from sage.databases.findstat import _plane_partitions_by_size_aux
|
|
3950
|
+
sage: list(_plane_partitions_by_size_aux(3))
|
|
3951
|
+
[[[1], [1], [1]], [[2], [1]], [[1, 1], [1]], [[3]], [[2, 1]], [[1, 1, 1]]]
|
|
3952
|
+
"""
|
|
3953
|
+
if n == 0:
|
|
3954
|
+
yield []
|
|
3955
|
+
return
|
|
3956
|
+
if outer is None:
|
|
3957
|
+
outer = [n]*n
|
|
3958
|
+
for k in range(1, n+1):
|
|
3959
|
+
for la in Partitions(k, outer=outer):
|
|
3960
|
+
for pp in _plane_partitions_by_size_aux(n-k, outer=la):
|
|
3961
|
+
pp = [la] + pp
|
|
3962
|
+
yield pp
|
|
3963
|
+
|
|
3964
|
+
|
|
3965
|
+
def _plane_partitions_by_size(n):
|
|
3966
|
+
"""
|
|
3967
|
+
Iterate over the plane partitions with `n` boxes.
|
|
3968
|
+
|
|
3969
|
+
.. TODO::
|
|
3970
|
+
|
|
3971
|
+
This can be replaced when :issue:`28244` is merged.
|
|
3972
|
+
|
|
3973
|
+
INPUT:
|
|
3974
|
+
|
|
3975
|
+
- ``n`` -- integer
|
|
3976
|
+
|
|
3977
|
+
OUTPUT: the plane partitions with `n` boxes
|
|
3978
|
+
|
|
3979
|
+
TESTS::
|
|
3980
|
+
|
|
3981
|
+
sage: from sage.databases.findstat import _plane_partitions_by_size
|
|
3982
|
+
sage: list(_plane_partitions_by_size(3))
|
|
3983
|
+
[Plane partition [[1], [1], [1]],
|
|
3984
|
+
Plane partition [[2], [1]],
|
|
3985
|
+
Plane partition [[1, 1], [1]],
|
|
3986
|
+
Plane partition [[3]],
|
|
3987
|
+
Plane partition [[2, 1]],
|
|
3988
|
+
Plane partition [[1, 1, 1]]]
|
|
3989
|
+
"""
|
|
3990
|
+
for pp in _plane_partitions_by_size_aux(n):
|
|
3991
|
+
yield PlanePartition(pp)
|
|
3992
|
+
|
|
3993
|
+
# helper for generation of Lattices
|
|
3994
|
+
|
|
3995
|
+
|
|
3996
|
+
def _finite_lattices(n):
|
|
3997
|
+
"""
|
|
3998
|
+
Iterate over the lattices with `n` elements.
|
|
3999
|
+
|
|
4000
|
+
INPUT:
|
|
4001
|
+
|
|
4002
|
+
- ``n`` -- integer
|
|
4003
|
+
|
|
4004
|
+
OUTPUT: the lattices with `n` elements
|
|
4005
|
+
|
|
4006
|
+
TESTS::
|
|
4007
|
+
|
|
4008
|
+
sage: from sage.databases.findstat import _finite_lattices
|
|
4009
|
+
sage: sorted((L.cover_relations() for L in _finite_lattices(4)), # needs nauty
|
|
4010
|
+
....: key=len)
|
|
4011
|
+
[[['bottom', 0], [0, 1], [1, 'top']],
|
|
4012
|
+
[['bottom', 0], ['bottom', 1], [0, 'top'], [1, 'top']]]
|
|
4013
|
+
"""
|
|
4014
|
+
if n <= 2:
|
|
4015
|
+
for P in Posets(n):
|
|
4016
|
+
if P.is_lattice():
|
|
4017
|
+
yield LatticePoset(P)
|
|
4018
|
+
else:
|
|
4019
|
+
for P in Posets(n - 2):
|
|
4020
|
+
Q = P.with_bounds()
|
|
4021
|
+
if Q.is_lattice():
|
|
4022
|
+
yield LatticePoset(Q)
|
|
4023
|
+
|
|
4024
|
+
|
|
4025
|
+
class FindStatCollection(Element,
|
|
4026
|
+
metaclass=InheritComparisonClasscallMetaclass):
|
|
4027
|
+
r"""
|
|
4028
|
+
A FindStat collection.
|
|
4029
|
+
|
|
4030
|
+
:class:`FindStatCollection` is a class representing a
|
|
4031
|
+
combinatorial collection available in the FindStat database.
|
|
4032
|
+
|
|
4033
|
+
Its main use is to allow easy specification of the combinatorial
|
|
4034
|
+
collection when using :class:`findstat<FindStat>`. It also
|
|
4035
|
+
provides methods to quickly access its FindStat web page
|
|
4036
|
+
(:meth:`browse`), check whether a particular element is actually
|
|
4037
|
+
in the range considered by FindStat (:meth:`in_range`), etc.
|
|
4038
|
+
|
|
4039
|
+
INPUT:
|
|
4040
|
+
|
|
4041
|
+
One of the following:
|
|
4042
|
+
|
|
4043
|
+
- a string eg. 'Dyck paths' or 'DyckPaths', case-insensitive, or
|
|
4044
|
+
|
|
4045
|
+
- an integer designating the FindStat id of the collection, or
|
|
4046
|
+
|
|
4047
|
+
- a Sage object belonging to a collection, or
|
|
4048
|
+
|
|
4049
|
+
- an iterable producing a Sage object belonging to a collection.
|
|
4050
|
+
|
|
4051
|
+
EXAMPLES::
|
|
4052
|
+
|
|
4053
|
+
sage: from sage.databases.findstat import FindStatCollection
|
|
4054
|
+
sage: FindStatCollection("Dyck paths") # optional -- internet
|
|
4055
|
+
Cc0005: Dyck paths
|
|
4056
|
+
|
|
4057
|
+
sage: FindStatCollection(5) # optional -- internet
|
|
4058
|
+
Cc0005: Dyck paths
|
|
4059
|
+
|
|
4060
|
+
sage: FindStatCollection(DyckWord([1,0,1,0])) # optional -- internet
|
|
4061
|
+
Cc0005: Dyck paths
|
|
4062
|
+
|
|
4063
|
+
sage: FindStatCollection(DyckWords(2)) # optional -- internet
|
|
4064
|
+
a subset of Cc0005: Dyck paths
|
|
4065
|
+
|
|
4066
|
+
sage: FindStatCollection(DyckWords) # optional -- internet
|
|
4067
|
+
Cc0005: Dyck paths
|
|
4068
|
+
|
|
4069
|
+
.. SEEALSO::
|
|
4070
|
+
|
|
4071
|
+
:class:`FindStatCollections`
|
|
4072
|
+
"""
|
|
4073
|
+
@staticmethod
|
|
4074
|
+
def __classcall_private__(cls, entry):
|
|
4075
|
+
"""
|
|
4076
|
+
Retrieve a collection from the database.
|
|
4077
|
+
|
|
4078
|
+
TESTS::
|
|
4079
|
+
|
|
4080
|
+
sage: from sage.databases.findstat import FindStatCollection
|
|
4081
|
+
sage: FindStatCollection(0) # optional -- internet
|
|
4082
|
+
Traceback (most recent call last):
|
|
4083
|
+
...
|
|
4084
|
+
ValueError: could not find FindStat collection for 0
|
|
4085
|
+
"""
|
|
4086
|
+
return FindStatCollections()(entry)
|
|
4087
|
+
|
|
4088
|
+
def __init__(self, parent, id, data, sageconstructor_overridden):
|
|
4089
|
+
"""
|
|
4090
|
+
Initialize the collection.
|
|
4091
|
+
|
|
4092
|
+
This should only be called in
|
|
4093
|
+
:meth:`FindStatCollections()._element_constructor_` via
|
|
4094
|
+
:meth:`FindStatCollections().element_class`.
|
|
4095
|
+
|
|
4096
|
+
INPUT:
|
|
4097
|
+
|
|
4098
|
+
- ``parent`` -- :class:`FindStatCollections`
|
|
4099
|
+
|
|
4100
|
+
- ``id`` -- the (padded) FindStat identifier of the collection
|
|
4101
|
+
|
|
4102
|
+
- ``data`` -- dictionary containing the properties of the
|
|
4103
|
+
collection, such as its name, the corresponding class in
|
|
4104
|
+
sage, and so on.
|
|
4105
|
+
|
|
4106
|
+
- ``sageconstructor_overridden`` -- either ``None`` or an
|
|
4107
|
+
iterable which yields a subset of the elements of the
|
|
4108
|
+
collection.
|
|
4109
|
+
|
|
4110
|
+
TESTS::
|
|
4111
|
+
|
|
4112
|
+
sage: from sage.databases.findstat import FindStatCollection
|
|
4113
|
+
sage: FindStatCollection(5).parent() # optional -- internet
|
|
4114
|
+
Set of combinatorial collections used by FindStat
|
|
4115
|
+
"""
|
|
4116
|
+
self._id = id
|
|
4117
|
+
self._data = data
|
|
4118
|
+
self._sageconstructor_overridden = sageconstructor_overridden
|
|
4119
|
+
|
|
4120
|
+
Element.__init__(self, parent)
|
|
4121
|
+
|
|
4122
|
+
def __reduce__(self):
|
|
4123
|
+
"""
|
|
4124
|
+
Return a function and its arguments needed to create the
|
|
4125
|
+
collection.
|
|
4126
|
+
|
|
4127
|
+
TESTS::
|
|
4128
|
+
|
|
4129
|
+
sage: from sage.databases.findstat import FindStatCollection
|
|
4130
|
+
sage: c = FindStatCollection("Permutations") # optional -- internet
|
|
4131
|
+
sage: loads(dumps(c)) == c # optional -- internet
|
|
4132
|
+
True
|
|
4133
|
+
"""
|
|
4134
|
+
return FindStatCollection, (self.id(),)
|
|
4135
|
+
|
|
4136
|
+
def _richcmp_(self, other, op):
|
|
4137
|
+
"""
|
|
4138
|
+
TESTS::
|
|
4139
|
+
|
|
4140
|
+
sage: from sage.databases.findstat import FindStatCollection, FindStatCollections
|
|
4141
|
+
sage: FindStatCollection("Permutations") == FindStatCollection("Permutations") # optional -- internet
|
|
4142
|
+
True
|
|
4143
|
+
|
|
4144
|
+
sage: FindStatCollection("Permutations") == FindStatCollection("Integer Partitions") # optional -- internet
|
|
4145
|
+
False
|
|
4146
|
+
|
|
4147
|
+
sage: FindStatCollection("Permutations") != FindStatCollection("Permutations") # optional -- internet
|
|
4148
|
+
False
|
|
4149
|
+
|
|
4150
|
+
sage: FindStatCollection("Permutations") != FindStatCollection("Integer Partitions") # optional -- internet
|
|
4151
|
+
True
|
|
4152
|
+
|
|
4153
|
+
sage: FindStatCollection("Permutations") == 1 # optional -- internet
|
|
4154
|
+
False
|
|
4155
|
+
|
|
4156
|
+
sage: FindStatCollection("Permutations") != 1 # optional -- internet
|
|
4157
|
+
True
|
|
4158
|
+
|
|
4159
|
+
sage: sorted(c for c in FindStatCollections())[0] # optional -- internet
|
|
4160
|
+
Cc0001: Permutations
|
|
4161
|
+
|
|
4162
|
+
It is not clear what we want here, but equality may be better::
|
|
4163
|
+
|
|
4164
|
+
sage: FindStatCollection(graphs(3)) == FindStatCollection("Graphs") # optional -- internet
|
|
4165
|
+
True
|
|
4166
|
+
"""
|
|
4167
|
+
return richcmp(self.id(), other.id(), op)
|
|
4168
|
+
|
|
4169
|
+
def __hash__(self):
|
|
4170
|
+
"""
|
|
4171
|
+
Return a hash value for the collection.
|
|
4172
|
+
|
|
4173
|
+
EXAMPLES::
|
|
4174
|
+
|
|
4175
|
+
sage: from sage.databases.findstat import FindStatCollections
|
|
4176
|
+
sage: set(FindStatCollections()) # optional -- internet
|
|
4177
|
+
{Cc0001: Permutations,
|
|
4178
|
+
Cc0002: Integer partitions,
|
|
4179
|
+
...
|
|
4180
|
+
"""
|
|
4181
|
+
return self.id()
|
|
4182
|
+
|
|
4183
|
+
def is_supported(self):
|
|
4184
|
+
"""
|
|
4185
|
+
Check whether the collection is fully supported by the interface.
|
|
4186
|
+
|
|
4187
|
+
EXAMPLES::
|
|
4188
|
+
|
|
4189
|
+
sage: from sage.databases.findstat import FindStatCollection
|
|
4190
|
+
sage: FindStatCollection(1).is_supported() # optional -- internet
|
|
4191
|
+
True
|
|
4192
|
+
|
|
4193
|
+
sage: FindStatCollection(24).is_supported() # optional -- internet, random
|
|
4194
|
+
False
|
|
4195
|
+
"""
|
|
4196
|
+
return "Code" in self._data
|
|
4197
|
+
|
|
4198
|
+
def elements_on_level(self, level):
|
|
4199
|
+
"""
|
|
4200
|
+
Return an iterable over the elements on the given level.
|
|
4201
|
+
|
|
4202
|
+
EXAMPLES::
|
|
4203
|
+
|
|
4204
|
+
sage: from sage.databases.findstat import FindStatCollection
|
|
4205
|
+
sage: FindStatCollection("Perfect Matchings").elements_on_level(4) # optional -- internet
|
|
4206
|
+
Perfect matchings of {1, 2, 3, 4}
|
|
4207
|
+
"""
|
|
4208
|
+
return self._data["Code"].elements_on_level(level)
|
|
4209
|
+
|
|
4210
|
+
def element_level(self, element):
|
|
4211
|
+
"""
|
|
4212
|
+
Return the level of an element.
|
|
4213
|
+
|
|
4214
|
+
EXAMPLES::
|
|
4215
|
+
|
|
4216
|
+
sage: from sage.databases.findstat import FindStatCollection
|
|
4217
|
+
sage: cc = FindStatCollection("Perfect Matchings") # optional -- internet
|
|
4218
|
+
sage: cc.element_level(PerfectMatching([[1,2],[3,4],[5,6]])) # optional -- internet
|
|
4219
|
+
6
|
|
4220
|
+
"""
|
|
4221
|
+
return self._data["Code"].element_level(element)
|
|
4222
|
+
|
|
4223
|
+
def is_element(self, element):
|
|
4224
|
+
"""
|
|
4225
|
+
Return whether the element belongs to the collection.
|
|
4226
|
+
|
|
4227
|
+
If the collection is not yet supported, return whether element is a string.
|
|
4228
|
+
|
|
4229
|
+
EXAMPLES::
|
|
4230
|
+
|
|
4231
|
+
sage: from sage.databases.findstat import FindStatCollection
|
|
4232
|
+
sage: cc = FindStatCollection("Perfect Matchings") # optional -- internet
|
|
4233
|
+
sage: cc.is_element(PerfectMatching([[1,2],[3,4],[5,6]])) # optional -- internet
|
|
4234
|
+
True
|
|
4235
|
+
|
|
4236
|
+
sage: cc.is_element(SetPartition([[1,2],[3,4],[5,6]])) # optional -- internet
|
|
4237
|
+
False
|
|
4238
|
+
"""
|
|
4239
|
+
if self.is_supported():
|
|
4240
|
+
return self._data["Code"].is_element(element)
|
|
4241
|
+
|
|
4242
|
+
return isinstance(element, str)
|
|
4243
|
+
|
|
4244
|
+
def levels_with_sizes(self):
|
|
4245
|
+
"""
|
|
4246
|
+
Return a dictionary from levels to level sizes.
|
|
4247
|
+
|
|
4248
|
+
EXAMPLES::
|
|
4249
|
+
|
|
4250
|
+
sage: from sage.databases.findstat import FindStatCollection
|
|
4251
|
+
sage: cc = FindStatCollection("Perfect Matchings") # optional -- internet
|
|
4252
|
+
sage: cc.levels_with_sizes() # optional -- internet
|
|
4253
|
+
{2: 1, 4: 3, 6: 15, 8: 105, 10: 945}
|
|
4254
|
+
"""
|
|
4255
|
+
return self._data["LevelsWithSizes"]
|
|
4256
|
+
|
|
4257
|
+
def in_range(self, element):
|
|
4258
|
+
r"""
|
|
4259
|
+
Check whether an element of the collection is in FindStat's precomputed range.
|
|
4260
|
+
|
|
4261
|
+
INPUT:
|
|
4262
|
+
|
|
4263
|
+
- ``element`` -- a Sage object that belongs to the collection
|
|
4264
|
+
|
|
4265
|
+
OUTPUT: ``True``, if ``element`` is used by the FindStat search
|
|
4266
|
+
engine, and ``False`` if it is ignored
|
|
4267
|
+
|
|
4268
|
+
EXAMPLES::
|
|
4269
|
+
|
|
4270
|
+
sage: # optional - internet
|
|
4271
|
+
sage: from sage.databases.findstat import FindStatCollection
|
|
4272
|
+
sage: c = FindStatCollection("GelfandTsetlinPatterns")
|
|
4273
|
+
sage: c.in_range(GelfandTsetlinPattern([[2, 1], [1]]))
|
|
4274
|
+
True
|
|
4275
|
+
sage: c.in_range(GelfandTsetlinPattern([[3, 1], [1]]))
|
|
4276
|
+
True
|
|
4277
|
+
sage: c.in_range(GelfandTsetlinPattern([[7, 1], [1]]))
|
|
4278
|
+
False
|
|
4279
|
+
|
|
4280
|
+
TESTS::
|
|
4281
|
+
|
|
4282
|
+
sage: from sage.databases.findstat import FindStatCollections
|
|
4283
|
+
sage: l = FindStatCollections() # optional -- internet
|
|
4284
|
+
sage: long = [14, 20]
|
|
4285
|
+
sage: for c in sorted(l): # optional -- internet
|
|
4286
|
+
....: if c.id() not in long and c.is_supported():
|
|
4287
|
+
....: f = c.first_terms(lambda x: 1)
|
|
4288
|
+
....: print("{} {} {}".format(c, len(list(f)), all(c.in_range(e) for e, _ in f)))
|
|
4289
|
+
Cc0001: Permutations 5913 True
|
|
4290
|
+
Cc0002: Integer partitions 1211 True
|
|
4291
|
+
Cc0005: Dyck paths 2055 True
|
|
4292
|
+
Cc0006: Integer compositions 1023 True
|
|
4293
|
+
Cc0007: Standard tableaux 1115 True
|
|
4294
|
+
Cc0009: Set partitions 1155 True
|
|
4295
|
+
Cc0010: Binary trees 2055 True
|
|
4296
|
+
Cc0012: Perfect matchings 1069 True
|
|
4297
|
+
Cc0013: Cores 100 True
|
|
4298
|
+
Cc0017: Alternating sign matrices 7917 True
|
|
4299
|
+
Cc0018: Gelfand-Tsetlin patterns 1409 True
|
|
4300
|
+
Cc0019: Semistandard tableaux 2374 True
|
|
4301
|
+
Cc0021: Ordered trees 2056 True
|
|
4302
|
+
Cc0022: Finite Cartan types 31 True
|
|
4303
|
+
Cc0023: Parking functions 18248 True
|
|
4304
|
+
Cc0024: Binary words 1022 True
|
|
4305
|
+
Cc0025: Plane partitions 1123 True
|
|
4306
|
+
Cc0026: Decorated permutations 2371 True
|
|
4307
|
+
Cc0027: Signed permutations 4282 True
|
|
4308
|
+
Cc0028: Skew partitions 1250 True
|
|
4309
|
+
Cc0029: Lattices 1378 True
|
|
4310
|
+
Cc0030: Ordered set partitions 5316 True
|
|
4311
|
+
"""
|
|
4312
|
+
return self._data["Code"].element_level(element) in self._data["LevelsWithSizes"]
|
|
4313
|
+
|
|
4314
|
+
def first_terms(self, function, level=None):
|
|
4315
|
+
r"""
|
|
4316
|
+
Compute the first few terms of the given function, possibly
|
|
4317
|
+
restricted to a level, as a lazy list.
|
|
4318
|
+
|
|
4319
|
+
INPUT:
|
|
4320
|
+
|
|
4321
|
+
- ``function`` -- a callable
|
|
4322
|
+
|
|
4323
|
+
- ``level`` -- (optional), the level to restrict to
|
|
4324
|
+
|
|
4325
|
+
OUTPUT:
|
|
4326
|
+
|
|
4327
|
+
A lazy list of pairs of the form ``(object, value)``.
|
|
4328
|
+
|
|
4329
|
+
EXAMPLES::
|
|
4330
|
+
|
|
4331
|
+
sage: from sage.databases.findstat import FindStatCollection
|
|
4332
|
+
sage: c = FindStatCollection("GelfandTsetlinPatterns") # optional -- internet
|
|
4333
|
+
sage: c.first_terms(lambda x: 1)[:10].list() # optional -- internet
|
|
4334
|
+
[([[1, 0], [0]], 1),
|
|
4335
|
+
([[1, 0], [1]], 1),
|
|
4336
|
+
([[2, 0], [0]], 1),
|
|
4337
|
+
([[2, 0], [1]], 1),
|
|
4338
|
+
([[2, 0], [2]], 1),
|
|
4339
|
+
([[1, 1], [1]], 1),
|
|
4340
|
+
([[1, 0, 0], [0, 0], [0]], 1),
|
|
4341
|
+
([[1, 0, 0], [1, 0], [0]], 1),
|
|
4342
|
+
([[1, 0, 0], [1, 0], [1]], 1),
|
|
4343
|
+
([[3, 0], [0]], 1)]
|
|
4344
|
+
"""
|
|
4345
|
+
if self._sageconstructor_overridden is None:
|
|
4346
|
+
if level is None:
|
|
4347
|
+
g = (x
|
|
4348
|
+
for level in self._data["LevelsWithSizes"]
|
|
4349
|
+
for x in self._data["Code"].elements_on_level(level))
|
|
4350
|
+
else:
|
|
4351
|
+
g = (x for x in self._data["Code"].elements_on_level(level))
|
|
4352
|
+
else:
|
|
4353
|
+
if level is None:
|
|
4354
|
+
g = self._sageconstructor_overridden
|
|
4355
|
+
else:
|
|
4356
|
+
g = (x for x in self._sageconstructor_overridden
|
|
4357
|
+
if self.element_level(x) == level)
|
|
4358
|
+
|
|
4359
|
+
return lazy_list((x, function(x)) for x in g)
|
|
4360
|
+
|
|
4361
|
+
def id(self):
|
|
4362
|
+
r"""
|
|
4363
|
+
Return the FindStat identifier of the collection.
|
|
4364
|
+
|
|
4365
|
+
OUTPUT: the FindStat identifier of the collection as an integer
|
|
4366
|
+
|
|
4367
|
+
EXAMPLES::
|
|
4368
|
+
|
|
4369
|
+
sage: from sage.databases.findstat import FindStatCollection
|
|
4370
|
+
sage: c = FindStatCollection("GelfandTsetlinPatterns") # optional -- internet
|
|
4371
|
+
sage: c.id() # optional -- internet
|
|
4372
|
+
18
|
|
4373
|
+
"""
|
|
4374
|
+
return int(self._id[2:])
|
|
4375
|
+
|
|
4376
|
+
def id_str(self):
|
|
4377
|
+
r"""
|
|
4378
|
+
Return the FindStat identifier of the collection.
|
|
4379
|
+
|
|
4380
|
+
OUTPUT: the FindStat identifier of the collection as a string
|
|
4381
|
+
|
|
4382
|
+
EXAMPLES::
|
|
4383
|
+
|
|
4384
|
+
sage: from sage.databases.findstat import FindStatCollection
|
|
4385
|
+
sage: c = FindStatCollection("GelfandTsetlinPatterns") # optional -- internet
|
|
4386
|
+
sage: c.id_str() # optional -- internet
|
|
4387
|
+
'Cc0018'
|
|
4388
|
+
"""
|
|
4389
|
+
return self._id
|
|
4390
|
+
|
|
4391
|
+
def browse(self):
|
|
4392
|
+
r"""
|
|
4393
|
+
Open the FindStat web page of the collection in a browser.
|
|
4394
|
+
|
|
4395
|
+
EXAMPLES::
|
|
4396
|
+
|
|
4397
|
+
sage: from sage.databases.findstat import FindStatCollection
|
|
4398
|
+
sage: FindStatCollection("Permutations").browse() # optional -- webbrowser
|
|
4399
|
+
"""
|
|
4400
|
+
webbrowser.open(FINDSTAT_URL_COLLECTIONS + self._id)
|
|
4401
|
+
|
|
4402
|
+
def to_string(self):
|
|
4403
|
+
r"""
|
|
4404
|
+
Return a function that returns a FindStat representation given an
|
|
4405
|
+
object.
|
|
4406
|
+
|
|
4407
|
+
If the collection is not yet supported, return the identity.
|
|
4408
|
+
|
|
4409
|
+
OUTPUT:
|
|
4410
|
+
|
|
4411
|
+
The function that produces the string representation as
|
|
4412
|
+
needed by the FindStat search webpage.
|
|
4413
|
+
|
|
4414
|
+
EXAMPLES::
|
|
4415
|
+
|
|
4416
|
+
sage: from sage.databases.findstat import FindStatCollection
|
|
4417
|
+
sage: p = Poset((range(3), [[0, 1], [1, 2]])) # optional -- internet
|
|
4418
|
+
sage: c = FindStatCollection("Posets") # optional -- internet
|
|
4419
|
+
sage: c.to_string()(p) # optional -- internet
|
|
4420
|
+
'([(0, 1), (1, 2)], 3)'
|
|
4421
|
+
"""
|
|
4422
|
+
if self.is_supported():
|
|
4423
|
+
return self._data["Code"].element_to_string
|
|
4424
|
+
return lambda x: x
|
|
4425
|
+
|
|
4426
|
+
def from_string(self):
|
|
4427
|
+
r"""
|
|
4428
|
+
Return a function that returns the object given a FindStat
|
|
4429
|
+
representation.
|
|
4430
|
+
|
|
4431
|
+
OUTPUT:
|
|
4432
|
+
|
|
4433
|
+
The function that produces the Sage object given its FindStat
|
|
4434
|
+
representation as a string.
|
|
4435
|
+
|
|
4436
|
+
EXAMPLES::
|
|
4437
|
+
|
|
4438
|
+
sage: from sage.databases.findstat import FindStatCollection
|
|
4439
|
+
sage: c = FindStatCollection("Posets") # optional -- internet
|
|
4440
|
+
sage: p = c.from_string()('([(0, 2), (2, 1)], 3)') # optional -- internet
|
|
4441
|
+
sage: p.cover_relations() # optional -- internet
|
|
4442
|
+
[[0, 2], [2, 1]]
|
|
4443
|
+
|
|
4444
|
+
sage: c = FindStatCollection("Binary Words") # optional -- internet
|
|
4445
|
+
sage: w = c.from_string()('010101') # optional -- internet
|
|
4446
|
+
sage: w in c._data["Code"].elements_on_level(6) # optional -- internet
|
|
4447
|
+
True
|
|
4448
|
+
"""
|
|
4449
|
+
return self._data["Code"].string_to_element
|
|
4450
|
+
|
|
4451
|
+
def _repr_(self):
|
|
4452
|
+
r"""
|
|
4453
|
+
Return the representation of the FindStat collection.
|
|
4454
|
+
|
|
4455
|
+
OUTPUT: the representation, including the identifier and the name
|
|
4456
|
+
|
|
4457
|
+
EXAMPLES::
|
|
4458
|
+
|
|
4459
|
+
sage: from sage.databases.findstat import FindStatCollection
|
|
4460
|
+
sage: FindStatCollection("Binary trees") # optional -- internet
|
|
4461
|
+
Cc0010: Binary trees
|
|
4462
|
+
"""
|
|
4463
|
+
if self._sageconstructor_overridden:
|
|
4464
|
+
return "a subset of %s: %s" % (self.id_str(), self._data["NamePlural"])
|
|
4465
|
+
return "%s: %s" % (self.id_str(), self._data["NamePlural"])
|
|
4466
|
+
|
|
4467
|
+
def name(self, style='singular'):
|
|
4468
|
+
r"""
|
|
4469
|
+
Return the name of the FindStat collection.
|
|
4470
|
+
|
|
4471
|
+
INPUT:
|
|
4472
|
+
|
|
4473
|
+
- ``style`` -- string (default: ``'singular'``); can be
|
|
4474
|
+
``'singular'``, or ``'plural'``
|
|
4475
|
+
|
|
4476
|
+
OUTPUT: the name of the FindStat collection, in singular or in plural
|
|
4477
|
+
|
|
4478
|
+
EXAMPLES::
|
|
4479
|
+
|
|
4480
|
+
sage: from sage.databases.findstat import FindStatCollection
|
|
4481
|
+
sage: FindStatCollection("Binary trees").name() # optional -- internet
|
|
4482
|
+
'Binary tree'
|
|
4483
|
+
|
|
4484
|
+
sage: FindStatCollection("Binary trees").name(style='plural') # optional -- internet
|
|
4485
|
+
'Binary trees'
|
|
4486
|
+
"""
|
|
4487
|
+
if style == "singular":
|
|
4488
|
+
return self._data["Name"]
|
|
4489
|
+
if style == "plural":
|
|
4490
|
+
return self._data["NamePlural"]
|
|
4491
|
+
raise ValueError("argument 'style' (=%s) must be 'singular' or 'plural'" % style)
|
|
4492
|
+
|
|
4493
|
+
|
|
4494
|
+
from collections import namedtuple
|
|
4495
|
+
_SupportedFindStatCollection = namedtuple("SupportedFindStatCollection",
|
|
4496
|
+
["string_to_element",
|
|
4497
|
+
"element_to_string",
|
|
4498
|
+
"elements_on_level", # return all elements on given level
|
|
4499
|
+
"element_level", # return level of a given element
|
|
4500
|
+
"is_element"]) # return whether element is member of this collection
|
|
4501
|
+
|
|
4502
|
+
# this dictionary must be sorted so that subclasses come before
|
|
4503
|
+
# superclasses, eg., "StandardTableaux" before "SemistandardTableaux"
|
|
4504
|
+
_SupportedFindStatCollections = {
|
|
4505
|
+
"Permutations":
|
|
4506
|
+
_SupportedFindStatCollection(lambda x: Permutation(literal_eval(x)),
|
|
4507
|
+
str,
|
|
4508
|
+
Permutations,
|
|
4509
|
+
lambda x: x.size(),
|
|
4510
|
+
lambda x: isinstance(x, Permutation)),
|
|
4511
|
+
"BinaryWords":
|
|
4512
|
+
_SupportedFindStatCollection(lambda x: Word((int(e) for e in str(x)), alphabet=[0,1]),
|
|
4513
|
+
str,
|
|
4514
|
+
lambda x: Words([0,1], length=x),
|
|
4515
|
+
lambda x: x.length(),
|
|
4516
|
+
lambda x: isinstance(x, Word_class)),
|
|
4517
|
+
"AlternatingSignMatrices":
|
|
4518
|
+
_SupportedFindStatCollection(lambda x: AlternatingSignMatrix(literal_eval(x)),
|
|
4519
|
+
lambda x: str(list(map(list, x.to_matrix().rows()))),
|
|
4520
|
+
AlternatingSignMatrices,
|
|
4521
|
+
lambda x: x.to_matrix().nrows(),
|
|
4522
|
+
lambda x: isinstance(x, AlternatingSignMatrix)),
|
|
4523
|
+
"BinaryTrees":
|
|
4524
|
+
_SupportedFindStatCollection(lambda x: BinaryTree(str(x)),
|
|
4525
|
+
str,
|
|
4526
|
+
BinaryTrees,
|
|
4527
|
+
lambda x: x.node_number(),
|
|
4528
|
+
lambda x: isinstance(x, BinaryTree)),
|
|
4529
|
+
"Cores":
|
|
4530
|
+
_SupportedFindStatCollection(lambda x: Core(*literal_eval(x)),
|
|
4531
|
+
lambda X: "( " + X._repr_() + ", " + str(X.k()) + " )",
|
|
4532
|
+
lambda x: Cores(x[1], x[0]),
|
|
4533
|
+
lambda x: (x.length(), x.k()),
|
|
4534
|
+
lambda x: isinstance(x, Core)),
|
|
4535
|
+
"DyckPaths":
|
|
4536
|
+
_SupportedFindStatCollection(lambda x: DyckWord(literal_eval(x)),
|
|
4537
|
+
lambda x: str(list(DyckWord(x))),
|
|
4538
|
+
DyckWords,
|
|
4539
|
+
lambda x: x.semilength(),
|
|
4540
|
+
lambda x: isinstance(x, DyckWord)),
|
|
4541
|
+
"FiniteCartanTypes":
|
|
4542
|
+
_SupportedFindStatCollection(lambda x: CartanType(*literal_eval(str(x))),
|
|
4543
|
+
str,
|
|
4544
|
+
_finite_irreducible_cartan_types_by_rank,
|
|
4545
|
+
lambda x: x.rank(),
|
|
4546
|
+
lambda x: isinstance(x, CartanType_abstract)),
|
|
4547
|
+
"GelfandTsetlinPatterns":
|
|
4548
|
+
_SupportedFindStatCollection(lambda x: GelfandTsetlinPattern(literal_eval(x)),
|
|
4549
|
+
str,
|
|
4550
|
+
lambda x: (P
|
|
4551
|
+
for la in Partitions(x[1], max_length=x[0])
|
|
4552
|
+
for P in GelfandTsetlinPatterns(top_row=la + [0]*(x[0]-len(la)))),
|
|
4553
|
+
lambda x: (len(x[0]), sum(x[0])),
|
|
4554
|
+
lambda x: (x == GelfandTsetlinPatterns
|
|
4555
|
+
or isinstance(x, GelfandTsetlinPattern))),
|
|
4556
|
+
"Graphs":
|
|
4557
|
+
_SupportedFindStatCollection(lambda x: (lambda E, V: Graph([list(range(V)),
|
|
4558
|
+
lambda i,j: (i,j) in E or (j,i) in E],
|
|
4559
|
+
immutable=True))(*literal_eval(x)),
|
|
4560
|
+
lambda X: str((X.edges(labels=False, sort=True), X.num_verts())),
|
|
4561
|
+
lambda x: (g.copy(immutable=True) for g in graphs(x, copy=False)),
|
|
4562
|
+
lambda x: x.num_verts(),
|
|
4563
|
+
lambda x: isinstance(x, Graph)),
|
|
4564
|
+
"IntegerPartitions":
|
|
4565
|
+
_SupportedFindStatCollection(lambda x: Partition(literal_eval(x)),
|
|
4566
|
+
str,
|
|
4567
|
+
Partitions,
|
|
4568
|
+
lambda x: x.size(),
|
|
4569
|
+
lambda x: isinstance(x, Partition)),
|
|
4570
|
+
"IntegerCompositions":
|
|
4571
|
+
_SupportedFindStatCollection(lambda x: Composition(literal_eval(x)),
|
|
4572
|
+
str,
|
|
4573
|
+
Compositions,
|
|
4574
|
+
lambda x: x.size(),
|
|
4575
|
+
lambda x: isinstance(x, Composition)),
|
|
4576
|
+
"OrderedTrees":
|
|
4577
|
+
_SupportedFindStatCollection(lambda x: OrderedTree(literal_eval(x)),
|
|
4578
|
+
str,
|
|
4579
|
+
OrderedTrees,
|
|
4580
|
+
lambda x: x.node_number(),
|
|
4581
|
+
lambda x: isinstance(x, OrderedTree)),
|
|
4582
|
+
"ParkingFunctions":
|
|
4583
|
+
_SupportedFindStatCollection(lambda x: ParkingFunction(literal_eval(x)),
|
|
4584
|
+
str,
|
|
4585
|
+
ParkingFunctions,
|
|
4586
|
+
len,
|
|
4587
|
+
lambda x: isinstance(x, ParkingFunction)),
|
|
4588
|
+
"Lattices":
|
|
4589
|
+
_SupportedFindStatCollection(lambda x: (lambda R, E: LatticePoset((list(range(E)), R)))(*literal_eval(x)),
|
|
4590
|
+
lambda X: str((sorted(X._hasse_diagram.cover_relations()),
|
|
4591
|
+
len(X._hasse_diagram.vertices(sort=False)))),
|
|
4592
|
+
_finite_lattices,
|
|
4593
|
+
lambda x: x.cardinality(),
|
|
4594
|
+
lambda x: isinstance(x, FiniteLatticePoset)),
|
|
4595
|
+
"Posets":
|
|
4596
|
+
_SupportedFindStatCollection(lambda x: (lambda R, E: Poset((list(range(E)), R)))(*literal_eval(x)),
|
|
4597
|
+
lambda X: str((sorted(X._hasse_diagram.cover_relations()),
|
|
4598
|
+
len(X._hasse_diagram.vertices(sort=False)))),
|
|
4599
|
+
Posets,
|
|
4600
|
+
lambda x: x.cardinality(),
|
|
4601
|
+
lambda x: isinstance(x, FinitePoset)),
|
|
4602
|
+
"StandardTableaux":
|
|
4603
|
+
_SupportedFindStatCollection(lambda x: StandardTableau(literal_eval(x)),
|
|
4604
|
+
str,
|
|
4605
|
+
StandardTableaux,
|
|
4606
|
+
lambda x: x.size(),
|
|
4607
|
+
lambda x: isinstance(x, StandardTableau)),
|
|
4608
|
+
"SemistandardTableaux":
|
|
4609
|
+
_SupportedFindStatCollection(lambda x: SemistandardTableau(literal_eval(x)),
|
|
4610
|
+
str,
|
|
4611
|
+
lambda x: (T for T in SemistandardTableaux(size=x[0], max_entry=x[1])
|
|
4612
|
+
if max(T.entries()) == x[1]),
|
|
4613
|
+
lambda x: (x.size(), max(x.entries())),
|
|
4614
|
+
lambda x: isinstance(x, SemistandardTableau)),
|
|
4615
|
+
"PerfectMatchings":
|
|
4616
|
+
_SupportedFindStatCollection(lambda x: PerfectMatching(literal_eval(x)),
|
|
4617
|
+
str,
|
|
4618
|
+
PerfectMatchings,
|
|
4619
|
+
lambda x: x.size(),
|
|
4620
|
+
lambda x: isinstance(x, PerfectMatching)),
|
|
4621
|
+
"SetPartitions":
|
|
4622
|
+
_SupportedFindStatCollection(lambda x: SetPartition(literal_eval(x.replace('{','[').replace('}',']'))),
|
|
4623
|
+
str,
|
|
4624
|
+
SetPartitions,
|
|
4625
|
+
lambda x: x.size(),
|
|
4626
|
+
lambda x: isinstance(x, SetPartition)),
|
|
4627
|
+
"SkewPartitions":
|
|
4628
|
+
_SupportedFindStatCollection(lambda x: SkewPartition(literal_eval(x)),
|
|
4629
|
+
str,
|
|
4630
|
+
SkewPartitions,
|
|
4631
|
+
lambda x: x.size(),
|
|
4632
|
+
lambda x: isinstance(x, SkewPartition)),
|
|
4633
|
+
"SignedPermutations":
|
|
4634
|
+
_SupportedFindStatCollection(lambda x: SignedPermutations(len(literal_eval(x)))(list(literal_eval(x))),
|
|
4635
|
+
str,
|
|
4636
|
+
SignedPermutations,
|
|
4637
|
+
lambda x: len(list(x)),
|
|
4638
|
+
lambda x: isinstance(x, SignedPermutation)),
|
|
4639
|
+
"PlanePartitions":
|
|
4640
|
+
_SupportedFindStatCollection(lambda x: PlanePartition(literal_eval(x)),
|
|
4641
|
+
lambda X: str(list(X)).replace(" ",""),
|
|
4642
|
+
_plane_partitions_by_size,
|
|
4643
|
+
lambda x: sum(sum(la) for la in x),
|
|
4644
|
+
lambda x: isinstance(x, PlanePartition)),
|
|
4645
|
+
"DecoratedPermutations":
|
|
4646
|
+
_SupportedFindStatCollection(lambda x: DecoratedPermutation([v if v > 0 else (i if v == 0 else -i)
|
|
4647
|
+
for i, v in enumerate(literal_eval(x.replace("+","0").replace("-","-1")), 1)]),
|
|
4648
|
+
lambda x: "[" + ",".join((str(v) if abs(v) != i else ("+" if v > 0 else "-")
|
|
4649
|
+
for i, v in enumerate(x, 1))) + "]",
|
|
4650
|
+
DecoratedPermutations,
|
|
4651
|
+
lambda x: x.size(),
|
|
4652
|
+
lambda x: isinstance(x, DecoratedPermutation)),
|
|
4653
|
+
"OrderedSetPartitions":
|
|
4654
|
+
_SupportedFindStatCollection(lambda x: OrderedSetPartition(literal_eval(x.replace('{','[').replace('}',']'))),
|
|
4655
|
+
str,
|
|
4656
|
+
OrderedSetPartitions,
|
|
4657
|
+
lambda x: x.size(),
|
|
4658
|
+
lambda x: isinstance(x, OrderedSetPartition))}
|
|
4659
|
+
|
|
4660
|
+
|
|
4661
|
+
class FindStatCollections(UniqueRepresentation, Parent):
|
|
4662
|
+
r"""
|
|
4663
|
+
The class of FindStat collections.
|
|
4664
|
+
|
|
4665
|
+
The elements of this class are combinatorial collections in
|
|
4666
|
+
FindStat as of January 2020. If a new collection was added to the
|
|
4667
|
+
web service since then, the dictionary ``_SupportedFindStatCollections``
|
|
4668
|
+
in this module has to be updated accordingly.
|
|
4669
|
+
|
|
4670
|
+
EXAMPLES::
|
|
4671
|
+
|
|
4672
|
+
sage: from sage.databases.findstat import FindStatCollections
|
|
4673
|
+
sage: sorted(c for c in FindStatCollections() if c.is_supported()) # optional -- internet
|
|
4674
|
+
[Cc0001: Permutations,
|
|
4675
|
+
Cc0002: Integer partitions,
|
|
4676
|
+
Cc0005: Dyck paths,
|
|
4677
|
+
Cc0006: Integer compositions,
|
|
4678
|
+
Cc0007: Standard tableaux,
|
|
4679
|
+
Cc0009: Set partitions,
|
|
4680
|
+
Cc0010: Binary trees,
|
|
4681
|
+
Cc0012: Perfect matchings,
|
|
4682
|
+
Cc0013: Cores,
|
|
4683
|
+
Cc0014: Posets,
|
|
4684
|
+
Cc0017: Alternating sign matrices,
|
|
4685
|
+
Cc0018: Gelfand-Tsetlin patterns,
|
|
4686
|
+
Cc0019: Semistandard tableaux,
|
|
4687
|
+
Cc0020: Graphs,
|
|
4688
|
+
Cc0021: Ordered trees,
|
|
4689
|
+
Cc0022: Finite Cartan types,
|
|
4690
|
+
Cc0023: Parking functions,
|
|
4691
|
+
Cc0024: Binary words,
|
|
4692
|
+
Cc0025: Plane partitions,
|
|
4693
|
+
Cc0026: Decorated permutations,
|
|
4694
|
+
Cc0027: Signed permutations,
|
|
4695
|
+
Cc0028: Skew partitions,
|
|
4696
|
+
Cc0029: Lattices,
|
|
4697
|
+
Cc0030: Ordered set partitions]
|
|
4698
|
+
"""
|
|
4699
|
+
def __init__(self):
|
|
4700
|
+
"""
|
|
4701
|
+
Fetch the collections from FindStat.
|
|
4702
|
+
|
|
4703
|
+
TESTS::
|
|
4704
|
+
|
|
4705
|
+
sage: from sage.databases.findstat import FindStatCollections
|
|
4706
|
+
sage: C = FindStatCollections() # optional -- internet
|
|
4707
|
+
sage: TestSuite(C).run() # optional -- internet
|
|
4708
|
+
"""
|
|
4709
|
+
fields = "LevelsWithSizes,Name,NamePlural,NameWiki"
|
|
4710
|
+
url = FINDSTAT_API_COLLECTIONS + "?fields=" + fields
|
|
4711
|
+
d = _get_json(url, object_pairs_hook=dict)["included"]["Collections"]
|
|
4712
|
+
for id, data in d.items():
|
|
4713
|
+
data["LevelsWithSizes"] = {literal_eval(level): size
|
|
4714
|
+
for level, size in data["LevelsWithSizes"].items()}
|
|
4715
|
+
if data["NameWiki"] in _SupportedFindStatCollections:
|
|
4716
|
+
data["Code"] = _SupportedFindStatCollections[data["NameWiki"]]
|
|
4717
|
+
else:
|
|
4718
|
+
print("%s provides a new collection:" % FindStat())
|
|
4719
|
+
print(" %s: %s" % (id, data["NamePlural"]))
|
|
4720
|
+
print("To use it with this interface, it has to be added to the dictionary")
|
|
4721
|
+
print(" _SupportedFindStatCollections in src/sage/databases/findstat.py")
|
|
4722
|
+
print("of the SageMath distribution. Please open an issue on github!")
|
|
4723
|
+
# print("Very likely, the following code would work:")
|
|
4724
|
+
# fields = "SageCodeElementToString,SageCodeElementsOnLevel,SageCodeStringToElement"
|
|
4725
|
+
# url = FINDSTAT_API_COLLECTIONS + id + "?fields=" + fields
|
|
4726
|
+
# print(json.load(urlopen(url))["included"]["Collections"][id])
|
|
4727
|
+
|
|
4728
|
+
def position(item):
|
|
4729
|
+
try:
|
|
4730
|
+
return tuple(_SupportedFindStatCollections).index(item[1]["NameWiki"])
|
|
4731
|
+
except ValueError:
|
|
4732
|
+
return len(_SupportedFindStatCollections)
|
|
4733
|
+
|
|
4734
|
+
self._findstat_collections = dict(sorted(d.items(), key=position))
|
|
4735
|
+
Parent.__init__(self, category=Sets())
|
|
4736
|
+
|
|
4737
|
+
def _element_constructor_(self, entry):
|
|
4738
|
+
"""
|
|
4739
|
+
Initialize a FindStat collection.
|
|
4740
|
+
|
|
4741
|
+
INPUT:
|
|
4742
|
+
|
|
4743
|
+
See :class:`FindStatCollection`.
|
|
4744
|
+
|
|
4745
|
+
TESTS:
|
|
4746
|
+
|
|
4747
|
+
Create an object and find its collection::
|
|
4748
|
+
|
|
4749
|
+
sage: from sage.databases.findstat import FindStatCollection, FindStatCollections
|
|
4750
|
+
sage: sorted([FindStatCollection(c.first_terms(lambda x: 0)[0][0]) for c in FindStatCollections() if c.is_supported()]) # optional -- internet
|
|
4751
|
+
[Cc0001: Permutations,
|
|
4752
|
+
Cc0002: Integer partitions,
|
|
4753
|
+
Cc0005: Dyck paths,
|
|
4754
|
+
Cc0006: Integer compositions,
|
|
4755
|
+
Cc0007: Standard tableaux,
|
|
4756
|
+
Cc0009: Set partitions,
|
|
4757
|
+
Cc0010: Binary trees,
|
|
4758
|
+
Cc0012: Perfect matchings,
|
|
4759
|
+
Cc0013: Cores,
|
|
4760
|
+
Cc0014: Posets,
|
|
4761
|
+
Cc0017: Alternating sign matrices,
|
|
4762
|
+
Cc0018: Gelfand-Tsetlin patterns,
|
|
4763
|
+
Cc0019: Semistandard tableaux,
|
|
4764
|
+
Cc0020: Graphs,
|
|
4765
|
+
Cc0021: Ordered trees,
|
|
4766
|
+
Cc0022: Finite Cartan types,
|
|
4767
|
+
Cc0023: Parking functions,
|
|
4768
|
+
Cc0024: Binary words,
|
|
4769
|
+
Cc0025: Plane partitions,
|
|
4770
|
+
Cc0026: Decorated permutations,
|
|
4771
|
+
Cc0027: Signed permutations,
|
|
4772
|
+
Cc0028: Skew partitions,
|
|
4773
|
+
Cc0029: Lattices,
|
|
4774
|
+
Cc0030: Ordered set partitions]
|
|
4775
|
+
|
|
4776
|
+
sage: FindStatCollection(Permutation([1,2,3])) # optional -- internet
|
|
4777
|
+
Cc0001: Permutations
|
|
4778
|
+
|
|
4779
|
+
sage: FindStatCollection(Permutations(3)) # optional -- internet
|
|
4780
|
+
a subset of Cc0001: Permutations
|
|
4781
|
+
|
|
4782
|
+
sage: FindStatCollection(PerfectMatching([[1,2]])) # optional -- internet
|
|
4783
|
+
Cc0012: Perfect matchings
|
|
4784
|
+
|
|
4785
|
+
sage: FindStatCollection(PerfectMatchings(4)) # optional -- internet
|
|
4786
|
+
a subset of Cc0012: Perfect matchings
|
|
4787
|
+
|
|
4788
|
+
sage: FindStatCollection(SetPartition([[1,2]])) # optional -- internet
|
|
4789
|
+
Cc0009: Set partitions
|
|
4790
|
+
|
|
4791
|
+
sage: FindStatCollection(CartanType("A3")) # optional -- internet
|
|
4792
|
+
Cc0022: Finite Cartan types
|
|
4793
|
+
|
|
4794
|
+
sage: cc = FindStatCollection(graphs(3)); cc # optional -- internet
|
|
4795
|
+
a subset of Cc0020: Graphs
|
|
4796
|
+
sage: cc.first_terms(lambda x: x.edges(labels=False, sort=True)).list() # optional -- internet
|
|
4797
|
+
[(Graph on 3 vertices, []),
|
|
4798
|
+
(Graph on 3 vertices, [(0, 2)]),
|
|
4799
|
+
(Graph on 3 vertices, [(0, 2), (1, 2)]),
|
|
4800
|
+
(Graph on 3 vertices, [(0, 1), (0, 2), (1, 2)])]
|
|
4801
|
+
|
|
4802
|
+
sage: len(cc.first_terms(lambda x: x.edges(labels=False, sort=False)).list()) # optional -- internet
|
|
4803
|
+
4
|
|
4804
|
+
|
|
4805
|
+
Check that we can override the automatic detection::
|
|
4806
|
+
|
|
4807
|
+
sage: l = [(T, len(T)) for n in range(1,5) for T in StandardTableaux(n)] # optional -- internet
|
|
4808
|
+
sage: qu = findstat("Semistandardtableaux", l, depth=1) # optional -- internet
|
|
4809
|
+
sage: qu[0] # optional -- internet
|
|
4810
|
+
St000010oMp00077 (quality [100, 100])
|
|
4811
|
+
"""
|
|
4812
|
+
if isinstance(entry, self.Element):
|
|
4813
|
+
return entry
|
|
4814
|
+
|
|
4815
|
+
if isinstance(entry, str):
|
|
4816
|
+
# find by name in self._findstat_collections (ignoring case and spaces)
|
|
4817
|
+
def normalize(e):
|
|
4818
|
+
return "".join(e.split()).upper()
|
|
4819
|
+
|
|
4820
|
+
for id, data in self._findstat_collections.items():
|
|
4821
|
+
if normalize(entry) in (normalize(id),
|
|
4822
|
+
normalize(data["NameWiki"]),
|
|
4823
|
+
normalize(data["NamePlural"]),
|
|
4824
|
+
normalize(data["Name"])):
|
|
4825
|
+
return self.element_class(self, id, data, None)
|
|
4826
|
+
|
|
4827
|
+
elif isinstance(entry, (int, Integer)):
|
|
4828
|
+
# find by id in _findstat_collections
|
|
4829
|
+
for id, data in self._findstat_collections.items():
|
|
4830
|
+
if entry == int(id[2:]):
|
|
4831
|
+
return self.element_class(self, id, data, None)
|
|
4832
|
+
|
|
4833
|
+
else:
|
|
4834
|
+
# find collection given an object or a constructor
|
|
4835
|
+
|
|
4836
|
+
# it would be good to first check whether entry is an
|
|
4837
|
+
# element rather than a type - unfortunately, we cannot
|
|
4838
|
+
# test with isinstance(_, SageObject), since this is True
|
|
4839
|
+
# for CartanType.
|
|
4840
|
+
|
|
4841
|
+
# first check whether the class fits:
|
|
4842
|
+
for id, data in self._findstat_collections.items():
|
|
4843
|
+
if ("Code" in data
|
|
4844
|
+
and (data["Code"].is_element(entry)
|
|
4845
|
+
# elements_on_level is rarely equal to entry
|
|
4846
|
+
# (it may be a function), but it is
|
|
4847
|
+
# convenient for some types
|
|
4848
|
+
or data["Code"].elements_on_level == entry)):
|
|
4849
|
+
return self.element_class(self, id, data, None)
|
|
4850
|
+
|
|
4851
|
+
# check whether entry is iterable (it's not a string!)
|
|
4852
|
+
# producing a subset of objects
|
|
4853
|
+
|
|
4854
|
+
# WARNING: SkewPartition is iterable and produces two
|
|
4855
|
+
# elements of Partition
|
|
4856
|
+
|
|
4857
|
+
# WARNING: we have to remember all elements of the
|
|
4858
|
+
# generator, because it is used again, for example in
|
|
4859
|
+
# self.first_terms
|
|
4860
|
+
try:
|
|
4861
|
+
entries = lazy_list(entry)
|
|
4862
|
+
obj = entries[0]
|
|
4863
|
+
except (TypeError, IndexError):
|
|
4864
|
+
pass
|
|
4865
|
+
else:
|
|
4866
|
+
for id, data in self._findstat_collections.items():
|
|
4867
|
+
if "Code" in data and data["Code"].is_element(obj):
|
|
4868
|
+
return self.element_class(self, id, data, entries)
|
|
4869
|
+
|
|
4870
|
+
raise ValueError("could not find FindStat collection for %s" % str(entry))
|
|
4871
|
+
|
|
4872
|
+
def _repr_(self):
|
|
4873
|
+
"""
|
|
4874
|
+
Return the representation of the set of FindStat collections.
|
|
4875
|
+
|
|
4876
|
+
EXAMPLES::
|
|
4877
|
+
|
|
4878
|
+
sage: from sage.databases.findstat import FindStatCollections
|
|
4879
|
+
sage: FindStatCollections() # optional -- internet
|
|
4880
|
+
Set of combinatorial collections used by FindStat
|
|
4881
|
+
"""
|
|
4882
|
+
return "Set of combinatorial collections used by FindStat"
|
|
4883
|
+
|
|
4884
|
+
def __iter__(self):
|
|
4885
|
+
"""
|
|
4886
|
+
Return an iterator over all FindStat collections.
|
|
4887
|
+
|
|
4888
|
+
EXAMPLES::
|
|
4889
|
+
|
|
4890
|
+
sage: from sage.databases.findstat import FindStatCollections
|
|
4891
|
+
sage: sorted(FindStatCollections())[0] # optional -- internet
|
|
4892
|
+
Cc0001: Permutations
|
|
4893
|
+
"""
|
|
4894
|
+
for c in self._findstat_collections:
|
|
4895
|
+
yield FindStatCollection(c)
|
|
4896
|
+
|
|
4897
|
+
Element = FindStatCollection
|