passagemath-combinat 10.6.42__cp314-cp314t-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_combinat/__init__.py +3 -0
- passagemath_combinat-10.6.42.dist-info/DELVEWHEEL +2 -0
- passagemath_combinat-10.6.42.dist-info/METADATA +160 -0
- passagemath_combinat-10.6.42.dist-info/RECORD +401 -0
- passagemath_combinat-10.6.42.dist-info/WHEEL +5 -0
- passagemath_combinat-10.6.42.dist-info/top_level.txt +3 -0
- passagemath_combinat.libs/libgmp-10-3a5f019e2510aeaad918cab2b57a689d.dll +0 -0
- passagemath_combinat.libs/libsymmetrica-3-7dcf900932804d0df5fd0919b4668720.dll +0 -0
- sage/algebras/affine_nil_temperley_lieb.py +263 -0
- sage/algebras/all.py +24 -0
- sage/algebras/all__sagemath_combinat.py +35 -0
- sage/algebras/askey_wilson.py +935 -0
- sage/algebras/associated_graded.py +345 -0
- sage/algebras/cellular_basis.py +350 -0
- sage/algebras/cluster_algebra.py +2766 -0
- sage/algebras/down_up_algebra.py +860 -0
- sage/algebras/free_algebra.py +1698 -0
- sage/algebras/free_algebra_element.py +345 -0
- sage/algebras/free_algebra_quotient.py +405 -0
- sage/algebras/free_algebra_quotient_element.py +295 -0
- sage/algebras/free_zinbiel_algebra.py +885 -0
- sage/algebras/hall_algebra.py +783 -0
- sage/algebras/hecke_algebras/all.py +4 -0
- sage/algebras/hecke_algebras/ariki_koike_algebra.py +1796 -0
- sage/algebras/hecke_algebras/ariki_koike_specht_modules.py +475 -0
- sage/algebras/hecke_algebras/cubic_hecke_algebra.py +3520 -0
- sage/algebras/hecke_algebras/cubic_hecke_base_ring.py +1473 -0
- sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py +1079 -0
- sage/algebras/iwahori_hecke_algebra.py +3095 -0
- sage/algebras/jordan_algebra.py +1773 -0
- sage/algebras/lie_conformal_algebras/abelian_lie_conformal_algebra.py +113 -0
- sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py +156 -0
- sage/algebras/lie_conformal_algebras/all.py +18 -0
- sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra.py +134 -0
- sage/algebras/lie_conformal_algebras/examples.py +43 -0
- sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra.py +131 -0
- sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py +139 -0
- sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py +174 -0
- sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py +167 -0
- sage/algebras/lie_conformal_algebras/freely_generated_lie_conformal_algebra.py +107 -0
- sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py +135 -0
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py +353 -0
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +236 -0
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py +78 -0
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py +328 -0
- sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra.py +117 -0
- sage/algebras/lie_conformal_algebras/neveu_schwarz_lie_conformal_algebra.py +86 -0
- sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra.py +82 -0
- sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra.py +205 -0
- sage/algebras/nil_coxeter_algebra.py +191 -0
- sage/algebras/q_commuting_polynomials.py +673 -0
- sage/algebras/q_system.py +608 -0
- sage/algebras/quantum_clifford.py +959 -0
- sage/algebras/quantum_groups/ace_quantum_onsager.py +693 -0
- sage/algebras/quantum_groups/all.py +9 -0
- sage/algebras/quantum_groups/fock_space.py +2219 -0
- sage/algebras/quantum_groups/q_numbers.py +207 -0
- sage/algebras/quantum_groups/quantum_group_gap.py +2695 -0
- sage/algebras/quantum_groups/representations.py +591 -0
- sage/algebras/quantum_matrix_coordinate_algebra.py +1006 -0
- sage/algebras/quantum_oscillator.py +623 -0
- sage/algebras/quaternion_algebra.py +20 -0
- sage/algebras/quaternion_algebra_element.py +55 -0
- sage/algebras/rational_cherednik_algebra.py +525 -0
- sage/algebras/schur_algebra.py +670 -0
- sage/algebras/shuffle_algebra.py +1011 -0
- sage/algebras/splitting_algebra.py +779 -0
- sage/algebras/tensor_algebra.py +709 -0
- sage/algebras/yangian.py +1082 -0
- sage/algebras/yokonuma_hecke_algebra.py +1018 -0
- sage/all__sagemath_combinat.py +44 -0
- sage/combinat/SJT.py +255 -0
- sage/combinat/affine_permutation.py +2405 -0
- sage/combinat/algebraic_combinatorics.py +55 -0
- sage/combinat/all.py +53 -0
- sage/combinat/all__sagemath_combinat.py +195 -0
- sage/combinat/alternating_sign_matrix.py +2063 -0
- sage/combinat/baxter_permutations.py +346 -0
- sage/combinat/bijectionist.py +3220 -0
- sage/combinat/binary_recurrence_sequences.py +1180 -0
- sage/combinat/blob_algebra.py +685 -0
- sage/combinat/catalog_partitions.py +27 -0
- sage/combinat/chas/all.py +23 -0
- sage/combinat/chas/fsym.py +1180 -0
- sage/combinat/chas/wqsym.py +2601 -0
- sage/combinat/cluster_complex.py +326 -0
- sage/combinat/colored_permutations.py +2039 -0
- sage/combinat/colored_permutations_representations.py +964 -0
- sage/combinat/composition_signed.py +142 -0
- sage/combinat/composition_tableau.py +855 -0
- sage/combinat/constellation.py +1729 -0
- sage/combinat/core.py +751 -0
- sage/combinat/counting.py +12 -0
- sage/combinat/crystals/affine.py +742 -0
- sage/combinat/crystals/affine_factorization.py +518 -0
- sage/combinat/crystals/affinization.py +331 -0
- sage/combinat/crystals/alcove_path.py +2013 -0
- sage/combinat/crystals/all.py +22 -0
- sage/combinat/crystals/bkk_crystals.py +141 -0
- sage/combinat/crystals/catalog.py +115 -0
- sage/combinat/crystals/catalog_elementary_crystals.py +18 -0
- sage/combinat/crystals/catalog_infinity_crystals.py +33 -0
- sage/combinat/crystals/catalog_kirillov_reshetikhin.py +18 -0
- sage/combinat/crystals/crystals.py +257 -0
- sage/combinat/crystals/direct_sum.py +260 -0
- sage/combinat/crystals/elementary_crystals.py +1251 -0
- sage/combinat/crystals/fast_crystals.py +441 -0
- sage/combinat/crystals/fully_commutative_stable_grothendieck.py +1205 -0
- sage/combinat/crystals/generalized_young_walls.py +1076 -0
- sage/combinat/crystals/highest_weight_crystals.py +436 -0
- sage/combinat/crystals/induced_structure.py +695 -0
- sage/combinat/crystals/infinity_crystals.py +730 -0
- sage/combinat/crystals/kac_modules.py +863 -0
- sage/combinat/crystals/kirillov_reshetikhin.py +4196 -0
- sage/combinat/crystals/kyoto_path_model.py +497 -0
- sage/combinat/crystals/letters.cp314t-win_amd64.pyd +0 -0
- sage/combinat/crystals/letters.pxd +79 -0
- sage/combinat/crystals/letters.pyx +3056 -0
- sage/combinat/crystals/littelmann_path.py +1518 -0
- sage/combinat/crystals/monomial_crystals.py +1262 -0
- sage/combinat/crystals/multisegments.py +462 -0
- sage/combinat/crystals/mv_polytopes.py +467 -0
- sage/combinat/crystals/pbw_crystal.py +511 -0
- sage/combinat/crystals/pbw_datum.cp314t-win_amd64.pyd +0 -0
- sage/combinat/crystals/pbw_datum.pxd +4 -0
- sage/combinat/crystals/pbw_datum.pyx +487 -0
- sage/combinat/crystals/polyhedral_realization.py +372 -0
- sage/combinat/crystals/spins.cp314t-win_amd64.pyd +0 -0
- sage/combinat/crystals/spins.pxd +21 -0
- sage/combinat/crystals/spins.pyx +756 -0
- sage/combinat/crystals/star_crystal.py +290 -0
- sage/combinat/crystals/subcrystal.py +464 -0
- sage/combinat/crystals/tensor_product.py +1177 -0
- sage/combinat/crystals/tensor_product_element.cp314t-win_amd64.pyd +0 -0
- sage/combinat/crystals/tensor_product_element.pxd +35 -0
- sage/combinat/crystals/tensor_product_element.pyx +1870 -0
- sage/combinat/crystals/virtual_crystal.py +420 -0
- sage/combinat/cyclic_sieving_phenomenon.py +204 -0
- sage/combinat/debruijn_sequence.cp314t-win_amd64.pyd +0 -0
- sage/combinat/debruijn_sequence.pyx +355 -0
- sage/combinat/decorated_permutation.py +270 -0
- sage/combinat/degree_sequences.cp314t-win_amd64.pyd +0 -0
- sage/combinat/degree_sequences.pyx +588 -0
- sage/combinat/derangements.py +527 -0
- sage/combinat/descent_algebra.py +1008 -0
- sage/combinat/diagram.py +1551 -0
- sage/combinat/diagram_algebras.py +5886 -0
- sage/combinat/dyck_word.py +4349 -0
- sage/combinat/e_one_star.py +1623 -0
- sage/combinat/enumerated_sets.py +123 -0
- sage/combinat/expnums.cp314t-win_amd64.pyd +0 -0
- sage/combinat/expnums.pyx +148 -0
- sage/combinat/fast_vector_partitions.cp314t-win_amd64.pyd +0 -0
- sage/combinat/fast_vector_partitions.pyx +346 -0
- sage/combinat/fqsym.py +1977 -0
- sage/combinat/free_dendriform_algebra.py +954 -0
- sage/combinat/free_prelie_algebra.py +1141 -0
- sage/combinat/fully_commutative_elements.py +1077 -0
- sage/combinat/fully_packed_loop.py +1523 -0
- sage/combinat/gelfand_tsetlin_patterns.py +1409 -0
- sage/combinat/gray_codes.py +311 -0
- sage/combinat/grossman_larson_algebras.py +667 -0
- sage/combinat/growth.py +4352 -0
- sage/combinat/hall_polynomial.py +188 -0
- sage/combinat/hillman_grassl.py +866 -0
- sage/combinat/integer_matrices.py +329 -0
- sage/combinat/integer_vectors_mod_permgroup.py +1238 -0
- sage/combinat/k_tableau.py +4564 -0
- sage/combinat/kazhdan_lusztig.py +215 -0
- sage/combinat/key_polynomial.py +885 -0
- sage/combinat/knutson_tao_puzzles.py +2286 -0
- sage/combinat/lr_tableau.py +311 -0
- sage/combinat/matrices/all.py +24 -0
- sage/combinat/matrices/hadamard_matrix.py +3790 -0
- sage/combinat/matrices/latin.py +2912 -0
- sage/combinat/misc.py +401 -0
- sage/combinat/multiset_partition_into_sets_ordered.py +3541 -0
- sage/combinat/ncsf_qsym/all.py +21 -0
- sage/combinat/ncsf_qsym/combinatorics.py +317 -0
- sage/combinat/ncsf_qsym/generic_basis_code.py +1427 -0
- sage/combinat/ncsf_qsym/ncsf.py +5637 -0
- sage/combinat/ncsf_qsym/qsym.py +4053 -0
- sage/combinat/ncsf_qsym/tutorial.py +447 -0
- sage/combinat/ncsym/all.py +21 -0
- sage/combinat/ncsym/bases.py +855 -0
- sage/combinat/ncsym/dual.py +593 -0
- sage/combinat/ncsym/ncsym.py +2076 -0
- sage/combinat/necklace.py +551 -0
- sage/combinat/non_decreasing_parking_function.py +634 -0
- sage/combinat/nu_dyck_word.py +1474 -0
- sage/combinat/output.py +861 -0
- sage/combinat/parallelogram_polyomino.py +4326 -0
- sage/combinat/parking_functions.py +1602 -0
- sage/combinat/partition_algebra.py +1998 -0
- sage/combinat/partition_kleshchev.py +1982 -0
- sage/combinat/partition_shifting_algebras.py +584 -0
- sage/combinat/partition_tuple.py +3114 -0
- sage/combinat/path_tableaux/all.py +13 -0
- sage/combinat/path_tableaux/catalog.py +29 -0
- sage/combinat/path_tableaux/dyck_path.py +380 -0
- sage/combinat/path_tableaux/frieze.py +476 -0
- sage/combinat/path_tableaux/path_tableau.py +728 -0
- sage/combinat/path_tableaux/semistandard.py +510 -0
- sage/combinat/perfect_matching.py +779 -0
- sage/combinat/plane_partition.py +3300 -0
- sage/combinat/q_bernoulli.cp314t-win_amd64.pyd +0 -0
- sage/combinat/q_bernoulli.pyx +128 -0
- sage/combinat/quickref.py +81 -0
- sage/combinat/recognizable_series.py +2051 -0
- sage/combinat/regular_sequence.py +4316 -0
- sage/combinat/regular_sequence_bounded.py +543 -0
- sage/combinat/restricted_growth.py +81 -0
- sage/combinat/ribbon.py +20 -0
- sage/combinat/ribbon_shaped_tableau.py +489 -0
- sage/combinat/ribbon_tableau.py +1180 -0
- sage/combinat/rigged_configurations/all.py +46 -0
- sage/combinat/rigged_configurations/bij_abstract_class.py +548 -0
- sage/combinat/rigged_configurations/bij_infinity.py +370 -0
- sage/combinat/rigged_configurations/bij_type_A.py +163 -0
- sage/combinat/rigged_configurations/bij_type_A2_dual.py +338 -0
- sage/combinat/rigged_configurations/bij_type_A2_even.py +218 -0
- sage/combinat/rigged_configurations/bij_type_A2_odd.py +199 -0
- sage/combinat/rigged_configurations/bij_type_B.py +900 -0
- sage/combinat/rigged_configurations/bij_type_C.py +267 -0
- sage/combinat/rigged_configurations/bij_type_D.py +771 -0
- sage/combinat/rigged_configurations/bij_type_D_tri.py +392 -0
- sage/combinat/rigged_configurations/bij_type_D_twisted.py +576 -0
- sage/combinat/rigged_configurations/bij_type_E67.py +402 -0
- sage/combinat/rigged_configurations/bijection.py +143 -0
- sage/combinat/rigged_configurations/kleber_tree.py +1475 -0
- sage/combinat/rigged_configurations/kr_tableaux.py +1898 -0
- sage/combinat/rigged_configurations/rc_crystal.py +461 -0
- sage/combinat/rigged_configurations/rc_infinity.py +540 -0
- sage/combinat/rigged_configurations/rigged_configuration_element.py +2403 -0
- sage/combinat/rigged_configurations/rigged_configurations.py +1918 -0
- sage/combinat/rigged_configurations/rigged_partition.cp314t-win_amd64.pyd +0 -0
- sage/combinat/rigged_configurations/rigged_partition.pxd +15 -0
- sage/combinat/rigged_configurations/rigged_partition.pyx +680 -0
- sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +499 -0
- sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py +428 -0
- sage/combinat/rsk.py +3438 -0
- sage/combinat/schubert_polynomial.py +508 -0
- sage/combinat/set_partition.py +3318 -0
- sage/combinat/set_partition_iterator.cp314t-win_amd64.pyd +0 -0
- sage/combinat/set_partition_iterator.pyx +136 -0
- sage/combinat/set_partition_ordered.py +1590 -0
- sage/combinat/sf/abreu_nigro.py +346 -0
- sage/combinat/sf/all.py +52 -0
- sage/combinat/sf/character.py +576 -0
- sage/combinat/sf/classical.py +319 -0
- sage/combinat/sf/dual.py +996 -0
- sage/combinat/sf/elementary.py +549 -0
- sage/combinat/sf/hall_littlewood.py +1028 -0
- sage/combinat/sf/hecke.py +336 -0
- sage/combinat/sf/homogeneous.py +464 -0
- sage/combinat/sf/jack.py +1428 -0
- sage/combinat/sf/k_dual.py +1458 -0
- sage/combinat/sf/kfpoly.py +447 -0
- sage/combinat/sf/llt.py +789 -0
- sage/combinat/sf/macdonald.py +2019 -0
- sage/combinat/sf/monomial.py +525 -0
- sage/combinat/sf/multiplicative.py +113 -0
- sage/combinat/sf/new_kschur.py +1786 -0
- sage/combinat/sf/ns_macdonald.py +964 -0
- sage/combinat/sf/orthogonal.py +246 -0
- sage/combinat/sf/orthotriang.py +355 -0
- sage/combinat/sf/powersum.py +963 -0
- sage/combinat/sf/schur.py +880 -0
- sage/combinat/sf/sf.py +1653 -0
- sage/combinat/sf/sfa.py +7053 -0
- sage/combinat/sf/symplectic.py +253 -0
- sage/combinat/sf/witt.py +721 -0
- sage/combinat/shifted_primed_tableau.py +2735 -0
- sage/combinat/shuffle.py +830 -0
- sage/combinat/sidon_sets.py +146 -0
- sage/combinat/similarity_class_type.py +1721 -0
- sage/combinat/sine_gordon.py +618 -0
- sage/combinat/six_vertex_model.py +784 -0
- sage/combinat/skew_partition.py +2053 -0
- sage/combinat/skew_tableau.py +2989 -0
- sage/combinat/sloane_functions.py +8935 -0
- sage/combinat/specht_module.py +1403 -0
- sage/combinat/species/all.py +48 -0
- sage/combinat/species/characteristic_species.py +321 -0
- sage/combinat/species/composition_species.py +273 -0
- sage/combinat/species/cycle_species.py +284 -0
- sage/combinat/species/empty_species.py +155 -0
- sage/combinat/species/functorial_composition_species.py +148 -0
- sage/combinat/species/generating_series.py +673 -0
- sage/combinat/species/library.py +148 -0
- sage/combinat/species/linear_order_species.py +169 -0
- sage/combinat/species/misc.py +83 -0
- sage/combinat/species/partition_species.py +290 -0
- sage/combinat/species/permutation_species.py +268 -0
- sage/combinat/species/product_species.py +423 -0
- sage/combinat/species/recursive_species.py +476 -0
- sage/combinat/species/set_species.py +192 -0
- sage/combinat/species/species.py +820 -0
- sage/combinat/species/structure.py +539 -0
- sage/combinat/species/subset_species.py +243 -0
- sage/combinat/species/sum_species.py +225 -0
- sage/combinat/subword.py +564 -0
- sage/combinat/subword_complex.py +2122 -0
- sage/combinat/subword_complex_c.cp314t-win_amd64.pyd +0 -0
- sage/combinat/subword_complex_c.pyx +119 -0
- sage/combinat/super_tableau.py +821 -0
- sage/combinat/superpartition.py +1154 -0
- sage/combinat/symmetric_group_algebra.py +3774 -0
- sage/combinat/symmetric_group_representations.py +1830 -0
- sage/combinat/t_sequences.py +877 -0
- sage/combinat/tableau.py +9506 -0
- sage/combinat/tableau_residues.py +860 -0
- sage/combinat/tableau_tuple.py +5353 -0
- sage/combinat/tiling.py +2432 -0
- sage/combinat/triangles_FHM.py +777 -0
- sage/combinat/tutorial.py +1857 -0
- sage/combinat/vector_partition.py +337 -0
- sage/combinat/words/abstract_word.py +1722 -0
- sage/combinat/words/all.py +59 -0
- sage/combinat/words/alphabet.py +268 -0
- sage/combinat/words/finite_word.py +7201 -0
- sage/combinat/words/infinite_word.py +113 -0
- sage/combinat/words/lyndon_word.py +652 -0
- sage/combinat/words/morphic.py +351 -0
- sage/combinat/words/morphism.py +3878 -0
- sage/combinat/words/paths.py +2932 -0
- sage/combinat/words/shuffle_product.py +278 -0
- sage/combinat/words/suffix_trees.py +1873 -0
- sage/combinat/words/word.py +769 -0
- sage/combinat/words/word_char.cp314t-win_amd64.pyd +0 -0
- sage/combinat/words/word_char.pyx +847 -0
- sage/combinat/words/word_datatypes.cp314t-win_amd64.pyd +0 -0
- sage/combinat/words/word_datatypes.pxd +4 -0
- sage/combinat/words/word_datatypes.pyx +1067 -0
- sage/combinat/words/word_generators.py +2026 -0
- sage/combinat/words/word_infinite_datatypes.py +1218 -0
- sage/combinat/words/word_options.py +99 -0
- sage/combinat/words/words.py +2396 -0
- sage/data_structures/all__sagemath_combinat.py +1 -0
- sage/databases/all__sagemath_combinat.py +13 -0
- sage/databases/findstat.py +4897 -0
- sage/databases/oeis.py +2058 -0
- sage/databases/sloane.py +393 -0
- sage/dynamics/all__sagemath_combinat.py +14 -0
- sage/dynamics/cellular_automata/all.py +7 -0
- sage/dynamics/cellular_automata/catalog.py +34 -0
- sage/dynamics/cellular_automata/elementary.py +612 -0
- sage/dynamics/cellular_automata/glca.py +477 -0
- sage/dynamics/cellular_automata/solitons.py +1463 -0
- sage/dynamics/finite_dynamical_system.py +1249 -0
- sage/dynamics/finite_dynamical_system_catalog.py +382 -0
- sage/games/all.py +7 -0
- sage/games/hexad.py +704 -0
- sage/games/quantumino.py +591 -0
- sage/games/sudoku.py +889 -0
- sage/games/sudoku_backtrack.cp314t-win_amd64.pyd +0 -0
- sage/games/sudoku_backtrack.pyx +189 -0
- sage/groups/all__sagemath_combinat.py +1 -0
- sage/groups/indexed_free_group.py +489 -0
- sage/libs/all__sagemath_combinat.py +6 -0
- sage/libs/lrcalc/__init__.py +1 -0
- sage/libs/lrcalc/lrcalc.py +525 -0
- sage/libs/symmetrica/__init__.py +7 -0
- sage/libs/symmetrica/all.py +101 -0
- sage/libs/symmetrica/kostka.pxi +168 -0
- sage/libs/symmetrica/part.pxi +193 -0
- sage/libs/symmetrica/plet.pxi +42 -0
- sage/libs/symmetrica/sab.pxi +196 -0
- sage/libs/symmetrica/sb.pxi +332 -0
- sage/libs/symmetrica/sc.pxi +192 -0
- sage/libs/symmetrica/schur.pxi +956 -0
- sage/libs/symmetrica/symmetrica.cp314t-win_amd64.pyd +0 -0
- sage/libs/symmetrica/symmetrica.pxi +1172 -0
- sage/libs/symmetrica/symmetrica.pyx +39 -0
- sage/monoids/all.py +13 -0
- sage/monoids/automatic_semigroup.py +1054 -0
- sage/monoids/free_abelian_monoid.py +315 -0
- sage/monoids/free_abelian_monoid_element.cp314t-win_amd64.pyd +0 -0
- sage/monoids/free_abelian_monoid_element.pxd +16 -0
- sage/monoids/free_abelian_monoid_element.pyx +397 -0
- sage/monoids/free_monoid.py +335 -0
- sage/monoids/free_monoid_element.py +431 -0
- sage/monoids/hecke_monoid.py +65 -0
- sage/monoids/string_monoid.py +817 -0
- sage/monoids/string_monoid_element.py +547 -0
- sage/monoids/string_ops.py +143 -0
- sage/monoids/trace_monoid.py +972 -0
- sage/rings/all__sagemath_combinat.py +2 -0
- sage/sat/all.py +4 -0
- sage/sat/boolean_polynomials.py +405 -0
- sage/sat/converters/__init__.py +6 -0
- sage/sat/converters/anf2cnf.py +14 -0
- sage/sat/converters/polybori.py +611 -0
- sage/sat/solvers/__init__.py +5 -0
- sage/sat/solvers/cryptominisat.py +287 -0
- sage/sat/solvers/dimacs.py +783 -0
- sage/sat/solvers/picosat.py +228 -0
- sage/sat/solvers/sat_lp.py +156 -0
- sage/sat/solvers/satsolver.cp314t-win_amd64.pyd +0 -0
- sage/sat/solvers/satsolver.pxd +3 -0
- sage/sat/solvers/satsolver.pyx +405 -0
|
@@ -0,0 +1,3300 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-combinat
|
|
2
|
+
r"""
|
|
3
|
+
Plane partitions
|
|
4
|
+
|
|
5
|
+
AUTHORS:
|
|
6
|
+
|
|
7
|
+
- Jang Soo Kim (2016): initial implementation
|
|
8
|
+
- Jessica Striker (2016): added additional methods
|
|
9
|
+
- Kevin Dilks (2021): added symmetry classes
|
|
10
|
+
"""
|
|
11
|
+
# ****************************************************************************
|
|
12
|
+
# Copyright (C) 2016 Jang Soo Kim <jangsookim@skku.edu>,
|
|
13
|
+
# 2016 Jessica Striker <jessicapalencia@gmail.com>
|
|
14
|
+
# 2021 Kevin Dilks <kdilks@gmail.com>
|
|
15
|
+
#
|
|
16
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
17
|
+
#
|
|
18
|
+
# This code is distributed in the hope that it will be useful, but
|
|
19
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
20
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
21
|
+
# General Public License for more details.
|
|
22
|
+
#
|
|
23
|
+
# The full text of the GPL is available at:
|
|
24
|
+
#
|
|
25
|
+
# https://www.gnu.org/licenses/
|
|
26
|
+
# ****************************************************************************
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
from typing import NewType
|
|
30
|
+
from collections.abc import Iterator
|
|
31
|
+
|
|
32
|
+
from sage.structure.richcmp import richcmp, richcmp_method
|
|
33
|
+
from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
|
|
34
|
+
from sage.combinat.tableau import Tableau
|
|
35
|
+
from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass
|
|
36
|
+
from sage.misc.lazy_import import lazy_import
|
|
37
|
+
from sage.misc.misc_c import prod
|
|
38
|
+
from sage.rings.integer import Integer
|
|
39
|
+
from sage.structure.list_clone import ClonableArray
|
|
40
|
+
from sage.structure.parent import Parent
|
|
41
|
+
from sage.structure.unique_representation import UniqueRepresentation
|
|
42
|
+
from sage.rings.integer_ring import ZZ
|
|
43
|
+
from sage.arith.misc import Sigma, binomial, factorial
|
|
44
|
+
from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets
|
|
45
|
+
from sage.sets.family import Family
|
|
46
|
+
from sage.sets.non_negative_integers import NonNegativeIntegers
|
|
47
|
+
|
|
48
|
+
lazy_import('sage.modules.free_module_element', 'vector')
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@richcmp_method
|
|
52
|
+
class PlanePartition(ClonableArray,
|
|
53
|
+
metaclass=InheritComparisonClasscallMetaclass):
|
|
54
|
+
r"""
|
|
55
|
+
A plane partition.
|
|
56
|
+
|
|
57
|
+
A *plane partition* is a stack of cubes in the positive orthant.
|
|
58
|
+
|
|
59
|
+
INPUT:
|
|
60
|
+
|
|
61
|
+
- ``PP`` -- list of lists which represents a tableau
|
|
62
|
+
- ``box_size`` -- (optional) a list ``[A, B, C]`` of 3 positive integers,
|
|
63
|
+
where ``A``, ``B``, ``C`` are the lengths of the box in the `x`-axis,
|
|
64
|
+
`y`-axis, `z`-axis, respectively; if this is not given, it is
|
|
65
|
+
determined by the smallest box bounding ``PP``
|
|
66
|
+
|
|
67
|
+
OUTPUT: the plane partition whose tableau representation is ``PP``
|
|
68
|
+
|
|
69
|
+
EXAMPLES::
|
|
70
|
+
|
|
71
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
72
|
+
sage: PP
|
|
73
|
+
Plane partition [[4, 3, 3, 1], [2, 1, 1], [1, 1]]
|
|
74
|
+
|
|
75
|
+
TESTS::
|
|
76
|
+
|
|
77
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
78
|
+
sage: TestSuite(PP).run()
|
|
79
|
+
sage: hash(PP) # random
|
|
80
|
+
"""
|
|
81
|
+
@staticmethod
|
|
82
|
+
def __classcall_private__(cls, PP, box_size=None):
|
|
83
|
+
"""
|
|
84
|
+
Construct a plane partition with the appropriate parent.
|
|
85
|
+
|
|
86
|
+
EXAMPLES::
|
|
87
|
+
|
|
88
|
+
sage: p = PlanePartition([[2,1],[1]])
|
|
89
|
+
sage: TestSuite(p).run()
|
|
90
|
+
|
|
91
|
+
sage: p.parent()
|
|
92
|
+
Plane partitions
|
|
93
|
+
sage: p.category()
|
|
94
|
+
Category of elements of Plane partitions
|
|
95
|
+
sage: type(p)
|
|
96
|
+
<class 'sage.combinat.plane_partition.PlanePartitions_all_with_category.element_class'>
|
|
97
|
+
"""
|
|
98
|
+
if isinstance(PP, PlanePartition) and box_size is None:
|
|
99
|
+
return PP
|
|
100
|
+
pp = PlanePartitions(box_size=box_size)
|
|
101
|
+
return pp.element_class(pp, PP) # The check() will raise the appropriate error
|
|
102
|
+
|
|
103
|
+
def __init__(self, parent, pp, check=True):
|
|
104
|
+
r"""
|
|
105
|
+
Initialize a plane partition.
|
|
106
|
+
|
|
107
|
+
TESTS::
|
|
108
|
+
|
|
109
|
+
sage: a = PlanePartitions()([[2,1],[1]])
|
|
110
|
+
sage: b = PlanePartitions([2,2,2])([[2,1],[1]])
|
|
111
|
+
sage: c = PlanePartitions(4)([[2,1],[1]])
|
|
112
|
+
sage: a == b
|
|
113
|
+
True
|
|
114
|
+
sage: a is b
|
|
115
|
+
False
|
|
116
|
+
sage: a == c
|
|
117
|
+
True
|
|
118
|
+
sage: a is c
|
|
119
|
+
False
|
|
120
|
+
"""
|
|
121
|
+
if isinstance(pp, PlanePartition):
|
|
122
|
+
ClonableArray.__init__(self, parent, pp, check=False)
|
|
123
|
+
else:
|
|
124
|
+
pp = [list(row) for row in pp]
|
|
125
|
+
if pp:
|
|
126
|
+
for i in reversed(range(len(pp))):
|
|
127
|
+
while pp[i] and not pp[i][-1]:
|
|
128
|
+
del pp[i][-1]
|
|
129
|
+
if not pp[i]:
|
|
130
|
+
pp.pop(i)
|
|
131
|
+
pp = [tuple(row) for row in pp]
|
|
132
|
+
ClonableArray.__init__(self, parent, pp, check=check)
|
|
133
|
+
if self.parent()._box is None:
|
|
134
|
+
if pp:
|
|
135
|
+
self._max_x = len(pp)
|
|
136
|
+
self._max_y = len(pp[0])
|
|
137
|
+
self._max_z = pp[0][0]
|
|
138
|
+
else:
|
|
139
|
+
self._max_x = 0
|
|
140
|
+
self._max_y = 0
|
|
141
|
+
self._max_z = 0
|
|
142
|
+
else:
|
|
143
|
+
(self._max_x, self._max_y, self._max_z) = self.parent()._box
|
|
144
|
+
|
|
145
|
+
def __richcmp__(self, other, op):
|
|
146
|
+
r"""
|
|
147
|
+
Compare ``self`` to ``other``.
|
|
148
|
+
|
|
149
|
+
.. TODO::
|
|
150
|
+
|
|
151
|
+
This overwrites the comparison check of
|
|
152
|
+
:class:`~sage.structure.list_clone.ClonableArray`
|
|
153
|
+
in order to circumvent the coercion framework.
|
|
154
|
+
Eventually this should be solved more elegantly,
|
|
155
|
+
for example along the lines of what was done for
|
|
156
|
+
`k`-tableaux.
|
|
157
|
+
|
|
158
|
+
For now, this compares two elements by their underlying
|
|
159
|
+
defining lists.
|
|
160
|
+
|
|
161
|
+
INPUT:
|
|
162
|
+
|
|
163
|
+
- ``other`` -- the element that ``self`` is compared to
|
|
164
|
+
|
|
165
|
+
OUTPUT: boolean
|
|
166
|
+
|
|
167
|
+
TESTS::
|
|
168
|
+
|
|
169
|
+
sage: t = PlanePartition([[2,1],[1]])
|
|
170
|
+
sage: t == 0
|
|
171
|
+
False
|
|
172
|
+
sage: t == PlanePartitions(4)([[2,1],[1]])
|
|
173
|
+
True
|
|
174
|
+
|
|
175
|
+
sage: s = PlanePartition([[3,1],[1]])
|
|
176
|
+
sage: s != []
|
|
177
|
+
True
|
|
178
|
+
|
|
179
|
+
sage: t < s
|
|
180
|
+
True
|
|
181
|
+
sage: s < t
|
|
182
|
+
False
|
|
183
|
+
sage: s > t
|
|
184
|
+
True
|
|
185
|
+
"""
|
|
186
|
+
if isinstance(other, PlanePartition):
|
|
187
|
+
return self._richcmp_(other, op)
|
|
188
|
+
|
|
189
|
+
return richcmp(list(self), other, op)
|
|
190
|
+
|
|
191
|
+
def check(self):
|
|
192
|
+
"""
|
|
193
|
+
Check to see that ``self`` is a valid plane partition.
|
|
194
|
+
|
|
195
|
+
EXAMPLES::
|
|
196
|
+
|
|
197
|
+
sage: a = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
198
|
+
sage: a.check()
|
|
199
|
+
sage: b = PlanePartition([[1,2],[1]])
|
|
200
|
+
Traceback (most recent call last):
|
|
201
|
+
...
|
|
202
|
+
ValueError: not weakly decreasing along rows
|
|
203
|
+
sage: c = PlanePartition([[1,1],[2]])
|
|
204
|
+
Traceback (most recent call last):
|
|
205
|
+
...
|
|
206
|
+
ValueError: not weakly decreasing along columns
|
|
207
|
+
sage: d = PlanePartition([[2,-1],[-2]])
|
|
208
|
+
Traceback (most recent call last):
|
|
209
|
+
...
|
|
210
|
+
ValueError: entries not all nonnegative
|
|
211
|
+
sage: e = PlanePartition([[3/2,1],[.5]])
|
|
212
|
+
Traceback (most recent call last):
|
|
213
|
+
...
|
|
214
|
+
ValueError: entries not all integers
|
|
215
|
+
"""
|
|
216
|
+
if not all(a in ZZ for b in self for a in b):
|
|
217
|
+
raise ValueError("entries not all integers")
|
|
218
|
+
for row in self:
|
|
219
|
+
if not all(c >= 0 for c in row):
|
|
220
|
+
raise ValueError("entries not all nonnegative")
|
|
221
|
+
if not all(row[i] >= row[i+1] for i in range(len(row)-1)):
|
|
222
|
+
raise ValueError("not weakly decreasing along rows")
|
|
223
|
+
for row, next in zip(self, self[1:]):
|
|
224
|
+
if not all(row[c] >= next[c] for c in range(len(next))):
|
|
225
|
+
raise ValueError("not weakly decreasing along columns")
|
|
226
|
+
|
|
227
|
+
def _repr_(self) -> str:
|
|
228
|
+
"""
|
|
229
|
+
Return a string representation of ``self``.
|
|
230
|
+
|
|
231
|
+
EXAMPLES::
|
|
232
|
+
|
|
233
|
+
sage: PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
234
|
+
Plane partition [[4, 3, 3, 1], [2, 1, 1], [1, 1]]
|
|
235
|
+
"""
|
|
236
|
+
return "Plane partition {}".format([list(row) for row in self])
|
|
237
|
+
|
|
238
|
+
def to_tableau(self) -> Tableau:
|
|
239
|
+
r"""
|
|
240
|
+
Return the tableau class of ``self``.
|
|
241
|
+
|
|
242
|
+
EXAMPLES::
|
|
243
|
+
|
|
244
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
245
|
+
sage: PP.to_tableau()
|
|
246
|
+
[[4, 3, 3, 1], [2, 1, 1], [1, 1]]
|
|
247
|
+
"""
|
|
248
|
+
return Tableau(self) # type:ignore
|
|
249
|
+
|
|
250
|
+
def z_tableau(self, tableau=True) -> Tableau:
|
|
251
|
+
r"""
|
|
252
|
+
Return the projection of ``self`` in the `z` direction.
|
|
253
|
+
|
|
254
|
+
If ``tableau`` is set to ``False``, then only the list of lists
|
|
255
|
+
consisting of the projection of boxes size onto the `xy`-plane
|
|
256
|
+
is returned instead of a :class:`Tableau` object. This output will
|
|
257
|
+
not have empty trailing rows or trailing zeros removed.
|
|
258
|
+
|
|
259
|
+
EXAMPLES::
|
|
260
|
+
|
|
261
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
262
|
+
sage: PP.z_tableau()
|
|
263
|
+
[[4, 3, 3, 1], [2, 1, 1, 0], [1, 1, 0, 0]]
|
|
264
|
+
"""
|
|
265
|
+
Z = [[0 for i in range(self._max_y)] for j in range(self._max_x)]
|
|
266
|
+
for C in self.cells():
|
|
267
|
+
Z[C[0]][C[1]] += 1
|
|
268
|
+
if tableau:
|
|
269
|
+
return Tableau(Z)
|
|
270
|
+
return Z
|
|
271
|
+
|
|
272
|
+
def y_tableau(self, tableau=True) -> Tableau:
|
|
273
|
+
r"""
|
|
274
|
+
Return the projection of ``self`` in the `y` direction.
|
|
275
|
+
|
|
276
|
+
If ``tableau`` is set to ``False``, then only the list of lists
|
|
277
|
+
consisting of the projection of boxes size onto the `xz`-plane
|
|
278
|
+
is returned instead of a :class:`Tableau` object. This output will
|
|
279
|
+
not have empty trailing rows or trailing zeros removed.
|
|
280
|
+
|
|
281
|
+
EXAMPLES::
|
|
282
|
+
|
|
283
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
284
|
+
sage: PP.y_tableau()
|
|
285
|
+
[[4, 3, 2], [3, 1, 0], [3, 0, 0], [1, 0, 0]]
|
|
286
|
+
"""
|
|
287
|
+
Y = [[0 for i in range(self._max_x)] for j in range(self._max_z)]
|
|
288
|
+
for C in self.cells():
|
|
289
|
+
Y[C[2]][C[0]] += 1
|
|
290
|
+
if tableau:
|
|
291
|
+
return Tableau(Y)
|
|
292
|
+
return Y
|
|
293
|
+
|
|
294
|
+
def x_tableau(self, tableau=True) -> Tableau:
|
|
295
|
+
r"""
|
|
296
|
+
Return the projection of ``self`` in the `x` direction.
|
|
297
|
+
|
|
298
|
+
If ``tableau`` is set to ``False``, then only the list of lists
|
|
299
|
+
consisting of the projection of boxes size onto the `yz`-plane
|
|
300
|
+
is returned instead of a :class:`Tableau` object. This output will
|
|
301
|
+
not have empty trailing rows or trailing zeros removed.
|
|
302
|
+
|
|
303
|
+
EXAMPLES::
|
|
304
|
+
|
|
305
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
306
|
+
sage: PP.x_tableau()
|
|
307
|
+
[[3, 2, 1, 1], [3, 1, 1, 0], [2, 1, 1, 0], [1, 0, 0, 0]]
|
|
308
|
+
"""
|
|
309
|
+
X = [[0 for i in range(self._max_z)] for j in range(self._max_y)]
|
|
310
|
+
for C in self.cells():
|
|
311
|
+
X[C[1]][C[2]] += 1
|
|
312
|
+
if tableau:
|
|
313
|
+
return Tableau(X)
|
|
314
|
+
return X
|
|
315
|
+
|
|
316
|
+
def cells(self) -> list[tuple[int, int, int]]:
|
|
317
|
+
r"""
|
|
318
|
+
Return the list of cells inside ``self``.
|
|
319
|
+
|
|
320
|
+
Each cell is a tuple.
|
|
321
|
+
|
|
322
|
+
EXAMPLES::
|
|
323
|
+
|
|
324
|
+
sage: PP = PlanePartition([[3,1],[2]])
|
|
325
|
+
sage: PP.cells()
|
|
326
|
+
[(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 1, 0), (1, 0, 0), (1, 0, 1)]
|
|
327
|
+
"""
|
|
328
|
+
return [(r, c, h)
|
|
329
|
+
for r in range(len(self))
|
|
330
|
+
for c in range(len(self[r]))
|
|
331
|
+
for h in range(self[r][c])]
|
|
332
|
+
|
|
333
|
+
def number_of_boxes(self) -> Integer:
|
|
334
|
+
r"""
|
|
335
|
+
Return the number of boxes in the plane partition.
|
|
336
|
+
|
|
337
|
+
EXAMPLES::
|
|
338
|
+
|
|
339
|
+
sage: PP = PlanePartition([[3,1],[2]])
|
|
340
|
+
sage: PP.number_of_boxes()
|
|
341
|
+
6
|
|
342
|
+
"""
|
|
343
|
+
return sum(sum(row) for row in self)
|
|
344
|
+
|
|
345
|
+
def _repr_diagram(self, show_box=False, use_unicode=False) -> str:
|
|
346
|
+
r"""
|
|
347
|
+
Return a string of the 3D diagram of ``self``.
|
|
348
|
+
|
|
349
|
+
INPUT:
|
|
350
|
+
|
|
351
|
+
- ``show_box`` -- boolean (default: ``False``); if ``True``,
|
|
352
|
+
also shows the visible tiles on the `xy`-, `yz`-, `zx`-planes
|
|
353
|
+
- ``use_unicode`` -- boolean (default: ``False``); use unicode
|
|
354
|
+
|
|
355
|
+
OUTPUT: string of the 3D diagram of the plane partition
|
|
356
|
+
|
|
357
|
+
EXAMPLES::
|
|
358
|
+
|
|
359
|
+
sage: print(PlanePartition([[4,3,3,1],[2,1,1],[1,1]])._repr_diagram())
|
|
360
|
+
__
|
|
361
|
+
/\_\
|
|
362
|
+
__/\/_/
|
|
363
|
+
__/\_\/\_\
|
|
364
|
+
/\_\/_/\/\_\
|
|
365
|
+
\/\_\_\/\/_/
|
|
366
|
+
\/_/\_\/_/
|
|
367
|
+
\/_/\_\
|
|
368
|
+
\/_/
|
|
369
|
+
sage: print(PlanePartition([[4,3,3,1],[2,1,1],[1,1]])._repr_diagram(True))
|
|
370
|
+
______
|
|
371
|
+
/_/_/\_\
|
|
372
|
+
/_/_/\/_/\
|
|
373
|
+
/_/\_\/\_\/\
|
|
374
|
+
/\_\/_/\/\_\/\
|
|
375
|
+
\/\_\_\/\/_/\/
|
|
376
|
+
\/_/\_\/_/\/
|
|
377
|
+
\_\/_/\_\/
|
|
378
|
+
\_\_\/_/
|
|
379
|
+
"""
|
|
380
|
+
x = self._max_x
|
|
381
|
+
y = self._max_y
|
|
382
|
+
z = self._max_z
|
|
383
|
+
|
|
384
|
+
drawing = [[" " for i in range(2 * x + y + z)]
|
|
385
|
+
for j in range(y + z + 1)]
|
|
386
|
+
|
|
387
|
+
hori = "_" if use_unicode else "_"
|
|
388
|
+
down = "╲" if use_unicode else "\\"
|
|
389
|
+
up = "╱" if use_unicode else "/"
|
|
390
|
+
|
|
391
|
+
def superpose(l, c, letter):
|
|
392
|
+
# add the given letter at line l and column c
|
|
393
|
+
exist = drawing[l][c]
|
|
394
|
+
if exist == " " or exist == "_":
|
|
395
|
+
drawing[l][c] = letter
|
|
396
|
+
|
|
397
|
+
def add_topside(i, j, k):
|
|
398
|
+
X = z + j - k
|
|
399
|
+
Y = 2 * x - 2 * i + j + k
|
|
400
|
+
superpose(X, Y - 2, hori)
|
|
401
|
+
superpose(X, Y - 1, hori)
|
|
402
|
+
superpose(X + 1, Y - 2, down)
|
|
403
|
+
superpose(X + 1, Y - 1, hori)
|
|
404
|
+
superpose(X + 1, Y, down)
|
|
405
|
+
|
|
406
|
+
def add_rightside(i, j, k):
|
|
407
|
+
X = z + j - k
|
|
408
|
+
Y = 2 * x - 2 * i + j + k
|
|
409
|
+
superpose(X - 1, Y - 1, hori)
|
|
410
|
+
superpose(X - 1, Y, hori)
|
|
411
|
+
superpose(X, Y - 2, up)
|
|
412
|
+
superpose(X, Y - 1, hori)
|
|
413
|
+
superpose(X, Y, up)
|
|
414
|
+
|
|
415
|
+
def add_leftside(i, j, k):
|
|
416
|
+
X = z + j - k
|
|
417
|
+
Y = 2 * x - 2 * i + j + k
|
|
418
|
+
superpose(X, Y, up)
|
|
419
|
+
superpose(X, Y + 1, down)
|
|
420
|
+
superpose(X + 1, Y + 1, up)
|
|
421
|
+
superpose(X + 1, Y, down)
|
|
422
|
+
|
|
423
|
+
tab = self.z_tableau()
|
|
424
|
+
for r in range(len(tab)):
|
|
425
|
+
for c in range(len(tab[r])):
|
|
426
|
+
if tab[r][c] > 0 or show_box:
|
|
427
|
+
add_topside(r, c, tab[r][c])
|
|
428
|
+
|
|
429
|
+
tab = self.y_tableau()
|
|
430
|
+
for r in range(len(tab)):
|
|
431
|
+
for c in range(len(tab[r])):
|
|
432
|
+
if self.y_tableau()[r][c] > 0 or show_box:
|
|
433
|
+
add_rightside(c, tab[r][c], r)
|
|
434
|
+
|
|
435
|
+
tab = self.x_tableau()
|
|
436
|
+
for r in range(len(tab)):
|
|
437
|
+
for c in range(len(tab[r])):
|
|
438
|
+
if self.x_tableau()[r][c] > 0 or show_box:
|
|
439
|
+
add_leftside(tab[r][c], r, c)
|
|
440
|
+
|
|
441
|
+
check = not show_box
|
|
442
|
+
while check:
|
|
443
|
+
if drawing and all(char == " " for char in drawing[-1]):
|
|
444
|
+
drawing.pop()
|
|
445
|
+
else:
|
|
446
|
+
check = False
|
|
447
|
+
|
|
448
|
+
if not drawing:
|
|
449
|
+
return "∅" if use_unicode else ""
|
|
450
|
+
|
|
451
|
+
if use_unicode:
|
|
452
|
+
return '\n'.join("".join(row) for row in drawing)
|
|
453
|
+
return '\n'.join("".join(row) for row in drawing)
|
|
454
|
+
|
|
455
|
+
def _ascii_art_(self):
|
|
456
|
+
r"""
|
|
457
|
+
Return an ascii art representation of ``self``.
|
|
458
|
+
|
|
459
|
+
EXAMPLES::
|
|
460
|
+
|
|
461
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
462
|
+
sage: ascii_art(PP)
|
|
463
|
+
__
|
|
464
|
+
/\_\
|
|
465
|
+
__/\/_/
|
|
466
|
+
__/\_\/\_\
|
|
467
|
+
/\_\/_/\/\_\
|
|
468
|
+
\/\_\_\/\/_/
|
|
469
|
+
\/_/\_\/_/
|
|
470
|
+
\/_/\_\
|
|
471
|
+
\/_/
|
|
472
|
+
"""
|
|
473
|
+
from sage.typeset.ascii_art import AsciiArt
|
|
474
|
+
return AsciiArt(self._repr_diagram().splitlines(), baseline=0)
|
|
475
|
+
|
|
476
|
+
def _unicode_art_(self):
|
|
477
|
+
r"""
|
|
478
|
+
Return a unicode representation of ``self``.
|
|
479
|
+
|
|
480
|
+
EXAMPLES::
|
|
481
|
+
|
|
482
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
483
|
+
sage: unicode_art(PP)
|
|
484
|
+
__
|
|
485
|
+
╱╲_╲
|
|
486
|
+
__╱╲╱_╱
|
|
487
|
+
__╱╲_╲╱╲_╲
|
|
488
|
+
╱╲_╲╱_╱╲╱╲_╲
|
|
489
|
+
╲╱╲_╲_╲╱╲╱_╱
|
|
490
|
+
╲╱_╱╲_╲╱_╱
|
|
491
|
+
╲╱_╱╲_╲
|
|
492
|
+
╲╱_╱
|
|
493
|
+
"""
|
|
494
|
+
from sage.typeset.unicode_art import UnicodeArt
|
|
495
|
+
return UnicodeArt(self._repr_diagram(use_unicode=True).splitlines(), baseline=0)
|
|
496
|
+
|
|
497
|
+
def pp(self, show_box=False):
|
|
498
|
+
r"""
|
|
499
|
+
Return a pretty print of the plane partition.
|
|
500
|
+
|
|
501
|
+
INPUT:
|
|
502
|
+
|
|
503
|
+
- ``show_box`` -- boolean (default: ``False``); if ``True``,
|
|
504
|
+
also shows the visible tiles on the `xy`-, `yz`-, `zx`-planes
|
|
505
|
+
|
|
506
|
+
OUTPUT: a pretty print of the plane partition
|
|
507
|
+
|
|
508
|
+
EXAMPLES::
|
|
509
|
+
|
|
510
|
+
sage: PlanePartition([[4,3,3,1],[2,1,1],[1,1]]).pp()
|
|
511
|
+
__
|
|
512
|
+
/\_\
|
|
513
|
+
__/\/_/
|
|
514
|
+
__/\_\/\_\
|
|
515
|
+
/\_\/_/\/\_\
|
|
516
|
+
\/\_\_\/\/_/
|
|
517
|
+
\/_/\_\/_/
|
|
518
|
+
\/_/\_\
|
|
519
|
+
\/_/
|
|
520
|
+
sage: PlanePartition([[4,3,3,1],[2,1,1],[1,1]]).pp(True)
|
|
521
|
+
______
|
|
522
|
+
/_/_/\_\
|
|
523
|
+
/_/_/\/_/\
|
|
524
|
+
/_/\_\/\_\/\
|
|
525
|
+
/\_\/_/\/\_\/\
|
|
526
|
+
\/\_\_\/\/_/\/
|
|
527
|
+
\/_/\_\/_/\/
|
|
528
|
+
\_\/_/\_\/
|
|
529
|
+
\_\_\/_/
|
|
530
|
+
"""
|
|
531
|
+
print(self._repr_diagram(show_box))
|
|
532
|
+
|
|
533
|
+
def _repr_svg_(self) -> str:
|
|
534
|
+
"""
|
|
535
|
+
Return the svg picture of a plane partition.
|
|
536
|
+
|
|
537
|
+
This can be displayed by Jupyter.
|
|
538
|
+
|
|
539
|
+
EXAMPLES::
|
|
540
|
+
|
|
541
|
+
sage: PP = PlanePartition([[2, 1, 1], [1, 1]])
|
|
542
|
+
sage: PP._repr_svg_() # needs sage.modules
|
|
543
|
+
'<?xml...</g></svg>'
|
|
544
|
+
"""
|
|
545
|
+
colors = ["snow", "tomato", "steelblue"]
|
|
546
|
+
|
|
547
|
+
resu = '<?xml version=\"1.0\" standalone=\"no\"?>'
|
|
548
|
+
resu += '<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" '
|
|
549
|
+
resu += '\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">'
|
|
550
|
+
resu += '<svg xmlns=\"http://www.w3.org/2000/svg\" '
|
|
551
|
+
resu += 'xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"300\" viewBox='
|
|
552
|
+
|
|
553
|
+
resu1 = '<defs><polygon points=\"0, 0 -0.866, 0.5 0, 1 0.866, 0.5\" '
|
|
554
|
+
resu1 += f'id=\"cz\" style=\"fill:{colors[0]}\"/>'
|
|
555
|
+
resu1 += '<polygon points=\"0, 0 0.866, 0.5 0.866, -0.5 0, -1\" '
|
|
556
|
+
resu1 += f'id=\"cx\" style=\"fill:{colors[1]}\"/>'
|
|
557
|
+
resu1 += '<polygon points=\"0, 0 0, -1 -0.866, -0.5 -0.866, 0.5\" '
|
|
558
|
+
resu1 += f'id=\"cy\" style=\"fill:{colors[2]}\"/></defs>'
|
|
559
|
+
resu1 += '<g style=\"stroke-width:0.01;stroke-linejoin:bevel; '
|
|
560
|
+
resu1 += 'stroke-linecap:butt; stroke:black; fill:red\">'
|
|
561
|
+
|
|
562
|
+
vx = -vector([0.866, -0.5])
|
|
563
|
+
vy = -vector([-0.866, -0.5])
|
|
564
|
+
vz = -vector([0, 1])
|
|
565
|
+
# Since we currently don't display the bounding box, just
|
|
566
|
+
# use the smallest one possible.
|
|
567
|
+
Nx, Ny, Nz = self.bounding_box()
|
|
568
|
+
|
|
569
|
+
resu += '\"%.3f %.3f %.3f %.3f \">' % (-0.866 * Nx, -Nz,
|
|
570
|
+
0.866 * Nx + 0.866 * Ny,
|
|
571
|
+
Nz + 0.5 * (Nx + Ny))
|
|
572
|
+
resu += resu1
|
|
573
|
+
|
|
574
|
+
mat = self.z_tableau()
|
|
575
|
+
for i in range(Nx):
|
|
576
|
+
for j in range(Ny):
|
|
577
|
+
if mat[i][j]:
|
|
578
|
+
v = i * vx + j * vy + mat[i][j] * vz
|
|
579
|
+
resu += '<use transform=\"translate(%.3f, %.3f)' % (v[0], v[1])
|
|
580
|
+
resu += '\" xlink:href=\"#cz\" />'
|
|
581
|
+
|
|
582
|
+
mat = self.y_tableau()
|
|
583
|
+
for j in range(Nz):
|
|
584
|
+
for k in range(Nx):
|
|
585
|
+
if mat[j][k]:
|
|
586
|
+
v = j * vz + k * vx + mat[j][k] * vy
|
|
587
|
+
resu += '<use transform=\"translate(%.3f, %.3f)' % (v[0], v[1])
|
|
588
|
+
resu += '\" xlink:href=\"#cy\" />'
|
|
589
|
+
|
|
590
|
+
mat = self.x_tableau()
|
|
591
|
+
for k in range(Ny):
|
|
592
|
+
for i in range(Nz):
|
|
593
|
+
if mat[k][i]:
|
|
594
|
+
v = k * vy + i * vz + mat[k][i] * vx
|
|
595
|
+
resu += '<use transform=\"translate(%.3f, %.3f)' % (v[0], v[1])
|
|
596
|
+
resu += '\" xlink:href=\"#cx\" />'
|
|
597
|
+
return resu + '</g></svg>'
|
|
598
|
+
|
|
599
|
+
def _latex_(self, show_box=False,
|
|
600
|
+
colors=["white", "lightgray", "darkgray"]) -> str:
|
|
601
|
+
r"""
|
|
602
|
+
Return latex code for ``self``, which uses TikZ package to draw
|
|
603
|
+
the plane partition.
|
|
604
|
+
|
|
605
|
+
INPUT:
|
|
606
|
+
|
|
607
|
+
- ``show_box`` -- boolean (default: ``False``); if ``True``,
|
|
608
|
+
also shows the visible tiles on the `xy`-, `yz`-, `zx`-planes
|
|
609
|
+
- ``colors`` -- (default: ``["white", "lightgray", "darkgray"]``)
|
|
610
|
+
list ``[A, B, C]`` of 3 strings representing colors
|
|
611
|
+
|
|
612
|
+
OUTPUT: latex code for drawing the plane partition
|
|
613
|
+
|
|
614
|
+
EXAMPLES::
|
|
615
|
+
|
|
616
|
+
sage: PP = PlanePartition([[1]])
|
|
617
|
+
sage: latex(PP) # needs sage.graphs
|
|
618
|
+
\begin{tikzpicture}
|
|
619
|
+
\draw[fill=white,shift={(210:0)},shift={(-30:0)},shift={(90:1)}]
|
|
620
|
+
(0,0)--(-30:1)--(0,-1)--(210:1)--(0,0);
|
|
621
|
+
\draw[fill=darkgray,shift={(210:0)},shift={(-30:1)},shift={(90:0)}]
|
|
622
|
+
(0,0)--(210:1)--(150:1)--(0,1)--(0,0);
|
|
623
|
+
\draw[fill=lightgray,shift={(210:1)},shift={(-30:0)},shift={(90:0)}]
|
|
624
|
+
(0,0)--(0,1)--(30:1)--(-30:1)--(0,0);
|
|
625
|
+
\end{tikzpicture}
|
|
626
|
+
"""
|
|
627
|
+
from sage.graphs.graph_latex import setup_latex_preamble
|
|
628
|
+
setup_latex_preamble()
|
|
629
|
+
|
|
630
|
+
ret = "\\begin{tikzpicture}\n"
|
|
631
|
+
|
|
632
|
+
def add_topside(i, j, k):
|
|
633
|
+
return "\\draw[fill={},shift={{(210:{})}},shift={{(-30:{})}},shift={{(90:{})}}]\n(0,0)--(-30:1)--(0,-1)--(210:1)--(0,0);\n".format(colors[0], i, j, k)
|
|
634
|
+
|
|
635
|
+
def add_leftside(j, k, i):
|
|
636
|
+
return "\\draw[fill={},shift={{(210:{})}},shift={{(-30:{})}},shift={{(90:{})}}]\n(0,0)--(0,1)--(30:1)--(-30:1)--(0,0);\n".format(colors[1], i, j, k)
|
|
637
|
+
|
|
638
|
+
def add_rightside(k, i, j):
|
|
639
|
+
return "\\draw[fill={},shift={{(210:{})}},shift={{(-30:{})}},shift={{(90:{})}}]\n(0,0)--(210:1)--(150:1)--(0,1)--(0,0);\n".format(colors[2], i, j, k)
|
|
640
|
+
funcs = [add_topside, add_rightside, add_leftside]
|
|
641
|
+
tableaux = [self.z_tableau(), self.y_tableau(), self.x_tableau()]
|
|
642
|
+
for i in range(3):
|
|
643
|
+
f = funcs[i]
|
|
644
|
+
tab = tableaux[i]
|
|
645
|
+
for r in range(len(tab)):
|
|
646
|
+
for c in range(len(tab[r])):
|
|
647
|
+
if tab[r][c] > 0 or show_box:
|
|
648
|
+
ret += f(r, c, tab[r][c])
|
|
649
|
+
return ret + "\\end{tikzpicture}"
|
|
650
|
+
|
|
651
|
+
def plot(self, show_box=False, colors=None):
|
|
652
|
+
r"""
|
|
653
|
+
Return a plot of ``self``.
|
|
654
|
+
|
|
655
|
+
INPUT:
|
|
656
|
+
|
|
657
|
+
- ``show_box`` -- boolean (default: ``False``); if ``True``,
|
|
658
|
+
also shows the visible tiles on the `xy`-, `yz`-, `zx`-planes
|
|
659
|
+
- ``colors`` -- (default: ``["white", "lightgray", "darkgray"]``)
|
|
660
|
+
list ``[A, B, C]`` of 3 strings representing colors
|
|
661
|
+
|
|
662
|
+
EXAMPLES::
|
|
663
|
+
|
|
664
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
665
|
+
sage: PP.plot() # needs sage.plot sage.symbolic
|
|
666
|
+
Graphics object consisting of 27 graphics primitives
|
|
667
|
+
"""
|
|
668
|
+
from sage.functions.trig import cos, sin
|
|
669
|
+
from sage.plot.polygon import polygon
|
|
670
|
+
from sage.symbolic.constants import pi
|
|
671
|
+
from sage.plot.plot import plot
|
|
672
|
+
if colors is None:
|
|
673
|
+
colors = ["white", "lightgray", "darkgray"]
|
|
674
|
+
Uside = [[0, 0], [cos(-pi / 6), sin(-pi / 6)],
|
|
675
|
+
[0, -1], [cos(7 * pi / 6), sin(7 * pi / 6)]]
|
|
676
|
+
Lside = [[0, 0], [cos(-pi / 6), sin(-pi / 6)],
|
|
677
|
+
[cos(pi / 6), sin(pi / 6)], [0, 1]]
|
|
678
|
+
Rside = [[0, 0], [0, 1], [cos(5 * pi / 6), sin(5 * pi / 6)],
|
|
679
|
+
[cos(7 * pi / 6), sin(7 * pi / 6)]]
|
|
680
|
+
Xdir = [cos(7 * pi / 6), sin(7 * pi / 6)]
|
|
681
|
+
Ydir = [cos(-pi / 6), sin(-pi / 6)]
|
|
682
|
+
Zdir = [0, 1]
|
|
683
|
+
|
|
684
|
+
def move(side, i, j, k):
|
|
685
|
+
return [[P[0] + i * Xdir[0] + j * Ydir[0] + k * Zdir[0],
|
|
686
|
+
P[1] + i * Xdir[1] + j * Ydir[1] + k * Zdir[1]]
|
|
687
|
+
for P in side]
|
|
688
|
+
|
|
689
|
+
def add_topside(i, j, k):
|
|
690
|
+
return polygon(move(Uside, i, j, k), edgecolor='black',
|
|
691
|
+
color=colors[0])
|
|
692
|
+
|
|
693
|
+
def add_leftside(i, j, k):
|
|
694
|
+
return polygon(move(Lside, i, j, k), edgecolor='black',
|
|
695
|
+
color=colors[1])
|
|
696
|
+
|
|
697
|
+
def add_rightside(i, j, k):
|
|
698
|
+
return polygon(move(Rside, i, j, k), edgecolor='black',
|
|
699
|
+
color=colors[2])
|
|
700
|
+
TP = plot([])
|
|
701
|
+
for r in range(len(self.z_tableau())):
|
|
702
|
+
for c in range(len(self.z_tableau()[r])):
|
|
703
|
+
if self.z_tableau()[r][c] > 0 or show_box:
|
|
704
|
+
TP += add_topside(r, c, self.z_tableau()[r][c])
|
|
705
|
+
for r in range(len(self.y_tableau())):
|
|
706
|
+
for c in range(len(self.y_tableau()[r])):
|
|
707
|
+
if self.y_tableau()[r][c] > 0 or show_box:
|
|
708
|
+
TP += add_rightside(c, self.y_tableau()[r][c], r)
|
|
709
|
+
for r in range(len(self.x_tableau())):
|
|
710
|
+
for c in range(len(self.x_tableau()[r])):
|
|
711
|
+
if self.x_tableau()[r][c] > 0 or show_box:
|
|
712
|
+
TP += add_leftside(self.x_tableau()[r][c], r, c)
|
|
713
|
+
TP.axes(show=False)
|
|
714
|
+
return TP
|
|
715
|
+
|
|
716
|
+
def contains(self, PP) -> bool:
|
|
717
|
+
r"""
|
|
718
|
+
Return ``True`` if ``PP`` is a plane partition that fits
|
|
719
|
+
inside ``self``.
|
|
720
|
+
|
|
721
|
+
Specifically, ``self`` contains ``PP`` if, for all `i`, `j`,
|
|
722
|
+
the height of ``PP`` at `ij` is less than or equal to the
|
|
723
|
+
height of ``self`` at `ij`.
|
|
724
|
+
|
|
725
|
+
EXAMPLES::
|
|
726
|
+
|
|
727
|
+
sage: P1 = PlanePartition([[5,4,3], [3,2,2], [1]])
|
|
728
|
+
sage: P2 = PlanePartition([[3,2], [1,1], [0,0], [0,0]])
|
|
729
|
+
sage: P3 = PlanePartition([[5,5,5], [2,1,0]])
|
|
730
|
+
sage: P1.contains(P2)
|
|
731
|
+
True
|
|
732
|
+
sage: P2.contains(P1)
|
|
733
|
+
False
|
|
734
|
+
sage: P1.contains(P3)
|
|
735
|
+
False
|
|
736
|
+
sage: P3.contains(P2)
|
|
737
|
+
True
|
|
738
|
+
"""
|
|
739
|
+
if not isinstance(PP, PlanePartition):
|
|
740
|
+
PP = PlanePartition(PP)
|
|
741
|
+
if len(self) < len(PP):
|
|
742
|
+
return False
|
|
743
|
+
for rowself, rowPP in zip(self, PP):
|
|
744
|
+
if len(rowself) < len(rowPP):
|
|
745
|
+
return False
|
|
746
|
+
if any(valself < valPP for valself, valPP in zip(rowself, rowPP)):
|
|
747
|
+
return False
|
|
748
|
+
return True
|
|
749
|
+
|
|
750
|
+
def plot3d(self, colors=None):
|
|
751
|
+
r"""
|
|
752
|
+
Return a 3D-plot of ``self``.
|
|
753
|
+
|
|
754
|
+
INPUT:
|
|
755
|
+
|
|
756
|
+
- ``colors`` -- (default: ``["white", "lightgray", "darkgray"]``)
|
|
757
|
+
list ``[A, B, C]`` of 3 strings representing colors
|
|
758
|
+
|
|
759
|
+
EXAMPLES::
|
|
760
|
+
|
|
761
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
762
|
+
sage: PP.plot3d() # needs sage.plot
|
|
763
|
+
Graphics3d Object
|
|
764
|
+
"""
|
|
765
|
+
if colors is None:
|
|
766
|
+
colors = ["white", "lightgray", "darkgray"]
|
|
767
|
+
from sage.plot.plot3d.platonic import cube
|
|
768
|
+
return sum(cube(c, color=colors, frame_thickness=2,
|
|
769
|
+
frame_color='black', frame=False)
|
|
770
|
+
for c in self.cells())
|
|
771
|
+
|
|
772
|
+
def complement(self, tableau_only=False) -> PP:
|
|
773
|
+
r"""
|
|
774
|
+
Return the complement of ``self``.
|
|
775
|
+
|
|
776
|
+
If the parent of ``self`` consists only of partitions inside a given
|
|
777
|
+
box, then the complement is taken in this box. Otherwise, the
|
|
778
|
+
complement is taken in the smallest box containing the plane partition.
|
|
779
|
+
The empty plane partition with no box specified is its own complement.
|
|
780
|
+
|
|
781
|
+
If ``tableau_only`` is set to ``True``, then only the tableau
|
|
782
|
+
consisting of the projection of boxes size onto the `xy`-plane
|
|
783
|
+
is returned instead of a :class:`PlanePartition`. This output will
|
|
784
|
+
not have empty trailing rows or trailing zeros removed.
|
|
785
|
+
|
|
786
|
+
EXAMPLES::
|
|
787
|
+
|
|
788
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
789
|
+
sage: PP.complement()
|
|
790
|
+
Plane partition [[4, 4, 3, 3], [4, 3, 3, 2], [3, 1, 1]]
|
|
791
|
+
sage: PP.complement(True)
|
|
792
|
+
[[4, 4, 3, 3], [4, 3, 3, 2], [3, 1, 1, 0]]
|
|
793
|
+
"""
|
|
794
|
+
A = self._max_x
|
|
795
|
+
B = self._max_y
|
|
796
|
+
C = self._max_z
|
|
797
|
+
T = [[C for i in range(B)] for j in range(A)]
|
|
798
|
+
z_tab = self.z_tableau()
|
|
799
|
+
for r in range(A):
|
|
800
|
+
for c in range(B):
|
|
801
|
+
T[A-1-r][B-1-c] = C - z_tab[r][c]
|
|
802
|
+
if tableau_only:
|
|
803
|
+
return T
|
|
804
|
+
P = self.parent()
|
|
805
|
+
if not P._box:
|
|
806
|
+
pp = PlanePartitions()
|
|
807
|
+
return pp.element_class(pp, T)
|
|
808
|
+
return P.element_class(P, T, check=False)
|
|
809
|
+
|
|
810
|
+
def transpose(self, tableau_only=False) -> PP:
|
|
811
|
+
r"""
|
|
812
|
+
Return the transpose of ``self``.
|
|
813
|
+
|
|
814
|
+
If ``tableau_only`` is set to ``True``, then only the tableau
|
|
815
|
+
consisting of the projection of boxes size onto the `xy`-plane
|
|
816
|
+
is returned instead of a :class:`PlanePartition`. This will
|
|
817
|
+
not necessarily have trailing rows or trailing zeros removed.
|
|
818
|
+
|
|
819
|
+
EXAMPLES::
|
|
820
|
+
|
|
821
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
822
|
+
sage: PP.transpose()
|
|
823
|
+
Plane partition [[4, 2, 1], [3, 1, 1], [3, 1], [1]]
|
|
824
|
+
sage: PP.transpose(True)
|
|
825
|
+
[[4, 2, 1], [3, 1, 1], [3, 1, 0], [1, 0, 0]]
|
|
826
|
+
|
|
827
|
+
sage: PPP = PlanePartitions([1, 2, 3])
|
|
828
|
+
sage: PP = PPP([[1, 1]])
|
|
829
|
+
sage: PT = PP.transpose(); PT
|
|
830
|
+
Plane partition [[1], [1]]
|
|
831
|
+
sage: PT.parent()
|
|
832
|
+
Plane partitions inside a 2 x 1 x 3 box
|
|
833
|
+
"""
|
|
834
|
+
T = [[0 for i in range(self._max_x)] for j in range(self._max_y)]
|
|
835
|
+
z_tab = self.z_tableau()
|
|
836
|
+
for r in range(len(z_tab)):
|
|
837
|
+
for c in range(len(z_tab[r])):
|
|
838
|
+
T[c][r] = z_tab[r][c]
|
|
839
|
+
P = self.parent()
|
|
840
|
+
if tableau_only:
|
|
841
|
+
return T
|
|
842
|
+
elif P._box is None or P._box[0] == P._box[1]:
|
|
843
|
+
return P.element_class(P, T, check=False)
|
|
844
|
+
new_box = (P._box[1], P._box[0], P._box[2])
|
|
845
|
+
newP = PlanePartitions(new_box, symmetry=P._symmetry)
|
|
846
|
+
return newP.element_class(newP, T)
|
|
847
|
+
|
|
848
|
+
def is_SPP(self) -> bool:
|
|
849
|
+
r"""
|
|
850
|
+
Return whether ``self`` is a symmetric plane partition.
|
|
851
|
+
|
|
852
|
+
A plane partition is symmetric if the corresponding tableau is
|
|
853
|
+
symmetric about the diagonal.
|
|
854
|
+
|
|
855
|
+
EXAMPLES::
|
|
856
|
+
|
|
857
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
858
|
+
sage: PP.is_SPP()
|
|
859
|
+
False
|
|
860
|
+
sage: PP = PlanePartition([[3,3,2],[3,3,2],[2,2,2]])
|
|
861
|
+
sage: PP.is_SPP()
|
|
862
|
+
True
|
|
863
|
+
sage: PP = PlanePartition([[3,2,1],[2,0,0]])
|
|
864
|
+
sage: PP.is_SPP()
|
|
865
|
+
False
|
|
866
|
+
sage: PP = PlanePartition([[3,2,0],[2,0,0]])
|
|
867
|
+
sage: PP.is_SPP()
|
|
868
|
+
True
|
|
869
|
+
sage: PP = PlanePartition([[3,2],[2,0],[1,0]])
|
|
870
|
+
sage: PP.is_SPP()
|
|
871
|
+
False
|
|
872
|
+
sage: PP = PlanePartition([[3,2],[2,0],[0,0]])
|
|
873
|
+
sage: PP.is_SPP()
|
|
874
|
+
True
|
|
875
|
+
|
|
876
|
+
TESTS::
|
|
877
|
+
|
|
878
|
+
sage: PlanePartition([]).is_SPP()
|
|
879
|
+
True
|
|
880
|
+
"""
|
|
881
|
+
if not self:
|
|
882
|
+
return True
|
|
883
|
+
Z = self.z_tableau()
|
|
884
|
+
c1 = len(Z)
|
|
885
|
+
c2 = len(Z[0])
|
|
886
|
+
size = max(c1, c2)
|
|
887
|
+
T = [[0 for i in range(size)] for j in range(size)]
|
|
888
|
+
for i in range(c1):
|
|
889
|
+
for j in range(c2):
|
|
890
|
+
T[i][j] = Z[i][j]
|
|
891
|
+
return all(T[r][c] == T[c][r]
|
|
892
|
+
for r in range(size)
|
|
893
|
+
for c in range(r, size))
|
|
894
|
+
|
|
895
|
+
def is_CSPP(self) -> bool:
|
|
896
|
+
r"""
|
|
897
|
+
Return whether ``self`` is a cyclically symmetric plane partition.
|
|
898
|
+
|
|
899
|
+
A plane partition is cyclically symmetric if its `x`, `y`, and `z`
|
|
900
|
+
tableaux are all equal.
|
|
901
|
+
|
|
902
|
+
EXAMPLES::
|
|
903
|
+
|
|
904
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
905
|
+
sage: PP.is_CSPP()
|
|
906
|
+
False
|
|
907
|
+
sage: PP = PlanePartition([[3,2,2],[3,1,0],[1,1,0]])
|
|
908
|
+
sage: PP.is_CSPP()
|
|
909
|
+
True
|
|
910
|
+
|
|
911
|
+
TESTS::
|
|
912
|
+
|
|
913
|
+
sage: PlanePartition([]).is_CSPP()
|
|
914
|
+
True
|
|
915
|
+
"""
|
|
916
|
+
if self.z_tableau() == self.y_tableau():
|
|
917
|
+
return True
|
|
918
|
+
return False
|
|
919
|
+
|
|
920
|
+
def is_TSPP(self) -> bool:
|
|
921
|
+
r"""
|
|
922
|
+
Return whether ``self`` is a totally symmetric plane partition.
|
|
923
|
+
|
|
924
|
+
A plane partition is totally symmetric if it is both symmetric and
|
|
925
|
+
cyclically symmetric.
|
|
926
|
+
|
|
927
|
+
EXAMPLES::
|
|
928
|
+
|
|
929
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
930
|
+
sage: PP.is_TSPP()
|
|
931
|
+
False
|
|
932
|
+
sage: PP = PlanePartition([[3,3,3],[3,3,2],[3,2,1]])
|
|
933
|
+
sage: PP.is_TSPP()
|
|
934
|
+
True
|
|
935
|
+
|
|
936
|
+
TESTS::
|
|
937
|
+
|
|
938
|
+
sage: PlanePartition([]).is_TSPP()
|
|
939
|
+
True
|
|
940
|
+
"""
|
|
941
|
+
return self.is_CSPP() and self.is_SPP()
|
|
942
|
+
|
|
943
|
+
def is_SCPP(self) -> bool:
|
|
944
|
+
r"""
|
|
945
|
+
Return whether ``self`` is a self-complementary plane partition.
|
|
946
|
+
|
|
947
|
+
Note that the complement of a plane partition (and thus the property of
|
|
948
|
+
being self-complementary) is dependent on the choice of a box that it is
|
|
949
|
+
contained in. If no parent/bounding box is specified, the box is taken
|
|
950
|
+
to be the smallest box that contains the plane partition.
|
|
951
|
+
|
|
952
|
+
EXAMPLES::
|
|
953
|
+
|
|
954
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
955
|
+
sage: PP.is_SCPP()
|
|
956
|
+
False
|
|
957
|
+
sage: PP = PlanePartition([[4,4,4,4],[4,4,2,0],[4,2,0,0],[0,0,0,0]])
|
|
958
|
+
sage: PP.is_SCPP()
|
|
959
|
+
False
|
|
960
|
+
sage: PP = PlanePartitions([4,4,4])([[4,4,4,4],[4,4,2,0],[4,2,0,0],[0,0,0,0]])
|
|
961
|
+
sage: PP.is_SCPP()
|
|
962
|
+
True
|
|
963
|
+
|
|
964
|
+
TESTS::
|
|
965
|
+
|
|
966
|
+
sage: PlanePartition([]).is_SCPP()
|
|
967
|
+
True
|
|
968
|
+
"""
|
|
969
|
+
return self.z_tableau(tableau=False) == self.complement(tableau_only=True)
|
|
970
|
+
|
|
971
|
+
def is_TCPP(self) -> bool:
|
|
972
|
+
r"""
|
|
973
|
+
Return whether ``self`` is a transpose-complementary plane partition.
|
|
974
|
+
|
|
975
|
+
EXAMPLES::
|
|
976
|
+
|
|
977
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
978
|
+
sage: PP.is_TCPP()
|
|
979
|
+
False
|
|
980
|
+
sage: PP = PlanePartition([[4,4,3,2],[4,4,2,1],[4,2,0,0],[2,0,0,0]])
|
|
981
|
+
sage: PP.is_TCPP()
|
|
982
|
+
True
|
|
983
|
+
|
|
984
|
+
TESTS::
|
|
985
|
+
|
|
986
|
+
sage: PlanePartition([]).is_TCPP()
|
|
987
|
+
True
|
|
988
|
+
"""
|
|
989
|
+
return self.transpose(True) == self.complement(True)
|
|
990
|
+
|
|
991
|
+
def is_SSCPP(self) -> bool:
|
|
992
|
+
r"""
|
|
993
|
+
Return whether ``self`` is a symmetric, self-complementary
|
|
994
|
+
plane partition.
|
|
995
|
+
|
|
996
|
+
EXAMPLES::
|
|
997
|
+
|
|
998
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
999
|
+
sage: PP.is_SSCPP()
|
|
1000
|
+
False
|
|
1001
|
+
sage: PP = PlanePartition([[4,3,3,2],[3,2,2,1],[3,2,2,1],[2,1,1,0]])
|
|
1002
|
+
sage: PP.is_SSCPP()
|
|
1003
|
+
True
|
|
1004
|
+
sage: PP = PlanePartition([[2,1],[1,0]])
|
|
1005
|
+
sage: PP.is_SSCPP()
|
|
1006
|
+
True
|
|
1007
|
+
sage: PP = PlanePartition([[4,3,2],[3,2,1],[2,1,0]])
|
|
1008
|
+
sage: PP.is_SSCPP()
|
|
1009
|
+
True
|
|
1010
|
+
sage: PP = PlanePartition([[4,2,2,2],[2,2,2,2],[2,2,2,2],[2,2,2,0]])
|
|
1011
|
+
sage: PP.is_SSCPP()
|
|
1012
|
+
True
|
|
1013
|
+
|
|
1014
|
+
TESTS::
|
|
1015
|
+
|
|
1016
|
+
sage: PlanePartition([]).is_SSCPP()
|
|
1017
|
+
True
|
|
1018
|
+
"""
|
|
1019
|
+
return self.is_SPP() and self.is_SCPP()
|
|
1020
|
+
|
|
1021
|
+
def is_CSTCPP(self) -> bool:
|
|
1022
|
+
r"""
|
|
1023
|
+
Return whether ``self`` is a cyclically symmetric and
|
|
1024
|
+
transpose-complementary plane partition.
|
|
1025
|
+
|
|
1026
|
+
EXAMPLES::
|
|
1027
|
+
|
|
1028
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
1029
|
+
sage: PP.is_CSTCPP()
|
|
1030
|
+
False
|
|
1031
|
+
sage: PP = PlanePartition([[4,4,3,2],[4,3,2,1],[3,2,1,0],[2,1,0,0]])
|
|
1032
|
+
sage: PP.is_CSTCPP()
|
|
1033
|
+
True
|
|
1034
|
+
|
|
1035
|
+
TESTS::
|
|
1036
|
+
|
|
1037
|
+
sage: PlanePartition([]).is_CSTCPP()
|
|
1038
|
+
True
|
|
1039
|
+
"""
|
|
1040
|
+
return self.is_CSPP() and self.is_TCPP()
|
|
1041
|
+
|
|
1042
|
+
def is_CSSCPP(self) -> bool:
|
|
1043
|
+
r"""
|
|
1044
|
+
Return whether ``self`` is a cyclically symmetric and
|
|
1045
|
+
self-complementary plane partition.
|
|
1046
|
+
|
|
1047
|
+
EXAMPLES::
|
|
1048
|
+
|
|
1049
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
1050
|
+
sage: PP.is_CSSCPP()
|
|
1051
|
+
False
|
|
1052
|
+
sage: PP = PlanePartition([[4,4,4,1],[3,3,2,1],[3,2,1,1],[3,0,0,0]])
|
|
1053
|
+
sage: PP.is_CSSCPP()
|
|
1054
|
+
True
|
|
1055
|
+
|
|
1056
|
+
TESTS::
|
|
1057
|
+
|
|
1058
|
+
sage: PlanePartition([]).is_CSSCPP()
|
|
1059
|
+
True
|
|
1060
|
+
"""
|
|
1061
|
+
return self.is_CSPP() and self.is_SCPP()
|
|
1062
|
+
|
|
1063
|
+
def is_TSSCPP(self) -> bool:
|
|
1064
|
+
r"""
|
|
1065
|
+
Return whether ``self`` is a totally symmetric self-complementary
|
|
1066
|
+
plane partition.
|
|
1067
|
+
|
|
1068
|
+
EXAMPLES::
|
|
1069
|
+
|
|
1070
|
+
sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]])
|
|
1071
|
+
sage: PP.is_TSSCPP()
|
|
1072
|
+
False
|
|
1073
|
+
sage: PP = PlanePartition([[4,4,3,2],[4,3,2,1],[3,2,1,0],[2,1,0,0]])
|
|
1074
|
+
sage: PP.is_TSSCPP()
|
|
1075
|
+
True
|
|
1076
|
+
|
|
1077
|
+
TESTS::
|
|
1078
|
+
|
|
1079
|
+
sage: PlanePartition([]).is_TSSCPP()
|
|
1080
|
+
True
|
|
1081
|
+
"""
|
|
1082
|
+
return self.is_TSPP() and self.is_SCPP()
|
|
1083
|
+
|
|
1084
|
+
def to_order_ideal(self):
|
|
1085
|
+
r"""
|
|
1086
|
+
Return the order ideal corresponding to ``self``.
|
|
1087
|
+
|
|
1088
|
+
.. TODO::
|
|
1089
|
+
|
|
1090
|
+
As many families of symmetric plane partitions are in bijection
|
|
1091
|
+
with order ideals in an associated poset, this function could
|
|
1092
|
+
feasibly have options to send symmetric plane partitions
|
|
1093
|
+
to the associated order ideal in that poset, instead.
|
|
1094
|
+
|
|
1095
|
+
EXAMPLES::
|
|
1096
|
+
|
|
1097
|
+
sage: PlanePartition([[3,2,1],[2,2],[2]]).to_order_ideal() # needs sage.graphs sage.modules
|
|
1098
|
+
[(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 1, 0), (0, 1, 1), (0, 2, 0),
|
|
1099
|
+
(1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1), (2, 0, 0), (2, 0, 1)]
|
|
1100
|
+
sage: PlanePartition([[2,1],[1],[1]]).to_order_ideal() # needs sage.graphs sage.modules
|
|
1101
|
+
[(0, 0, 0), (0, 0, 1), (0, 1, 0), (1, 0, 0), (2, 0, 0)]
|
|
1102
|
+
"""
|
|
1103
|
+
from sage.combinat.posets.poset_examples import posets
|
|
1104
|
+
(a, b, c) = (self._max_x, self._max_y, self._max_z)
|
|
1105
|
+
Q = posets.ProductOfChains([a, b, c])
|
|
1106
|
+
count = 0
|
|
1107
|
+
generate = []
|
|
1108
|
+
for i, row in enumerate(self):
|
|
1109
|
+
for j, val in enumerate(row):
|
|
1110
|
+
if val > 0:
|
|
1111
|
+
generate.append((i, j, val-1))
|
|
1112
|
+
count += 1
|
|
1113
|
+
oi = Q.order_ideal(generate)
|
|
1114
|
+
return oi
|
|
1115
|
+
|
|
1116
|
+
def maximal_boxes(self) -> list:
|
|
1117
|
+
r"""
|
|
1118
|
+
Return the coordinates of the maximal boxes of ``self``.
|
|
1119
|
+
|
|
1120
|
+
The maximal boxes of a plane partitions are the boxes that can be
|
|
1121
|
+
removed from a plane partition and still yield a valid plane partition.
|
|
1122
|
+
|
|
1123
|
+
EXAMPLES::
|
|
1124
|
+
|
|
1125
|
+
sage: sorted(PlanePartition([[3,2,1],[2,2],[2]]).maximal_boxes())
|
|
1126
|
+
[[0, 0, 2], [0, 2, 0], [1, 1, 1], [2, 0, 1]]
|
|
1127
|
+
sage: sorted(PlanePartition([[2,1],[1],[1]]).maximal_boxes())
|
|
1128
|
+
[[0, 0, 1], [0, 1, 0], [2, 0, 0]]
|
|
1129
|
+
"""
|
|
1130
|
+
generate = []
|
|
1131
|
+
for i, row in enumerate(self):
|
|
1132
|
+
for j, entry in enumerate(row):
|
|
1133
|
+
if (i == len(self)-1 or len(self[i+1])-1 < j or self[i+1][j] < entry) and (j == len(row)-1 or row[j+1] < entry):
|
|
1134
|
+
generate.append([i, j, entry-1])
|
|
1135
|
+
return generate
|
|
1136
|
+
|
|
1137
|
+
def cyclically_rotate(self, preserve_parent=False) -> PP:
|
|
1138
|
+
r"""
|
|
1139
|
+
Return the cyclic rotation of ``self``.
|
|
1140
|
+
|
|
1141
|
+
By default, if the parent of ``self`` consists of plane
|
|
1142
|
+
partitions inside an `a \times b \times c` box, the result
|
|
1143
|
+
will have a parent consisting of partitions inside
|
|
1144
|
+
a `c \times a \times b` box, unless the optional parameter
|
|
1145
|
+
``preserve_parent`` is set to ``True``. Enabling this setting
|
|
1146
|
+
may give an element that is **not** an element of its parent.
|
|
1147
|
+
|
|
1148
|
+
EXAMPLES::
|
|
1149
|
+
|
|
1150
|
+
sage: PlanePartition([[3,2,1],[2,2],[2]]).cyclically_rotate()
|
|
1151
|
+
Plane partition [[3, 3, 1], [2, 2], [1]]
|
|
1152
|
+
sage: PP = PlanePartition([[4,1],[1],[1]])
|
|
1153
|
+
sage: PP.cyclically_rotate()
|
|
1154
|
+
Plane partition [[3, 1, 1, 1], [1]]
|
|
1155
|
+
sage: PP == PP.cyclically_rotate().cyclically_rotate().cyclically_rotate()
|
|
1156
|
+
True
|
|
1157
|
+
|
|
1158
|
+
sage: # needs sage.graphs sage.modules
|
|
1159
|
+
sage: PP = PlanePartitions([4,3,2]).random_element()
|
|
1160
|
+
sage: PP.cyclically_rotate().parent()
|
|
1161
|
+
Plane partitions inside a 2 x 4 x 3 box
|
|
1162
|
+
sage: PP = PlanePartitions([3,4,2])([[2,2,2,2],[2,2,2,2],[2,2,2,2]])
|
|
1163
|
+
sage: PP_rotated = PP.cyclically_rotate(preserve_parent=True)
|
|
1164
|
+
sage: PP_rotated in PP_rotated.parent()
|
|
1165
|
+
False
|
|
1166
|
+
"""
|
|
1167
|
+
b = self._max_y
|
|
1168
|
+
c = self._max_z
|
|
1169
|
+
new_antichain = []
|
|
1170
|
+
for elem in self.maximal_boxes():
|
|
1171
|
+
new = (elem[1], elem[2], elem[0])
|
|
1172
|
+
new_antichain.append(new)
|
|
1173
|
+
pp_matrix = [[0] * (c) for i in range(b)]
|
|
1174
|
+
for box in new_antichain:
|
|
1175
|
+
y = box[0]
|
|
1176
|
+
z = box[1]
|
|
1177
|
+
x = box[2]
|
|
1178
|
+
pp_matrix[y][z] = x + 1
|
|
1179
|
+
if new_antichain:
|
|
1180
|
+
for i in range(b):
|
|
1181
|
+
i = b - (i+1)
|
|
1182
|
+
for j in range(c):
|
|
1183
|
+
j = c - (j+1)
|
|
1184
|
+
if pp_matrix[i][j] == 0:
|
|
1185
|
+
iValue = 0
|
|
1186
|
+
jValue = 0
|
|
1187
|
+
if i < b-1:
|
|
1188
|
+
iValue = pp_matrix[i+1][j]
|
|
1189
|
+
if j < c-1:
|
|
1190
|
+
jValue = pp_matrix[i][j+1]
|
|
1191
|
+
pp_matrix[i][j] = max(iValue, jValue)
|
|
1192
|
+
# Start code for determining correct parent
|
|
1193
|
+
P = self.parent()
|
|
1194
|
+
if P._box is None or preserve_parent or (P._box[0] == P._box[1] == P._box[2]):
|
|
1195
|
+
return P.element_class(P, pp_matrix, check=preserve_parent)
|
|
1196
|
+
new_box = (P._box[2], P._box[0], P._box[1])
|
|
1197
|
+
newP = PlanePartitions(new_box, symmetry=P._symmetry)
|
|
1198
|
+
return newP.element_class(newP, pp_matrix)
|
|
1199
|
+
|
|
1200
|
+
def bounding_box(self):
|
|
1201
|
+
r"""
|
|
1202
|
+
Return the smallest box `(a, b, c)` that ``self`` is contained in.
|
|
1203
|
+
|
|
1204
|
+
EXAMPLES::
|
|
1205
|
+
|
|
1206
|
+
sage: PP = PlanePartition([[5,2,1,1], [2,2], [2]])
|
|
1207
|
+
sage: PP.bounding_box()
|
|
1208
|
+
(3, 4, 5)
|
|
1209
|
+
"""
|
|
1210
|
+
if not self:
|
|
1211
|
+
return (0, 0, 0)
|
|
1212
|
+
return (len(self), len(self[0]), self[0][0])
|
|
1213
|
+
|
|
1214
|
+
|
|
1215
|
+
PP = NewType('PP', PlanePartition)
|
|
1216
|
+
|
|
1217
|
+
|
|
1218
|
+
class PlanePartitions(UniqueRepresentation, Parent):
|
|
1219
|
+
r"""
|
|
1220
|
+
Plane partitions.
|
|
1221
|
+
|
|
1222
|
+
``PlanePartitions()`` returns the class of all plane partitions.
|
|
1223
|
+
|
|
1224
|
+
``PlanePartitions(n)`` return the class of all plane partitions with
|
|
1225
|
+
precisely `n` boxes.
|
|
1226
|
+
|
|
1227
|
+
``PlanePartitions([a, b, c])`` returns the class of plane partitions
|
|
1228
|
+
that fit inside an `a \times b \times c` box.
|
|
1229
|
+
|
|
1230
|
+
``PlanePartitions([a, b, c])`` has the optional keyword ``symmetry``, which
|
|
1231
|
+
restricts the plane partitions inside a box of the specified size satisfying
|
|
1232
|
+
certain symmetry conditions.
|
|
1233
|
+
|
|
1234
|
+
- ``symmetry='SPP'`` gives the class of symmetric plane partitions. which
|
|
1235
|
+
is all plane partitions fixed under reflection across the diagonal.
|
|
1236
|
+
Requires that `a = b`.
|
|
1237
|
+
|
|
1238
|
+
- ``symmetry='CSPP'`` gives the class of cyclic plane partitions, which
|
|
1239
|
+
is all plane partitions fixed under cyclic rotation of coordinates.
|
|
1240
|
+
Requires that `a = b = c`.
|
|
1241
|
+
|
|
1242
|
+
- ``symmetry='TSPP'`` gives the class of totally symmetric plane partitions,
|
|
1243
|
+
which is all plane partitions fixed under any interchanging of coordinates.
|
|
1244
|
+
Requires that `a = b = c`.
|
|
1245
|
+
|
|
1246
|
+
- ``symmetry='SCPP'`` gives the class of self-complementary plane partitions.
|
|
1247
|
+
which is all plane partitions that are equal to their own complement
|
|
1248
|
+
in the specified box. Requires at least one of `a,b,c` be even.
|
|
1249
|
+
|
|
1250
|
+
- ``symmetry='TCPP'`` gives the class of transpose complement plane
|
|
1251
|
+
partitions, which is all plane partitions whose complement in the box
|
|
1252
|
+
of the specified size is equal to their transpose. Requires `a = b` and
|
|
1253
|
+
at least one of `a, b, c` be even.
|
|
1254
|
+
|
|
1255
|
+
- ``symmetry='SSCPP'`` gives the class of symmetric self-complementary
|
|
1256
|
+
plane partitions, which is all plane partitions that are both
|
|
1257
|
+
symmetric and self-complementary. Requires `a = b` and at least one of
|
|
1258
|
+
`a, b, c` be even.
|
|
1259
|
+
|
|
1260
|
+
- ``symmetry='CSTCPP'`` gives the class of cyclically symmetric transpose
|
|
1261
|
+
complement plane partitions, which is all plane partitions that are
|
|
1262
|
+
both symmetric and equal to the transpose of their complement. Requires
|
|
1263
|
+
`a = b = c`.
|
|
1264
|
+
|
|
1265
|
+
- ``symmetry='CSSCPP'`` gives the class of cyclically symmetric
|
|
1266
|
+
self-complementary plane partitions, which is all plane partitions that
|
|
1267
|
+
are both cyclically symmetric and self-complementary. Requires `a = b = c`
|
|
1268
|
+
and all `a, b, c` be even.
|
|
1269
|
+
|
|
1270
|
+
- ``symmetry='TSSCPP'`` gives the class of totally symmetric
|
|
1271
|
+
self-complementary plane partitions, which is all plane partitions that
|
|
1272
|
+
are totally symmetric and also self-complementary. Requires `a = b = c`
|
|
1273
|
+
and all `a, b, c` be even.
|
|
1274
|
+
|
|
1275
|
+
EXAMPLES:
|
|
1276
|
+
|
|
1277
|
+
If no arguments are passed, then the class of all plane partitions
|
|
1278
|
+
is returned::
|
|
1279
|
+
|
|
1280
|
+
sage: PlanePartitions()
|
|
1281
|
+
Plane partitions
|
|
1282
|
+
sage: [[2,1],[1]] in PlanePartitions()
|
|
1283
|
+
True
|
|
1284
|
+
|
|
1285
|
+
If an integer `n` is passed, then the class of plane partitions of `n`
|
|
1286
|
+
is returned::
|
|
1287
|
+
|
|
1288
|
+
sage: PlanePartitions(3)
|
|
1289
|
+
Plane partitions of size 3
|
|
1290
|
+
sage: PlanePartitions(3).list()
|
|
1291
|
+
[Plane partition [[3]],
|
|
1292
|
+
Plane partition [[2, 1]],
|
|
1293
|
+
Plane partition [[1, 1, 1]],
|
|
1294
|
+
Plane partition [[2], [1]],
|
|
1295
|
+
Plane partition [[1, 1], [1]],
|
|
1296
|
+
Plane partition [[1], [1], [1]]]
|
|
1297
|
+
|
|
1298
|
+
If a three-element tuple or list `[a,b,c]` is passed, then the class of all
|
|
1299
|
+
plane partitions that fit inside and `a \times b \times c` box is returned::
|
|
1300
|
+
|
|
1301
|
+
sage: PlanePartitions([2,2,2])
|
|
1302
|
+
Plane partitions inside a 2 x 2 x 2 box
|
|
1303
|
+
sage: [[2,1],[1]] in PlanePartitions([2,2,2])
|
|
1304
|
+
True
|
|
1305
|
+
|
|
1306
|
+
If an additional keyword ``symmetry`` is pass along with a three-element
|
|
1307
|
+
tuple or list `[a, b,c ]`, then the class of all plane partitions that fit
|
|
1308
|
+
inside an `a \times b \times c` box with the specified symmetry is returned::
|
|
1309
|
+
|
|
1310
|
+
sage: PlanePartitions([2,2,2], symmetry='CSPP')
|
|
1311
|
+
Cyclically symmetric plane partitions inside a 2 x 2 x 2 box
|
|
1312
|
+
sage: [[2,1],[1]] in PlanePartitions([2,2,2], symmetry='CSPP')
|
|
1313
|
+
True
|
|
1314
|
+
|
|
1315
|
+
.. SEEALSO::
|
|
1316
|
+
|
|
1317
|
+
- :class:`PlanePartition`
|
|
1318
|
+
- :class:`PlanePartitions_all`
|
|
1319
|
+
- :class:`PlanePartitions_n`
|
|
1320
|
+
- :class:`PlanePartitions_box`
|
|
1321
|
+
- :class:`PlanePartitions_SPP`
|
|
1322
|
+
- :class:`PlanePartitions_CSPP`
|
|
1323
|
+
- :class:`PlanePartitions_TSPP`
|
|
1324
|
+
- :class:`PlanePartitions_SCPP`
|
|
1325
|
+
- :class:`PlanePartitions_TCPP`
|
|
1326
|
+
- :class:`PlanePartitions_SSCPP`
|
|
1327
|
+
- :class:`PlanePartitions_CSTCPP`
|
|
1328
|
+
- :class:`PlanePartitions_CSSCPP`
|
|
1329
|
+
- :class:`PlanePartitions_TSSCPP`
|
|
1330
|
+
"""
|
|
1331
|
+
@staticmethod
|
|
1332
|
+
def __classcall_private__(cls, *args, **kwds):
|
|
1333
|
+
r"""
|
|
1334
|
+
Return the appropriate parent based on arguments.
|
|
1335
|
+
|
|
1336
|
+
See the documentation for :class:`PlanePartitions` for more information.
|
|
1337
|
+
|
|
1338
|
+
TESTS::
|
|
1339
|
+
|
|
1340
|
+
sage: PlanePartitions()
|
|
1341
|
+
Plane partitions
|
|
1342
|
+
sage: PlanePartitions([3,3,3])
|
|
1343
|
+
Plane partitions inside a 3 x 3 x 3 box
|
|
1344
|
+
sage: PlanePartitions(3)
|
|
1345
|
+
Plane partitions of size 3
|
|
1346
|
+
sage: PlanePartitions([4,4,4], symmetry='TSSCPP')
|
|
1347
|
+
Totally symmetric self-complementary plane partitions inside a 4 x 4 x 4 box
|
|
1348
|
+
sage: PlanePartitions(4, symmetry='TSSCPP')
|
|
1349
|
+
Traceback (most recent call last):
|
|
1350
|
+
...
|
|
1351
|
+
ValueError: the number of boxes may only be specified if no symmetry is required
|
|
1352
|
+
"""
|
|
1353
|
+
symmetry = kwds.get('symmetry', None)
|
|
1354
|
+
box_size = kwds.get('box_size', None)
|
|
1355
|
+
|
|
1356
|
+
if not args and symmetry is None and box_size is None:
|
|
1357
|
+
return PlanePartitions_all()
|
|
1358
|
+
|
|
1359
|
+
if args and box_size is None:
|
|
1360
|
+
# The first arg could be either a size or a box size
|
|
1361
|
+
if isinstance(args[0], (int, Integer)):
|
|
1362
|
+
if symmetry is None:
|
|
1363
|
+
return PlanePartitions_n(args[0])
|
|
1364
|
+
else:
|
|
1365
|
+
raise ValueError("the number of boxes may only be specified if no symmetry is required")
|
|
1366
|
+
box_size = args[0]
|
|
1367
|
+
|
|
1368
|
+
box_size = tuple(box_size)
|
|
1369
|
+
if symmetry is None:
|
|
1370
|
+
return PlanePartitions_box(box_size)
|
|
1371
|
+
elif symmetry == 'SPP':
|
|
1372
|
+
return PlanePartitions_SPP(box_size)
|
|
1373
|
+
elif symmetry == 'CSPP':
|
|
1374
|
+
return PlanePartitions_CSPP(box_size)
|
|
1375
|
+
elif symmetry == 'TSPP':
|
|
1376
|
+
return PlanePartitions_TSPP(box_size)
|
|
1377
|
+
elif symmetry == 'SCPP':
|
|
1378
|
+
return PlanePartitions_SCPP(box_size)
|
|
1379
|
+
elif symmetry == 'TCPP':
|
|
1380
|
+
return PlanePartitions_TCPP(box_size)
|
|
1381
|
+
elif symmetry == 'SSCPP':
|
|
1382
|
+
return PlanePartitions_SSCPP(box_size)
|
|
1383
|
+
elif symmetry == 'CSTCPP':
|
|
1384
|
+
return PlanePartitions_CSTCPP(box_size)
|
|
1385
|
+
elif symmetry == 'CSSCPP':
|
|
1386
|
+
return PlanePartitions_CSSCPP(box_size)
|
|
1387
|
+
elif symmetry == 'TSSCPP':
|
|
1388
|
+
return PlanePartitions_TSSCPP(box_size)
|
|
1389
|
+
|
|
1390
|
+
raise ValueError("invalid symmetry class option")
|
|
1391
|
+
|
|
1392
|
+
def __init__(self, box_size=None, symmetry=None, category=None):
|
|
1393
|
+
r"""
|
|
1394
|
+
Initialize ``self``.
|
|
1395
|
+
|
|
1396
|
+
TESTS::
|
|
1397
|
+
|
|
1398
|
+
sage: PP = PlanePartitions(box_size=[2,2,1])
|
|
1399
|
+
sage: TestSuite(PP).run() # needs sage.modules
|
|
1400
|
+
"""
|
|
1401
|
+
if box_size is not None and len(box_size) != 3:
|
|
1402
|
+
raise ValueError("invalid box size")
|
|
1403
|
+
self._box = box_size
|
|
1404
|
+
self._symmetry = symmetry
|
|
1405
|
+
Parent.__init__(self, category=category)
|
|
1406
|
+
|
|
1407
|
+
Element = PlanePartition
|
|
1408
|
+
|
|
1409
|
+
def __contains__(self, pp):
|
|
1410
|
+
"""
|
|
1411
|
+
Check to see that ``pp`` is a valid plane partition.
|
|
1412
|
+
|
|
1413
|
+
EXAMPLES::
|
|
1414
|
+
|
|
1415
|
+
sage: [[3,2,1],[2,1]] in PlanePartitions()
|
|
1416
|
+
True
|
|
1417
|
+
sage: [[3,2,1],[1,2]] in PlanePartitions()
|
|
1418
|
+
False
|
|
1419
|
+
sage: [[3,2,1],[3,3]] in PlanePartitions()
|
|
1420
|
+
False
|
|
1421
|
+
"""
|
|
1422
|
+
if isinstance(pp, PlanePartition):
|
|
1423
|
+
return True
|
|
1424
|
+
if isinstance(pp, (list, tuple)):
|
|
1425
|
+
if not pp:
|
|
1426
|
+
return True
|
|
1427
|
+
if not all(a in ZZ for b in pp for a in b):
|
|
1428
|
+
return False
|
|
1429
|
+
for row in pp:
|
|
1430
|
+
if not all(c >= 0 for c in row):
|
|
1431
|
+
return False
|
|
1432
|
+
if not all(row[i] >= row[i+1] for i in range(len(row)-1)):
|
|
1433
|
+
return False
|
|
1434
|
+
for row, nxt in zip(pp, pp[1:]):
|
|
1435
|
+
if not all(row[c] >= nxt[c] for c in range(len(nxt))):
|
|
1436
|
+
return False
|
|
1437
|
+
return True
|
|
1438
|
+
return False
|
|
1439
|
+
|
|
1440
|
+
def box(self) -> tuple:
|
|
1441
|
+
"""
|
|
1442
|
+
Return the size of the box of the plane partition of ``self``
|
|
1443
|
+
is contained in.
|
|
1444
|
+
|
|
1445
|
+
EXAMPLES::
|
|
1446
|
+
|
|
1447
|
+
sage: P = PlanePartitions([4,3,5])
|
|
1448
|
+
sage: P.box()
|
|
1449
|
+
(4, 3, 5)
|
|
1450
|
+
|
|
1451
|
+
sage: PP = PlanePartitions()
|
|
1452
|
+
sage: PP.box() is None
|
|
1453
|
+
True
|
|
1454
|
+
"""
|
|
1455
|
+
return self._box
|
|
1456
|
+
|
|
1457
|
+
def symmetry(self) -> str:
|
|
1458
|
+
"""
|
|
1459
|
+
Return the symmetry class of ``self``.
|
|
1460
|
+
|
|
1461
|
+
EXAMPLES::
|
|
1462
|
+
|
|
1463
|
+
sage: PP = PlanePartitions([3,3,2], symmetry='SPP')
|
|
1464
|
+
sage: PP.symmetry()
|
|
1465
|
+
'SPP'
|
|
1466
|
+
sage: PP = PlanePartitions()
|
|
1467
|
+
sage: PP.symmetry() is None
|
|
1468
|
+
True
|
|
1469
|
+
"""
|
|
1470
|
+
return self._symmetry
|
|
1471
|
+
|
|
1472
|
+
|
|
1473
|
+
class PlanePartitions_all(PlanePartitions, DisjointUnionEnumeratedSets):
|
|
1474
|
+
r"""
|
|
1475
|
+
All plane partitions.
|
|
1476
|
+
"""
|
|
1477
|
+
def __init__(self):
|
|
1478
|
+
r"""
|
|
1479
|
+
Initialize the class of all plane partitions.
|
|
1480
|
+
|
|
1481
|
+
.. WARNING::
|
|
1482
|
+
|
|
1483
|
+
Input is not checked; please use :class:`PlanePartitions` to
|
|
1484
|
+
ensure the options are properly parsed.
|
|
1485
|
+
|
|
1486
|
+
TESTS::
|
|
1487
|
+
|
|
1488
|
+
sage: from sage.combinat.plane_partition import PlanePartitions_all
|
|
1489
|
+
sage: P = PlanePartitions_all()
|
|
1490
|
+
sage: TestSuite(P).run()
|
|
1491
|
+
"""
|
|
1492
|
+
# We manually set these here rather than invoking the super().__init__().
|
|
1493
|
+
# This is so DisjointUnionEnumeratedSets can make the Parent.__init__() call.
|
|
1494
|
+
self._box = None
|
|
1495
|
+
self._symmetry = None
|
|
1496
|
+
# super(PlanePartitions_all, self).__init__(category=InfiniteEnumeratedSets())
|
|
1497
|
+
|
|
1498
|
+
DisjointUnionEnumeratedSets.__init__(self,
|
|
1499
|
+
Family(NonNegativeIntegers(),
|
|
1500
|
+
PlanePartitions_n),
|
|
1501
|
+
facade=True,
|
|
1502
|
+
keepkey=False)
|
|
1503
|
+
|
|
1504
|
+
def _repr_(self) -> str:
|
|
1505
|
+
"""
|
|
1506
|
+
Return a string representation of ``self``.
|
|
1507
|
+
|
|
1508
|
+
EXAMPLES::
|
|
1509
|
+
|
|
1510
|
+
sage: PlanePartitions()
|
|
1511
|
+
Plane partitions
|
|
1512
|
+
"""
|
|
1513
|
+
return "Plane partitions"
|
|
1514
|
+
|
|
1515
|
+
def an_element(self):
|
|
1516
|
+
r"""
|
|
1517
|
+
Return a particular element of the class.
|
|
1518
|
+
|
|
1519
|
+
TESTS::
|
|
1520
|
+
|
|
1521
|
+
sage: P = PlanePartitions()
|
|
1522
|
+
sage: P.an_element()
|
|
1523
|
+
Plane partition [[2, 1], [1]]
|
|
1524
|
+
"""
|
|
1525
|
+
return self.element_class(self, [[2, 1], [1]])
|
|
1526
|
+
|
|
1527
|
+
|
|
1528
|
+
class PlanePartitions_box(PlanePartitions):
|
|
1529
|
+
r"""
|
|
1530
|
+
All plane partitions that fit inside a box of a specified size.
|
|
1531
|
+
|
|
1532
|
+
By convention, a plane partition in an `a \times b \times c` box
|
|
1533
|
+
will have at most `a` rows, of lengths at most `b`, with entries
|
|
1534
|
+
at most `c`.
|
|
1535
|
+
"""
|
|
1536
|
+
def __init__(self, box_size):
|
|
1537
|
+
r"""
|
|
1538
|
+
Initialize the class of plane partitions that fit in a box of a
|
|
1539
|
+
specified size.
|
|
1540
|
+
|
|
1541
|
+
EXAMPLES::
|
|
1542
|
+
|
|
1543
|
+
sage: PP = PlanePartitions([4,3,2])
|
|
1544
|
+
sage: TestSuite(PP).run() # long time # needs sage.modules
|
|
1545
|
+
"""
|
|
1546
|
+
super().__init__(box_size, category=FiniteEnumeratedSets())
|
|
1547
|
+
|
|
1548
|
+
def _repr_(self) -> str:
|
|
1549
|
+
"""
|
|
1550
|
+
Return a string representation of ``self``.
|
|
1551
|
+
|
|
1552
|
+
EXAMPLES::
|
|
1553
|
+
|
|
1554
|
+
sage: PlanePartitions([4,3,2])
|
|
1555
|
+
Plane partitions inside a 4 x 3 x 2 box
|
|
1556
|
+
"""
|
|
1557
|
+
return "Plane partitions inside a {} x {} x {} box".format(
|
|
1558
|
+
self._box[0], self._box[1], self._box[2])
|
|
1559
|
+
|
|
1560
|
+
def __contains__(self, x):
|
|
1561
|
+
"""
|
|
1562
|
+
TESTS::
|
|
1563
|
+
|
|
1564
|
+
sage: [[2,1],[1]] in PlanePartitions([2,2,2])
|
|
1565
|
+
True
|
|
1566
|
+
sage: [[3,1],[1]] in PlanePartitions([2,2,2])
|
|
1567
|
+
False
|
|
1568
|
+
sage: [[2,1],[1],[1]] in PlanePartitions([2,2,2])
|
|
1569
|
+
False
|
|
1570
|
+
sage: [[2,1,1],[1]] in PlanePartitions([2,2,2])
|
|
1571
|
+
False
|
|
1572
|
+
"""
|
|
1573
|
+
if len(x) == 0:
|
|
1574
|
+
return True
|
|
1575
|
+
return PlanePartitions.__contains__(self, x) and len(x) <= self._box[0] and len(x[0]) <= self._box[1] and x[0][0] <= self._box[2]
|
|
1576
|
+
|
|
1577
|
+
def to_poset(self):
|
|
1578
|
+
r"""
|
|
1579
|
+
Return the product of three chains poset, whose order ideals are
|
|
1580
|
+
naturally in bijection with plane partitions inside a box.
|
|
1581
|
+
|
|
1582
|
+
EXAMPLES::
|
|
1583
|
+
|
|
1584
|
+
sage: PlanePartitions([2,2,2]).to_poset() # needs sage.graphs sage.modules
|
|
1585
|
+
Finite lattice containing 8 elements
|
|
1586
|
+
"""
|
|
1587
|
+
a = self._box[0]
|
|
1588
|
+
b = self._box[1]
|
|
1589
|
+
c = self._box[2]
|
|
1590
|
+
from sage.combinat.posets.poset_examples import posets
|
|
1591
|
+
return posets.ProductOfChains([a, b, c])
|
|
1592
|
+
|
|
1593
|
+
def from_order_ideal(self, I) -> PP:
|
|
1594
|
+
r"""
|
|
1595
|
+
Return the plane partition corresponding to an order ideal in the
|
|
1596
|
+
poset given in :meth:`to_poset`.
|
|
1597
|
+
|
|
1598
|
+
EXAMPLES::
|
|
1599
|
+
|
|
1600
|
+
sage: I = [(1, 0, 0), (1, 0, 1), (1, 1, 0), (0, 1, 0),
|
|
1601
|
+
....: (0, 0, 0), (0, 0, 1), (0, 1, 1)]
|
|
1602
|
+
sage: PlanePartitions([2,2,2]).from_order_ideal(I) # needs sage.graphs sage.modules
|
|
1603
|
+
Plane partition [[2, 2], [2, 1]]
|
|
1604
|
+
"""
|
|
1605
|
+
return self.from_antichain(self.to_poset().order_ideal_generators(I))
|
|
1606
|
+
|
|
1607
|
+
def from_antichain(self, A) -> PP:
|
|
1608
|
+
r"""
|
|
1609
|
+
Return the plane partition corresponding to an antichain in the poset
|
|
1610
|
+
given in :meth:`to_poset`.
|
|
1611
|
+
|
|
1612
|
+
EXAMPLES::
|
|
1613
|
+
|
|
1614
|
+
sage: A = [(1,0,1), (0,1,1), (1,1,0)]
|
|
1615
|
+
sage: PlanePartitions([2,2,2]).from_antichain(A)
|
|
1616
|
+
Plane partition [[2, 2], [2, 1]]
|
|
1617
|
+
"""
|
|
1618
|
+
a = self._box[0]
|
|
1619
|
+
b = self._box[1]
|
|
1620
|
+
# Creates a matrix for the plane partition populated by 0s EX: [[0,0,0], [0,0,0], [0,0,0]]
|
|
1621
|
+
pp_matrix = [[0] * (b) for i in range(a)]
|
|
1622
|
+
|
|
1623
|
+
# ac format ex: [x,y,z]
|
|
1624
|
+
# iterate through each antichain, assigning the y,z position in pp_matrix = the height of the stack (x + 1)
|
|
1625
|
+
for ac in A:
|
|
1626
|
+
x = ac[0]
|
|
1627
|
+
y = ac[1]
|
|
1628
|
+
z = ac[2]
|
|
1629
|
+
pp_matrix[x][y] = z + 1
|
|
1630
|
+
|
|
1631
|
+
# For each value in current antichain, fill in the rest of the matrix by
|
|
1632
|
+
# rule M[y,z] = Max(M[y+1,z], M[y,z+1]) antichain is now in plane partition format
|
|
1633
|
+
if A:
|
|
1634
|
+
for i in range(a):
|
|
1635
|
+
i = a - (i + 1)
|
|
1636
|
+
for j in range(b):
|
|
1637
|
+
j = b - (j + 1)
|
|
1638
|
+
if pp_matrix[i][j] == 0:
|
|
1639
|
+
iValue = 0
|
|
1640
|
+
jValue = 0
|
|
1641
|
+
if i < a-1:
|
|
1642
|
+
iValue = pp_matrix[i+1][j]
|
|
1643
|
+
if j < b-1:
|
|
1644
|
+
jValue = pp_matrix[i][j+1]
|
|
1645
|
+
pp_matrix[i][j] = max(iValue, jValue)
|
|
1646
|
+
return self.element_class(self, pp_matrix)
|
|
1647
|
+
|
|
1648
|
+
def __iter__(self) -> Iterator:
|
|
1649
|
+
r"""
|
|
1650
|
+
Iterate over all partitions that fit inside a box.
|
|
1651
|
+
|
|
1652
|
+
EXAMPLES::
|
|
1653
|
+
|
|
1654
|
+
sage: list(PlanePartitions([1,2,1])) # needs sage.modules
|
|
1655
|
+
[Plane partition [], Plane partition [[1]], Plane partition [[1, 1]]]
|
|
1656
|
+
|
|
1657
|
+
TESTS::
|
|
1658
|
+
|
|
1659
|
+
sage: all(len(set(PP)) == PP.cardinality() # needs sage.modules
|
|
1660
|
+
....: for b in cartesian_product([range(4)]*3)
|
|
1661
|
+
....: if (PP := PlanePartitions(b)))
|
|
1662
|
+
True
|
|
1663
|
+
"""
|
|
1664
|
+
A = self._box[0]
|
|
1665
|
+
B = self._box[1]
|
|
1666
|
+
C = self._box[2]
|
|
1667
|
+
if not A:
|
|
1668
|
+
yield self.element_class(self, [], check=False)
|
|
1669
|
+
return
|
|
1670
|
+
from sage.combinat.tableau import SemistandardTableaux as SST
|
|
1671
|
+
for T in SST([B for i in range(A)], max_entry=C + A): # type:ignore
|
|
1672
|
+
PP = [[0 for _ in range(B)] for _ in range(A)]
|
|
1673
|
+
for r in range(A):
|
|
1674
|
+
for c in range(B):
|
|
1675
|
+
PP[A - 1 - r][B - 1 - c] = T[r][c] - r - 1
|
|
1676
|
+
yield self.element_class(self, PP, check=False)
|
|
1677
|
+
|
|
1678
|
+
def cardinality(self) -> Integer:
|
|
1679
|
+
r"""
|
|
1680
|
+
Return the cardinality of ``self``.
|
|
1681
|
+
|
|
1682
|
+
The number of plane partitions inside an `a \times b \times c`
|
|
1683
|
+
box is equal to
|
|
1684
|
+
|
|
1685
|
+
.. MATH::
|
|
1686
|
+
|
|
1687
|
+
\prod_{i=1}^{a} \prod_{j=1}^{b} \prod_{k=1}^{c}
|
|
1688
|
+
\frac{i+j+k-1}{i+j+k-2}.
|
|
1689
|
+
|
|
1690
|
+
EXAMPLES::
|
|
1691
|
+
|
|
1692
|
+
sage: P = PlanePartitions([4,3,5])
|
|
1693
|
+
sage: P.cardinality()
|
|
1694
|
+
116424
|
|
1695
|
+
"""
|
|
1696
|
+
A = self._box[0]
|
|
1697
|
+
B = self._box[1]
|
|
1698
|
+
C = self._box[2]
|
|
1699
|
+
return Integer(prod(i + j + k - 1
|
|
1700
|
+
for i in range(1, A + 1)
|
|
1701
|
+
for j in range(1, B + 1)
|
|
1702
|
+
for k in range(1, C + 1)) //
|
|
1703
|
+
prod(i + j + k - 2
|
|
1704
|
+
for i in range(1, A + 1)
|
|
1705
|
+
for j in range(1, B + 1)
|
|
1706
|
+
for k in range(1, C + 1)))
|
|
1707
|
+
|
|
1708
|
+
def random_element(self) -> PP:
|
|
1709
|
+
r"""
|
|
1710
|
+
Return a uniformly random plane partition inside a box.
|
|
1711
|
+
|
|
1712
|
+
ALGORITHM:
|
|
1713
|
+
|
|
1714
|
+
This uses the
|
|
1715
|
+
:meth:`~sage.combinat.posets.posets.FinitePoset.random_order_ideal`
|
|
1716
|
+
method and the natural bijection with plane partitions.
|
|
1717
|
+
|
|
1718
|
+
EXAMPLES::
|
|
1719
|
+
|
|
1720
|
+
sage: P = PlanePartitions([4,3,5])
|
|
1721
|
+
sage: P.random_element() # random # needs sage.graphs sage.modules
|
|
1722
|
+
Plane partition [[4, 3, 3], [4], [2]]
|
|
1723
|
+
"""
|
|
1724
|
+
Z = self.from_order_ideal(self.to_poset().random_order_ideal())
|
|
1725
|
+
return self.element_class(self, Z, check=False)
|
|
1726
|
+
|
|
1727
|
+
|
|
1728
|
+
class PlanePartitions_n(PlanePartitions):
|
|
1729
|
+
"""
|
|
1730
|
+
Plane partitions with a fixed number of boxes.
|
|
1731
|
+
"""
|
|
1732
|
+
def __init__(self, n):
|
|
1733
|
+
r"""
|
|
1734
|
+
Initialize the class of plane partitions with ``n`` boxes.
|
|
1735
|
+
|
|
1736
|
+
.. WARNING::
|
|
1737
|
+
|
|
1738
|
+
Input is not checked; please use :class:`PlanePartitions` to
|
|
1739
|
+
ensure the options are properly parsed.
|
|
1740
|
+
|
|
1741
|
+
TESTS::
|
|
1742
|
+
|
|
1743
|
+
sage: PP = PlanePartitions(4)
|
|
1744
|
+
sage: type(PP)
|
|
1745
|
+
<class 'sage.combinat.plane_partition.PlanePartitions_n_with_category'>
|
|
1746
|
+
sage: TestSuite(PP).run()
|
|
1747
|
+
"""
|
|
1748
|
+
super().__init__(category=FiniteEnumeratedSets())
|
|
1749
|
+
self._n = n
|
|
1750
|
+
|
|
1751
|
+
def _repr_(self) -> str:
|
|
1752
|
+
"""
|
|
1753
|
+
TESTS::
|
|
1754
|
+
|
|
1755
|
+
sage: PlanePartitions(3)
|
|
1756
|
+
Plane partitions of size 3
|
|
1757
|
+
"""
|
|
1758
|
+
return "Plane partitions of size {}".format(self._n)
|
|
1759
|
+
|
|
1760
|
+
def __contains__(self, x) -> bool:
|
|
1761
|
+
"""
|
|
1762
|
+
TESTS::
|
|
1763
|
+
|
|
1764
|
+
sage: [[2,1],[1]] in PlanePartitions(4)
|
|
1765
|
+
True
|
|
1766
|
+
sage: [[2,1],[1]] in PlanePartitions(3)
|
|
1767
|
+
False
|
|
1768
|
+
"""
|
|
1769
|
+
return PlanePartitions.__contains__(self, x) and PlanePartition(x).number_of_boxes() == self._n
|
|
1770
|
+
|
|
1771
|
+
def __iter__(self) -> Iterator:
|
|
1772
|
+
r"""
|
|
1773
|
+
Iterate over all plane partitions of a fixed size.
|
|
1774
|
+
|
|
1775
|
+
EXAMPLES::
|
|
1776
|
+
|
|
1777
|
+
sage: list(PlanePartitions(2))
|
|
1778
|
+
[Plane partition [[2]], Plane partition [[1, 1]], Plane partition [[1], [1]]]
|
|
1779
|
+
|
|
1780
|
+
TESTS::
|
|
1781
|
+
|
|
1782
|
+
sage: all(len(set(PP)) == PP.cardinality() for n in range(9) if (PP := PlanePartitions(n)))
|
|
1783
|
+
True
|
|
1784
|
+
"""
|
|
1785
|
+
from sage.combinat.partition import Partitions
|
|
1786
|
+
|
|
1787
|
+
def P_in_shape_iter(n, la):
|
|
1788
|
+
if n < 0 or sum(la) < n:
|
|
1789
|
+
return
|
|
1790
|
+
if n == 0:
|
|
1791
|
+
yield []
|
|
1792
|
+
return
|
|
1793
|
+
if len(la) == 1:
|
|
1794
|
+
if la[0] >= n:
|
|
1795
|
+
yield [n]
|
|
1796
|
+
return
|
|
1797
|
+
if sum(la) == n:
|
|
1798
|
+
yield la
|
|
1799
|
+
return
|
|
1800
|
+
for mu_0 in range(min(n, la[0]), 0, -1):
|
|
1801
|
+
new_la = [min(mu_0, la[i]) for i in range(1, len(la))]
|
|
1802
|
+
for mu in P_in_shape_iter(n-mu_0, new_la):
|
|
1803
|
+
yield [mu_0] + mu
|
|
1804
|
+
|
|
1805
|
+
def PP_first_row_iter(n, la):
|
|
1806
|
+
m = n - sum(la)
|
|
1807
|
+
if m < 0:
|
|
1808
|
+
return
|
|
1809
|
+
if m == 0:
|
|
1810
|
+
yield [la]
|
|
1811
|
+
return
|
|
1812
|
+
for k in range(m, 0, -1):
|
|
1813
|
+
for mu in P_in_shape_iter(k, la):
|
|
1814
|
+
for PP in PP_first_row_iter(m, mu):
|
|
1815
|
+
yield [la] + PP
|
|
1816
|
+
|
|
1817
|
+
n = self._n
|
|
1818
|
+
if not n:
|
|
1819
|
+
yield PlanePartition([])
|
|
1820
|
+
return
|
|
1821
|
+
|
|
1822
|
+
for m in range(n, 0, -1):
|
|
1823
|
+
for la in Partitions(m):
|
|
1824
|
+
for a in PP_first_row_iter(n, la):
|
|
1825
|
+
yield self.element_class(self, a, check=False)
|
|
1826
|
+
|
|
1827
|
+
def cardinality(self) -> Integer:
|
|
1828
|
+
r"""
|
|
1829
|
+
Return the number of plane partitions with ``n`` boxes.
|
|
1830
|
+
|
|
1831
|
+
Calculated using the recurrence relation
|
|
1832
|
+
|
|
1833
|
+
.. MATH::
|
|
1834
|
+
|
|
1835
|
+
PL(n) = \sum_{k=1}^n PL(n-k) \sigma_2(k),
|
|
1836
|
+
|
|
1837
|
+
where `\sigma_k(n)` is the sum of the `k`-th powers of
|
|
1838
|
+
divisors of `n`.
|
|
1839
|
+
|
|
1840
|
+
EXAMPLES::
|
|
1841
|
+
|
|
1842
|
+
sage: P = PlanePartitions(17)
|
|
1843
|
+
sage: P.cardinality()
|
|
1844
|
+
18334
|
|
1845
|
+
"""
|
|
1846
|
+
PPn = [1]
|
|
1847
|
+
for i in range(1, 1+self._n):
|
|
1848
|
+
nextPPn = sum(PPn[i-k] * Sigma()(k, 2) for k in range(1, i+1)) / i
|
|
1849
|
+
PPn.append(nextPPn)
|
|
1850
|
+
return Integer(PPn[-1])
|
|
1851
|
+
|
|
1852
|
+
|
|
1853
|
+
# Symmetry classes are enumerated and labelled in order as in Proofs and
|
|
1854
|
+
# Confirmations/Stanley (with all plane partitions being the first class)
|
|
1855
|
+
|
|
1856
|
+
# Class 2
|
|
1857
|
+
# Symmetric Plane Partitions
|
|
1858
|
+
class PlanePartitions_SPP(PlanePartitions):
|
|
1859
|
+
r"""
|
|
1860
|
+
Plane partitions that fit inside a box of a specified size that are
|
|
1861
|
+
symmetric.
|
|
1862
|
+
"""
|
|
1863
|
+
def __init__(self, box_size):
|
|
1864
|
+
"""
|
|
1865
|
+
Initialize ``self``.
|
|
1866
|
+
|
|
1867
|
+
TESTS::
|
|
1868
|
+
|
|
1869
|
+
sage: PP = PlanePartitions([3,3,2], symmetry='SPP')
|
|
1870
|
+
sage: TestSuite(PP).run() # needs sage.graphs sage.modules
|
|
1871
|
+
sage: PlanePartitions([4,3,2], symmetry='SPP')
|
|
1872
|
+
Traceback (most recent call last):
|
|
1873
|
+
...
|
|
1874
|
+
ValueError: x and y dimensions (4 and 3) must be equal
|
|
1875
|
+
"""
|
|
1876
|
+
if box_size[0] != box_size[1]:
|
|
1877
|
+
raise ValueError("x and y dimensions ({} and {}) must be equal".format(box_size[0], box_size[1]))
|
|
1878
|
+
super().__init__(box_size, "SPP", category=FiniteEnumeratedSets())
|
|
1879
|
+
|
|
1880
|
+
def _repr_(self) -> str:
|
|
1881
|
+
"""
|
|
1882
|
+
EXAMPLES::
|
|
1883
|
+
|
|
1884
|
+
sage: PlanePartitions([3,3,2], symmetry='SPP')
|
|
1885
|
+
Symmetric plane partitions inside a 3 x 3 x 2 box
|
|
1886
|
+
"""
|
|
1887
|
+
return "Symmetric plane partitions inside a {} x {} x {} box".format(
|
|
1888
|
+
self._box[0], self._box[1], self._box[2])
|
|
1889
|
+
|
|
1890
|
+
def __contains__(self, x) -> bool:
|
|
1891
|
+
"""
|
|
1892
|
+
TESTS::
|
|
1893
|
+
|
|
1894
|
+
sage: [[2,1],[1]] in PlanePartitions([2,2,2], symmetry='SPP')
|
|
1895
|
+
True
|
|
1896
|
+
sage: [[2,1],[1]] in PlanePartitions([1,1,1], symmetry='SPP')
|
|
1897
|
+
False
|
|
1898
|
+
sage: [[2,1],[2]] in PlanePartitions([2,2,2], symmetry='SPP')
|
|
1899
|
+
False
|
|
1900
|
+
"""
|
|
1901
|
+
P = PlanePartition(x)
|
|
1902
|
+
max = (P._max_x, P._max_y, P._max_z)
|
|
1903
|
+
return (PlanePartitions.__contains__(self, x)
|
|
1904
|
+
and P.is_SPP()
|
|
1905
|
+
and all(a <= b for a, b in zip(max, self._box)))
|
|
1906
|
+
|
|
1907
|
+
def to_poset(self):
|
|
1908
|
+
r"""
|
|
1909
|
+
Return a poset whose order ideals are in bijection with
|
|
1910
|
+
symmetric plane partitions.
|
|
1911
|
+
|
|
1912
|
+
EXAMPLES::
|
|
1913
|
+
|
|
1914
|
+
sage: PP = PlanePartitions([3,3,2], symmetry='SPP')
|
|
1915
|
+
sage: PP.to_poset() # needs sage.graphs
|
|
1916
|
+
Finite poset containing 12 elements
|
|
1917
|
+
sage: PP.to_poset().order_ideals_lattice().cardinality() == PP.cardinality() # needs sage.graphs sage.modules sage.rings.finite_rings
|
|
1918
|
+
True
|
|
1919
|
+
"""
|
|
1920
|
+
a = self._box[0]
|
|
1921
|
+
c = self._box[2]
|
|
1922
|
+
|
|
1923
|
+
def comp(x, y):
|
|
1924
|
+
return all(a <= b for a, b in zip(x, y))
|
|
1925
|
+
|
|
1926
|
+
pl = [(x, y, z) for x in range(a) for y in range(x + 1)
|
|
1927
|
+
for z in range(c)]
|
|
1928
|
+
from sage.combinat.posets.posets import Poset
|
|
1929
|
+
return Poset((pl, comp))
|
|
1930
|
+
|
|
1931
|
+
def from_order_ideal(self, I) -> PP:
|
|
1932
|
+
r"""
|
|
1933
|
+
Return the symmetric plane partition corresponding to an order ideal
|
|
1934
|
+
in the poset given in :meth:`to_poset()`.
|
|
1935
|
+
|
|
1936
|
+
EXAMPLES::
|
|
1937
|
+
|
|
1938
|
+
sage: PP = PlanePartitions([3,3,2], symmetry='SPP')
|
|
1939
|
+
sage: I = [(0, 0, 0), (1, 0, 0), (1, 1, 0), (2, 0, 0)]
|
|
1940
|
+
sage: PP.from_order_ideal(I) # needs sage.graphs
|
|
1941
|
+
Plane partition [[1, 1, 1], [1, 1], [1]]
|
|
1942
|
+
"""
|
|
1943
|
+
return self.from_antichain(self.to_poset().order_ideal_generators(I))
|
|
1944
|
+
|
|
1945
|
+
def from_antichain(self, A) -> PP:
|
|
1946
|
+
r"""
|
|
1947
|
+
Return the symmetric plane partition corresponding to an antichain
|
|
1948
|
+
in the poset given in :meth:`to_poset()`.
|
|
1949
|
+
|
|
1950
|
+
EXAMPLES::
|
|
1951
|
+
|
|
1952
|
+
sage: PP = PlanePartitions([3,3,2], symmetry='SPP')
|
|
1953
|
+
sage: A = [(2, 2, 0), (1, 0, 1), (1, 1, 0)]
|
|
1954
|
+
sage: PP.from_antichain(A)
|
|
1955
|
+
Plane partition [[2, 2, 1], [2, 1, 1], [1, 1, 1]]
|
|
1956
|
+
"""
|
|
1957
|
+
# Initialize an empty plane partition
|
|
1958
|
+
a = self._box[0]
|
|
1959
|
+
b = self._box[1]
|
|
1960
|
+
pp_matrix = [[0] * (b) for i in range(a)]
|
|
1961
|
+
# Antichain indicates where the 'corners' will be in the plane partition
|
|
1962
|
+
for ac in A:
|
|
1963
|
+
x = ac[0]
|
|
1964
|
+
y = ac[1]
|
|
1965
|
+
z = ac[2]
|
|
1966
|
+
pp_matrix[x][y] = z + 1
|
|
1967
|
+
# Fill out the rest of the plane partition using symmetry and the
|
|
1968
|
+
# rule pp[i][j]=max(pp[i][j+1],pp[i+1][j])
|
|
1969
|
+
if A:
|
|
1970
|
+
for i in range(a):
|
|
1971
|
+
i = a - (i + 1)
|
|
1972
|
+
for j in range(b):
|
|
1973
|
+
j = b - (j + 1)
|
|
1974
|
+
if pp_matrix[i][j] == 0 and i >= j:
|
|
1975
|
+
iValue = 0
|
|
1976
|
+
jValue = 0
|
|
1977
|
+
if i < a - 1:
|
|
1978
|
+
iValue = pp_matrix[i+1][j]
|
|
1979
|
+
if j < b - 1:
|
|
1980
|
+
jValue = pp_matrix[i][j+1]
|
|
1981
|
+
pp_matrix[i][j] = max(iValue, jValue)
|
|
1982
|
+
elif j > i:
|
|
1983
|
+
pp_matrix[i][j] = pp_matrix[j][i]
|
|
1984
|
+
return self.element_class(self, pp_matrix)
|
|
1985
|
+
|
|
1986
|
+
def __iter__(self) -> Iterator:
|
|
1987
|
+
"""
|
|
1988
|
+
Iterate over all symmetric plane partitions.
|
|
1989
|
+
|
|
1990
|
+
EXAMPLES::
|
|
1991
|
+
|
|
1992
|
+
sage: list(PlanePartitions([2,2,1], symmetry='SPP')) # needs sage.graphs sage.modules sage.rings.finite_rings
|
|
1993
|
+
[Plane partition [],
|
|
1994
|
+
Plane partition [[1, 1], [1, 1]],
|
|
1995
|
+
Plane partition [[1, 1], [1]],
|
|
1996
|
+
Plane partition [[1]]]
|
|
1997
|
+
|
|
1998
|
+
TESTS::
|
|
1999
|
+
|
|
2000
|
+
sage: all(len(set(PP)) == PP.cardinality() # needs sage.graphs sage.modules
|
|
2001
|
+
....: for a, b in cartesian_product([range(4)]*2)
|
|
2002
|
+
....: if (PP := PlanePartitions([a, a, b], symmetry='SPP')))
|
|
2003
|
+
True
|
|
2004
|
+
"""
|
|
2005
|
+
for acl in self.to_poset().antichains_iterator():
|
|
2006
|
+
yield self.from_antichain(acl)
|
|
2007
|
+
|
|
2008
|
+
def cardinality(self) -> Integer:
|
|
2009
|
+
r"""
|
|
2010
|
+
Return the cardinality of ``self``.
|
|
2011
|
+
|
|
2012
|
+
The number of symmetric plane partitions inside an `a \times a \times b`
|
|
2013
|
+
box is equal to
|
|
2014
|
+
|
|
2015
|
+
.. MATH::
|
|
2016
|
+
|
|
2017
|
+
\left(\prod_{i=1}^{a} \frac{2i + b - 1}{2i - 1}\right)
|
|
2018
|
+
\left(\prod_{1 \leq i < j \leq a} \frac{i+j+b-1}{i+j-1}\right).
|
|
2019
|
+
|
|
2020
|
+
EXAMPLES::
|
|
2021
|
+
|
|
2022
|
+
sage: P = PlanePartitions([3,3,2], symmetry='SPP')
|
|
2023
|
+
sage: P.cardinality()
|
|
2024
|
+
35
|
|
2025
|
+
"""
|
|
2026
|
+
a = self._box[0]
|
|
2027
|
+
c = self._box[2]
|
|
2028
|
+
left_prod_num = prod(2*i + c - 1 for i in range(1, a+1))
|
|
2029
|
+
left_prod_den = prod(2*i - 1 for i in range(1, a+1))
|
|
2030
|
+
right_prod_num = prod(i + j + c - 1
|
|
2031
|
+
for j in range(1, a+1)
|
|
2032
|
+
for i in range(1, j))
|
|
2033
|
+
right_prod_den = prod(i + j - 1
|
|
2034
|
+
for j in range(1, a+1)
|
|
2035
|
+
for i in range(1, j))
|
|
2036
|
+
return Integer(left_prod_num * right_prod_num // left_prod_den // right_prod_den)
|
|
2037
|
+
|
|
2038
|
+
def random_element(self) -> PP:
|
|
2039
|
+
r"""
|
|
2040
|
+
Return a uniformly random element of ``self``.
|
|
2041
|
+
|
|
2042
|
+
ALGORITHM:
|
|
2043
|
+
|
|
2044
|
+
This uses the
|
|
2045
|
+
:meth:`~sage.combinat.posets.posets.FinitePoset.random_order_ideal`
|
|
2046
|
+
method and the natural bijection between symmetric plane partitions
|
|
2047
|
+
and order ideals in an associated poset.
|
|
2048
|
+
|
|
2049
|
+
EXAMPLES::
|
|
2050
|
+
|
|
2051
|
+
sage: PP = PlanePartitions([3,3,2], symmetry='SPP')
|
|
2052
|
+
sage: PP.random_element() # random # needs sage.graphs
|
|
2053
|
+
Plane partition [[2, 2, 2], [2, 2], [2]]
|
|
2054
|
+
"""
|
|
2055
|
+
Z = self.from_order_ideal(self.to_poset().random_order_ideal())
|
|
2056
|
+
return self.element_class(self, Z, check=False)
|
|
2057
|
+
|
|
2058
|
+
|
|
2059
|
+
# Class 3
|
|
2060
|
+
# Cyclically Symmetric Plane Partitions
|
|
2061
|
+
class PlanePartitions_CSPP(PlanePartitions):
|
|
2062
|
+
r"""
|
|
2063
|
+
Plane partitions that fit inside a box of a specified size that are
|
|
2064
|
+
cyclically symmetric.
|
|
2065
|
+
"""
|
|
2066
|
+
def __init__(self, box_size):
|
|
2067
|
+
"""
|
|
2068
|
+
Initialize ``self``.
|
|
2069
|
+
|
|
2070
|
+
TESTS::
|
|
2071
|
+
|
|
2072
|
+
sage: PP = PlanePartitions([3,3,3], symmetry='CSPP')
|
|
2073
|
+
sage: TestSuite(PP).run() # needs sage.graphs sage.modules sage.rings.finite_rings
|
|
2074
|
+
sage: PlanePartitions([4,3,2], symmetry='CSPP')
|
|
2075
|
+
Traceback (most recent call last):
|
|
2076
|
+
...
|
|
2077
|
+
ValueError: x, y, and z dimensions (4,3,2) must all be equal
|
|
2078
|
+
"""
|
|
2079
|
+
if box_size[0] != box_size[1] or box_size[1] != box_size[2]:
|
|
2080
|
+
raise ValueError("x, y, and z dimensions ({},{},{}) must all be equal".format(*box_size))
|
|
2081
|
+
super().__init__(box_size, "CSPP", category=FiniteEnumeratedSets())
|
|
2082
|
+
|
|
2083
|
+
def _repr_(self) -> str:
|
|
2084
|
+
"""
|
|
2085
|
+
EXAMPLES::
|
|
2086
|
+
|
|
2087
|
+
sage: PlanePartitions([3,3,3], symmetry='CSPP')
|
|
2088
|
+
Cyclically symmetric plane partitions inside a 3 x 3 x 3 box
|
|
2089
|
+
"""
|
|
2090
|
+
return "Cyclically symmetric plane partitions inside a {} x {} x {} box".format(
|
|
2091
|
+
self._box[0], self._box[1], self._box[2])
|
|
2092
|
+
|
|
2093
|
+
def __contains__(self, x) -> bool:
|
|
2094
|
+
"""
|
|
2095
|
+
TESTS::
|
|
2096
|
+
|
|
2097
|
+
sage: [[2,1],[1]] in PlanePartitions([2,2,2], symmetry='CSPP')
|
|
2098
|
+
True
|
|
2099
|
+
sage: [[2,1],[1]] in PlanePartitions([1,1,1], symmetry='CSPP')
|
|
2100
|
+
False
|
|
2101
|
+
sage: [[2,1],[2]] in PlanePartitions([2,2,2], symmetry='CSPP')
|
|
2102
|
+
False
|
|
2103
|
+
"""
|
|
2104
|
+
P = PlanePartition(x)
|
|
2105
|
+
max = (P._max_x, P._max_y, P._max_z)
|
|
2106
|
+
return (PlanePartitions.__contains__(self, x)
|
|
2107
|
+
and P.is_CSPP()
|
|
2108
|
+
and all(a <= b for a, b in zip(max, self._box)))
|
|
2109
|
+
|
|
2110
|
+
def to_poset(self):
|
|
2111
|
+
"""
|
|
2112
|
+
Return a partially ordered set whose order ideals are in bijection with
|
|
2113
|
+
cyclically symmetric plane partitions.
|
|
2114
|
+
|
|
2115
|
+
EXAMPLES::
|
|
2116
|
+
|
|
2117
|
+
sage: PP = PlanePartitions([3,3,3], symmetry='CSPP')
|
|
2118
|
+
sage: PP.to_poset() # needs sage.graphs
|
|
2119
|
+
Finite poset containing 11 elements
|
|
2120
|
+
sage: PP.to_poset().order_ideals_lattice().cardinality() == PP.cardinality() # needs sage.graphs sage.modules
|
|
2121
|
+
True
|
|
2122
|
+
"""
|
|
2123
|
+
a = self._box[0]
|
|
2124
|
+
b = self._box[1]
|
|
2125
|
+
c = self._box[2]
|
|
2126
|
+
|
|
2127
|
+
def comp(x, y):
|
|
2128
|
+
return all(a <= b for a, b in zip(x, y))
|
|
2129
|
+
|
|
2130
|
+
def comp2(x, y):
|
|
2131
|
+
return comp(x, y) or comp(x, (y[2], y[0], y[1])) or comp(x, (y[1], y[2], y[0]))
|
|
2132
|
+
|
|
2133
|
+
pl = [(x, y, z) for x in range(a) for y in range(b) for z in range(x, c)
|
|
2134
|
+
if y <= z and (x != z or y == x)]
|
|
2135
|
+
from sage.combinat.posets.posets import Poset
|
|
2136
|
+
return Poset((pl, comp2))
|
|
2137
|
+
|
|
2138
|
+
def from_antichain(self, acl) -> PP:
|
|
2139
|
+
r"""
|
|
2140
|
+
Return the cyclically symmetric plane partition corresponding to an
|
|
2141
|
+
antichain in the poset given in :meth:`to_poset()`.
|
|
2142
|
+
|
|
2143
|
+
EXAMPLES::
|
|
2144
|
+
|
|
2145
|
+
sage: PP = PlanePartitions([3,3,3], symmetry='CSPP')
|
|
2146
|
+
sage: A = [(0, 2, 2), (1, 1, 1)]
|
|
2147
|
+
sage: PP.from_antichain(A)
|
|
2148
|
+
Plane partition [[3, 3, 3], [3, 2, 1], [3, 1, 1]]
|
|
2149
|
+
"""
|
|
2150
|
+
b = self._box[1]
|
|
2151
|
+
c = self._box[2]
|
|
2152
|
+
pp_matrix = [[0] * (c) for i in range(b)]
|
|
2153
|
+
# creates a matrix for the plane partition populated by 0s
|
|
2154
|
+
# EX: [[0,0,0], [0,0,0], [0,0,0]]
|
|
2155
|
+
# ac format ex: [x,y,z]
|
|
2156
|
+
for ac in acl:
|
|
2157
|
+
x = ac[0]
|
|
2158
|
+
y = ac[1]
|
|
2159
|
+
z = ac[2]
|
|
2160
|
+
pp_matrix[y][z] = (x+1)
|
|
2161
|
+
pp_matrix[z][x] = (y+1)
|
|
2162
|
+
pp_matrix[x][y] = (z+1)
|
|
2163
|
+
|
|
2164
|
+
# For each value in current antichain, fill in the rest of the
|
|
2165
|
+
# matrix by rule M[y,z] = Max(M[y+1,z], M[y,z+1]) antichain is
|
|
2166
|
+
# now in plane partition format.
|
|
2167
|
+
if acl != []:
|
|
2168
|
+
for i in range(b):
|
|
2169
|
+
i = b - (i + 1)
|
|
2170
|
+
for j in range(c):
|
|
2171
|
+
j = c - (j + 1)
|
|
2172
|
+
if pp_matrix[i][j] == 0:
|
|
2173
|
+
iValue = 0
|
|
2174
|
+
jValue = 0
|
|
2175
|
+
if i < b - 1:
|
|
2176
|
+
iValue = pp_matrix[i+1][j]
|
|
2177
|
+
if j < c - 1:
|
|
2178
|
+
jValue = pp_matrix[i][j+1]
|
|
2179
|
+
pp_matrix[i][j] = max(iValue, jValue)
|
|
2180
|
+
return self.element_class(self, pp_matrix)
|
|
2181
|
+
|
|
2182
|
+
def from_order_ideal(self, I) -> PP:
|
|
2183
|
+
r"""
|
|
2184
|
+
Return the cyclically symmetric plane partition corresponding
|
|
2185
|
+
to an order ideal in the poset given in :meth:`to_poset`.
|
|
2186
|
+
|
|
2187
|
+
EXAMPLES::
|
|
2188
|
+
|
|
2189
|
+
sage: PP = PlanePartitions([3,3,3], symmetry='CSPP')
|
|
2190
|
+
sage: I = [(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 1, 1), (0, 1, 2),
|
|
2191
|
+
....: (1, 0, 2), (0, 2, 2), (1, 1, 1), (1, 1, 2), (1, 2, 2)]
|
|
2192
|
+
sage: PP.from_order_ideal(I) # needs sage.graphs
|
|
2193
|
+
Plane partition [[3, 3, 3], [3, 3, 3], [3, 3, 2]]
|
|
2194
|
+
"""
|
|
2195
|
+
return self.from_antichain(self.to_poset().order_ideal_generators(I))
|
|
2196
|
+
|
|
2197
|
+
def random_element(self) -> PP:
|
|
2198
|
+
r"""
|
|
2199
|
+
Return a uniformly random element of ``self``.
|
|
2200
|
+
|
|
2201
|
+
ALGORITHM:
|
|
2202
|
+
|
|
2203
|
+
This uses the
|
|
2204
|
+
:meth:`~sage.combinat.posets.posets.FinitePoset.random_order_ideal`
|
|
2205
|
+
method and the natural bijection between cyclically symmetric plane
|
|
2206
|
+
partitions and order ideals in an associated poset.
|
|
2207
|
+
|
|
2208
|
+
EXAMPLES::
|
|
2209
|
+
|
|
2210
|
+
sage: PP = PlanePartitions([3,3,3], symmetry='CSPP')
|
|
2211
|
+
sage: PP.random_element() # random # needs sage.graphs
|
|
2212
|
+
Plane partition [[3, 2, 2], [3, 1], [1, 1]]
|
|
2213
|
+
"""
|
|
2214
|
+
Z = self.from_order_ideal(self.to_poset().random_order_ideal())
|
|
2215
|
+
return self.element_class(self, Z, check=False)
|
|
2216
|
+
|
|
2217
|
+
def __iter__(self) -> Iterator:
|
|
2218
|
+
"""
|
|
2219
|
+
Iterate over all cyclically symmetric plane partitions.
|
|
2220
|
+
|
|
2221
|
+
EXAMPLES::
|
|
2222
|
+
|
|
2223
|
+
sage: list(PlanePartitions([2,2,2], symmetry='CSPP')) # needs sage.graphs sage.modules
|
|
2224
|
+
[Plane partition [],
|
|
2225
|
+
Plane partition [[2, 2], [2, 2]],
|
|
2226
|
+
Plane partition [[2, 2], [2, 1]],
|
|
2227
|
+
Plane partition [[2, 1], [1]],
|
|
2228
|
+
Plane partition [[1]]]
|
|
2229
|
+
|
|
2230
|
+
TESTS::
|
|
2231
|
+
|
|
2232
|
+
sage: all(len(set(PP)) == PP.cardinality() for n in range(5) # needs sage.graphs sage.modules
|
|
2233
|
+
....: if (PP := PlanePartitions([n]*3, symmetry='CSPP')))
|
|
2234
|
+
True
|
|
2235
|
+
"""
|
|
2236
|
+
for acl in self.to_poset().antichains_iterator():
|
|
2237
|
+
yield self.from_antichain(acl)
|
|
2238
|
+
|
|
2239
|
+
def cardinality(self) -> Integer:
|
|
2240
|
+
r"""
|
|
2241
|
+
Return the cardinality of ``self``.
|
|
2242
|
+
|
|
2243
|
+
The number of cyclically symmetric plane partitions inside an
|
|
2244
|
+
`a \times a \times a` box is equal to
|
|
2245
|
+
|
|
2246
|
+
.. MATH::
|
|
2247
|
+
|
|
2248
|
+
\left(\prod_{i=1}^{a} \frac{3i - 1}{3i - 2}\right)
|
|
2249
|
+
\left(\prod_{1 \leq i < j \leq a} \frac{i+j+a-1}{2i+j-1}\right).
|
|
2250
|
+
|
|
2251
|
+
EXAMPLES::
|
|
2252
|
+
|
|
2253
|
+
sage: P = PlanePartitions([4,4,4], symmetry='CSPP')
|
|
2254
|
+
sage: P.cardinality()
|
|
2255
|
+
132
|
|
2256
|
+
"""
|
|
2257
|
+
a = self._box[0]
|
|
2258
|
+
num = (prod(3*i - 1 for i in range(1, a + 1))
|
|
2259
|
+
* prod(i + j + a - 1 for j in range(1, a + 1)
|
|
2260
|
+
for i in range(1, j + 1)))
|
|
2261
|
+
den = (prod(3*i - 2 for i in range(1, a + 1))
|
|
2262
|
+
* prod(2*i + j - 1 for j in range(1, a + 1)
|
|
2263
|
+
for i in range(1, j + 1)))
|
|
2264
|
+
return Integer(num // den)
|
|
2265
|
+
|
|
2266
|
+
|
|
2267
|
+
# Class 4
|
|
2268
|
+
# Totally Symmetric Plane Partitions
|
|
2269
|
+
class PlanePartitions_TSPP(PlanePartitions):
|
|
2270
|
+
r"""
|
|
2271
|
+
Plane partitions that fit inside a box of a specified size that are
|
|
2272
|
+
totally symmetric.
|
|
2273
|
+
"""
|
|
2274
|
+
def __init__(self, box_size):
|
|
2275
|
+
"""
|
|
2276
|
+
Initialize ``self``.
|
|
2277
|
+
|
|
2278
|
+
TESTS::
|
|
2279
|
+
|
|
2280
|
+
sage: PP = PlanePartitions([3,3,3], symmetry='TSPP')
|
|
2281
|
+
sage: TestSuite(PP).run() # needs sage.graphs sage.modules
|
|
2282
|
+
sage: PlanePartitions([4,3,2], symmetry='TSPP')
|
|
2283
|
+
Traceback (most recent call last):
|
|
2284
|
+
...
|
|
2285
|
+
ValueError: x, y, and z dimensions (4,3,2) must all be equal
|
|
2286
|
+
"""
|
|
2287
|
+
if box_size[0] != box_size[1] or box_size[1] != box_size[2]:
|
|
2288
|
+
raise ValueError("x, y, and z dimensions ({},{},{}) must all be equal".format(box_size[0], box_size[1], box_size[2]))
|
|
2289
|
+
super().__init__(box_size, "TSPP", category=FiniteEnumeratedSets())
|
|
2290
|
+
|
|
2291
|
+
def _repr_(self) -> str:
|
|
2292
|
+
"""
|
|
2293
|
+
EXAMPLES::
|
|
2294
|
+
|
|
2295
|
+
sage: PlanePartitions([3,3,3], symmetry='TSPP')
|
|
2296
|
+
Totally symmetric plane partitions inside a 3 x 3 x 3 box
|
|
2297
|
+
"""
|
|
2298
|
+
return "Totally symmetric plane partitions inside a {} x {} x {} box".format(
|
|
2299
|
+
self._box[0], self._box[1], self._box[2])
|
|
2300
|
+
|
|
2301
|
+
def __contains__(self, x) -> bool:
|
|
2302
|
+
"""
|
|
2303
|
+
TESTS::
|
|
2304
|
+
|
|
2305
|
+
sage: [[2,1],[1]] in PlanePartitions([2,2,2], symmetry='TSPP')
|
|
2306
|
+
True
|
|
2307
|
+
sage: [[2,1],[1]] in PlanePartitions([1,1,1], symmetry='TSPP')
|
|
2308
|
+
False
|
|
2309
|
+
sage: [[2,1],[2]] in PlanePartitions([2,2,2], symmetry='TSPP')
|
|
2310
|
+
False
|
|
2311
|
+
"""
|
|
2312
|
+
P = PlanePartition(x)
|
|
2313
|
+
maxval = (P._max_x, P._max_y, P._max_z)
|
|
2314
|
+
return (PlanePartitions.__contains__(self, x) and P.is_TSPP()
|
|
2315
|
+
and all(a <= b for a, b in zip(maxval, self._box)))
|
|
2316
|
+
|
|
2317
|
+
def to_poset(self):
|
|
2318
|
+
r"""
|
|
2319
|
+
Return a poset whose order ideals are in bijection with totally
|
|
2320
|
+
symmetric plane partitions.
|
|
2321
|
+
|
|
2322
|
+
EXAMPLES::
|
|
2323
|
+
|
|
2324
|
+
sage: PP = PlanePartitions([3,3,3], symmetry='TSPP')
|
|
2325
|
+
sage: PP.to_poset() # needs sage.graphs
|
|
2326
|
+
Finite poset containing 10 elements
|
|
2327
|
+
sage: (PP.to_poset().order_ideals_lattice().cardinality() # needs sage.graphs sage.modules sage.rings.finite_rings
|
|
2328
|
+
....: == PP.cardinality())
|
|
2329
|
+
True
|
|
2330
|
+
"""
|
|
2331
|
+
a = self._box[0]
|
|
2332
|
+
b = self._box[1]
|
|
2333
|
+
c = self._box[2]
|
|
2334
|
+
|
|
2335
|
+
def comp(x, y):
|
|
2336
|
+
return all(a <= b for a, b in zip(x, y))
|
|
2337
|
+
|
|
2338
|
+
pl = [(x, y, z) for x in range(a) for y in range(x, b) for z in range(y, c)]
|
|
2339
|
+
from sage.combinat.posets.posets import Poset
|
|
2340
|
+
return Poset((pl, comp))
|
|
2341
|
+
|
|
2342
|
+
def from_antichain(self, acl) -> PP:
|
|
2343
|
+
r"""
|
|
2344
|
+
Return the totally symmetric plane partition corresponding to an
|
|
2345
|
+
antichain in the poset given in :meth:`to_poset()`.
|
|
2346
|
+
|
|
2347
|
+
EXAMPLES::
|
|
2348
|
+
|
|
2349
|
+
sage: PP = PlanePartitions([3,3,3], symmetry='TSPP')
|
|
2350
|
+
sage: A = [(0, 0, 2), (0, 1, 1)]
|
|
2351
|
+
sage: PP.from_antichain(A)
|
|
2352
|
+
Plane partition [[3, 2, 1], [2, 1], [1]]
|
|
2353
|
+
"""
|
|
2354
|
+
b = self._box[1]
|
|
2355
|
+
c = self._box[2]
|
|
2356
|
+
pp_matrix = [[0] * (c) for i in range(b)] # creates a matrix for the plane
|
|
2357
|
+
# partition populated by 0s EX: [[0,0,0], [0,0,0], [0,0,0]]
|
|
2358
|
+
for ac in acl:
|
|
2359
|
+
x = ac[0]
|
|
2360
|
+
y = ac[1]
|
|
2361
|
+
z = ac[2]
|
|
2362
|
+
|
|
2363
|
+
pp_matrix[y][z] = x + 1 # x,y,z
|
|
2364
|
+
pp_matrix[z][x] = y + 1 # y,z,x
|
|
2365
|
+
pp_matrix[x][y] = z + 1 # z,x,y
|
|
2366
|
+
|
|
2367
|
+
pp_matrix[z][y] = x + 1 # x,z,y
|
|
2368
|
+
pp_matrix[x][z] = y + 1 # y,x,z
|
|
2369
|
+
pp_matrix[y][x] = z + 1 # z,y,x
|
|
2370
|
+
|
|
2371
|
+
# for each value in current antichain, fill in the rest of the matrix by
|
|
2372
|
+
# rule M[y,z] = Max(M[y+1,z], M[y,z+1]) antichain is now in plane partition format
|
|
2373
|
+
if acl != []:
|
|
2374
|
+
for i in range(b):
|
|
2375
|
+
i = b - (i + 1)
|
|
2376
|
+
for j in range(c):
|
|
2377
|
+
j = c - (j + 1)
|
|
2378
|
+
if pp_matrix[i][j] == 0:
|
|
2379
|
+
iValue = 0
|
|
2380
|
+
jValue = 0
|
|
2381
|
+
if i < b - 1:
|
|
2382
|
+
iValue = pp_matrix[i+1][j]
|
|
2383
|
+
if j < c - 1:
|
|
2384
|
+
jValue = pp_matrix[i][j+1]
|
|
2385
|
+
pp_matrix[i][j] = max(iValue, jValue)
|
|
2386
|
+
return self.element_class(self, pp_matrix)
|
|
2387
|
+
|
|
2388
|
+
def from_order_ideal(self, I) -> PP:
|
|
2389
|
+
r"""
|
|
2390
|
+
Return the totally symmetric plane partition corresponding
|
|
2391
|
+
to an order ideal in the poset given in :meth:`to_poset`.
|
|
2392
|
+
|
|
2393
|
+
EXAMPLES::
|
|
2394
|
+
|
|
2395
|
+
sage: PP = PlanePartitions([3,3,3], symmetry='TSPP')
|
|
2396
|
+
sage: I = [(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 1, 1)]
|
|
2397
|
+
sage: PP.from_order_ideal(I) # needs sage.graphs
|
|
2398
|
+
Plane partition [[3, 2, 1], [2, 1], [1]]
|
|
2399
|
+
"""
|
|
2400
|
+
return self.from_antichain(self.to_poset().order_ideal_generators(I))
|
|
2401
|
+
|
|
2402
|
+
def __iter__(self) -> Iterator:
|
|
2403
|
+
"""
|
|
2404
|
+
An iterator for totally symmetric plane partitions.
|
|
2405
|
+
|
|
2406
|
+
EXAMPLES::
|
|
2407
|
+
|
|
2408
|
+
sage: list(PlanePartitions([2,2,2], symmetry='TSPP')) # needs sage.graphs sage.modules
|
|
2409
|
+
[Plane partition [],
|
|
2410
|
+
Plane partition [[2, 2], [2, 2]],
|
|
2411
|
+
Plane partition [[2, 2], [2, 1]],
|
|
2412
|
+
Plane partition [[2, 1], [1]],
|
|
2413
|
+
Plane partition [[1]]]
|
|
2414
|
+
|
|
2415
|
+
TESTS::
|
|
2416
|
+
|
|
2417
|
+
sage: all(len(set(PP)) == PP.cardinality() # needs sage.graphs sage.modules
|
|
2418
|
+
....: for n in range(5) if (PP := PlanePartitions([n]*3, symmetry='TSPP')))
|
|
2419
|
+
True
|
|
2420
|
+
"""
|
|
2421
|
+
for acl in self.to_poset().antichains_iterator():
|
|
2422
|
+
yield self.from_antichain(acl)
|
|
2423
|
+
|
|
2424
|
+
def cardinality(self) -> Integer:
|
|
2425
|
+
r"""
|
|
2426
|
+
Return the cardinality of ``self``.
|
|
2427
|
+
|
|
2428
|
+
The number of totally symmetric plane partitions inside an
|
|
2429
|
+
`a \times a \times a` box is equal to
|
|
2430
|
+
|
|
2431
|
+
.. MATH::
|
|
2432
|
+
|
|
2433
|
+
\prod_{1 \leq i \leq j \leq a} \frac{i+j+a-1}{i+2j-2}.
|
|
2434
|
+
|
|
2435
|
+
EXAMPLES::
|
|
2436
|
+
|
|
2437
|
+
sage: P = PlanePartitions([4,4,4], symmetry='TSPP')
|
|
2438
|
+
sage: P.cardinality()
|
|
2439
|
+
66
|
|
2440
|
+
"""
|
|
2441
|
+
a = self._box[0]
|
|
2442
|
+
num = prod(i + j + a - 1 for j in range(1, a + 1) for i in range(1, j + 1))
|
|
2443
|
+
den = prod(i + 2*j - 2 for j in range(1, a + 1) for i in range(1, j + 1))
|
|
2444
|
+
return Integer(num // den)
|
|
2445
|
+
|
|
2446
|
+
|
|
2447
|
+
# Class 5
|
|
2448
|
+
# Self-complementary Plane Partitions
|
|
2449
|
+
class PlanePartitions_SCPP(PlanePartitions):
|
|
2450
|
+
r"""
|
|
2451
|
+
Plane partitions that fit inside a box of a specified size that are
|
|
2452
|
+
self-complementary.
|
|
2453
|
+
"""
|
|
2454
|
+
def __init__(self, box_size):
|
|
2455
|
+
"""
|
|
2456
|
+
Initialize ``self``.
|
|
2457
|
+
|
|
2458
|
+
TESTS::
|
|
2459
|
+
|
|
2460
|
+
sage: PP = PlanePartitions([4,3,2], symmetry='SCPP')
|
|
2461
|
+
sage: TestSuite(PP).run()
|
|
2462
|
+
sage: PlanePartitions([5,3,1], symmetry='SCPP')
|
|
2463
|
+
Traceback (most recent call last):
|
|
2464
|
+
...
|
|
2465
|
+
ValueError: dimensions (5,3,1) cannot all be odd
|
|
2466
|
+
"""
|
|
2467
|
+
if (box_size[0] % 2 == 1 and box_size[1] % 2 == 1 and box_size[2] % 2 == 1):
|
|
2468
|
+
raise ValueError("dimensions ({},{},{}) cannot all be odd".format(*box_size))
|
|
2469
|
+
super().__init__(box_size, "SCPP", category=FiniteEnumeratedSets())
|
|
2470
|
+
|
|
2471
|
+
def __contains__(self, x) -> bool:
|
|
2472
|
+
"""
|
|
2473
|
+
TESTS::
|
|
2474
|
+
|
|
2475
|
+
sage: [[2,1],[1]] in PlanePartitions([2,2,2], symmetry='SCPP')
|
|
2476
|
+
True
|
|
2477
|
+
sage: [[2,1],[1]] in PlanePartitions([3,2,2], symmetry='SCPP')
|
|
2478
|
+
False
|
|
2479
|
+
sage: [[2,1],[1]] in PlanePartitions([2,1,1], symmetry='SCPP')
|
|
2480
|
+
False
|
|
2481
|
+
sage: [[2,1],[2]] in PlanePartitions([2,2,2], symmetry='SCPP')
|
|
2482
|
+
False
|
|
2483
|
+
"""
|
|
2484
|
+
# P = PlanePartitions(self._box)(x)
|
|
2485
|
+
# max = (P._max_x, P._max_y, P._max_z)
|
|
2486
|
+
# return PlanePartitions.__contains__(self, x) and P.is_SCPP() and all( a<=b for a,b in zip(max,self._box))
|
|
2487
|
+
return x in PlanePartitions(self._box) and PlanePartitions(self._box)(x).is_SCPP()
|
|
2488
|
+
|
|
2489
|
+
def _repr_(self) -> str:
|
|
2490
|
+
"""
|
|
2491
|
+
EXAMPLES::
|
|
2492
|
+
|
|
2493
|
+
sage: PlanePartitions([4,3,2], symmetry='SCPP')
|
|
2494
|
+
Self-complementary plane partitions inside a 4 x 3 x 2 box
|
|
2495
|
+
"""
|
|
2496
|
+
return "Self-complementary plane partitions inside a {} x {} x {} box".format(
|
|
2497
|
+
self._box[0], self._box[1], self._box[2])
|
|
2498
|
+
|
|
2499
|
+
def __iter__(self) -> Iterator:
|
|
2500
|
+
"""
|
|
2501
|
+
An iterator for self-complementary plane partitions.
|
|
2502
|
+
|
|
2503
|
+
EXAMPLES::
|
|
2504
|
+
|
|
2505
|
+
sage: list(PlanePartitions([3,2,2], symmetry='SCPP'))
|
|
2506
|
+
[Plane partition [[1, 1], [1, 1], [1, 1]],
|
|
2507
|
+
Plane partition [[2, 1], [1, 1], [1]],
|
|
2508
|
+
Plane partition [[2, 2], [1, 1]],
|
|
2509
|
+
Plane partition [[2], [2], [2]],
|
|
2510
|
+
Plane partition [[2, 1], [2], [1]],
|
|
2511
|
+
Plane partition [[2, 2], [2]]]
|
|
2512
|
+
|
|
2513
|
+
TESTS::
|
|
2514
|
+
|
|
2515
|
+
sage: PP = PlanePartitions([3,4,5], symmetry='SCPP')
|
|
2516
|
+
sage: len(set(PP)) == PP.cardinality()
|
|
2517
|
+
True
|
|
2518
|
+
|
|
2519
|
+
sage: all(len(set(PP)) == PP.cardinality()
|
|
2520
|
+
....: for b in cartesian_product([range(4)]*3)
|
|
2521
|
+
....: if is_even(prod(b)) and (PP := PlanePartitions(b, symmetry='SCPP')))
|
|
2522
|
+
True
|
|
2523
|
+
"""
|
|
2524
|
+
b = self._box[0]
|
|
2525
|
+
a = self._box[1]
|
|
2526
|
+
c = self._box[2]
|
|
2527
|
+
|
|
2528
|
+
def Partitions_inside_lambda(la):
|
|
2529
|
+
"""
|
|
2530
|
+
Iterate over all partitions contained in la with the same number
|
|
2531
|
+
of parts including 0s.
|
|
2532
|
+
"""
|
|
2533
|
+
from sage.combinat.partition import Partitions
|
|
2534
|
+
for k in range(sum(la), -1, -1):
|
|
2535
|
+
for mu in Partitions(k, outer=la):
|
|
2536
|
+
yield mu + [0]*(len(la)-len(mu))
|
|
2537
|
+
|
|
2538
|
+
def Partitions_inside_lambda_with_smallest_at_least_k(la, k):
|
|
2539
|
+
"""
|
|
2540
|
+
Iterate over all partitions contained in la with the smallest
|
|
2541
|
+
entry at least k.
|
|
2542
|
+
"""
|
|
2543
|
+
for mu in Partitions_inside_lambda([val - k for val in la]):
|
|
2544
|
+
yield [mu[i] + k for i in range(len(la))]
|
|
2545
|
+
|
|
2546
|
+
def possible_middle_row_for_b_odd(a, c):
|
|
2547
|
+
"""
|
|
2548
|
+
Iterate over all possible middle row for SCPP inside box(a,b,c)
|
|
2549
|
+
when b is odd.
|
|
2550
|
+
"""
|
|
2551
|
+
if a * c % 2 == 1:
|
|
2552
|
+
yield
|
|
2553
|
+
return
|
|
2554
|
+
for mu in Partitions_inside_lambda([c // 2 for i in range(a // 2)]):
|
|
2555
|
+
nu = [c - mu[len(mu)-1-i] for i in range(len(mu))]
|
|
2556
|
+
if not a % 2:
|
|
2557
|
+
la = nu + mu
|
|
2558
|
+
else:
|
|
2559
|
+
la = nu + [c // 2] + mu
|
|
2560
|
+
yield la
|
|
2561
|
+
|
|
2562
|
+
def possible_middle_row_for_b_even(a, c):
|
|
2563
|
+
"""
|
|
2564
|
+
Iterate over all possible middle ((b/2)+1)st row for SCPP inside
|
|
2565
|
+
box(a,b,c) when b is even.
|
|
2566
|
+
"""
|
|
2567
|
+
for mu in Partitions_inside_lambda([c // 2 for i in range((a+1) // 2)]):
|
|
2568
|
+
if not mu:
|
|
2569
|
+
yield []
|
|
2570
|
+
continue
|
|
2571
|
+
nu = [c - mu[len(mu)-1-i] for i in range(a // 2)]
|
|
2572
|
+
for tau in Partitions_inside_lambda_with_smallest_at_least_k(nu, mu[0]):
|
|
2573
|
+
la = tau + mu
|
|
2574
|
+
yield la
|
|
2575
|
+
|
|
2576
|
+
def PPs_with_first_row_la_and_with_k_rows(la, k):
|
|
2577
|
+
"Iterate over PPs with first row la and with k rows in total."
|
|
2578
|
+
if k == 0:
|
|
2579
|
+
yield []
|
|
2580
|
+
return
|
|
2581
|
+
if k == 1:
|
|
2582
|
+
yield [la]
|
|
2583
|
+
return
|
|
2584
|
+
for mu in Partitions_inside_lambda(la):
|
|
2585
|
+
for PP in PPs_with_first_row_la_and_with_k_rows(mu, k-1):
|
|
2586
|
+
yield [la] + PP
|
|
2587
|
+
|
|
2588
|
+
def complement(PP, c):
|
|
2589
|
+
"Return the complement of PP with respect to height c"
|
|
2590
|
+
b = len(PP)
|
|
2591
|
+
if not b:
|
|
2592
|
+
return []
|
|
2593
|
+
a = len(PP[0])
|
|
2594
|
+
return [[c - PP[b-1-i][a-1-j] for j in range(a)] for i in range(b)]
|
|
2595
|
+
|
|
2596
|
+
if b % 2 == 1:
|
|
2597
|
+
# la is the middle row of SCPP
|
|
2598
|
+
for la in possible_middle_row_for_b_odd(a, c):
|
|
2599
|
+
for PP in PPs_with_first_row_la_and_with_k_rows(la, (b+1) // 2):
|
|
2600
|
+
PP_below = PP[1:]
|
|
2601
|
+
PP_above = complement(PP_below, c)
|
|
2602
|
+
yield self.element_class(self, PP_above + [la] + PP_below)
|
|
2603
|
+
else:
|
|
2604
|
+
# la is the middle ((a/2)+1)st row of SCPP
|
|
2605
|
+
for la in possible_middle_row_for_b_even(a, c):
|
|
2606
|
+
for PP in PPs_with_first_row_la_and_with_k_rows(la, b // 2):
|
|
2607
|
+
PP_below = PP
|
|
2608
|
+
PP_above = complement(PP_below, c)
|
|
2609
|
+
yield self.element_class(self, PP_above + PP_below)
|
|
2610
|
+
|
|
2611
|
+
def cardinality(self) -> Integer:
|
|
2612
|
+
r"""
|
|
2613
|
+
Return the cardinality of ``self``.
|
|
2614
|
+
|
|
2615
|
+
The number of self complementary plane partitions inside a
|
|
2616
|
+
`2a \times 2b \times 2c` box is equal to
|
|
2617
|
+
|
|
2618
|
+
.. MATH::
|
|
2619
|
+
|
|
2620
|
+
\left(\prod_{i=1}^{r}\prod_{j=1}^{b}
|
|
2621
|
+
\frac{i + j + c - 1}{i + j - 1}\right)^2.
|
|
2622
|
+
|
|
2623
|
+
The number of self complementary plane partitions inside an
|
|
2624
|
+
`(2a+1) \times 2b \times 2c` box is equal to
|
|
2625
|
+
|
|
2626
|
+
.. MATH::
|
|
2627
|
+
|
|
2628
|
+
\left(\prod_{i=1}^{a} \prod_{j=1}^{b} \frac{i+j+c-1}{i+j-1} \right)
|
|
2629
|
+
\left(\prod_{i=1}^{a+1} \prod_{j=1}^{b} \frac{i+j+c-1}{i+j-1} \right).
|
|
2630
|
+
|
|
2631
|
+
The number of self complementary plane partitions inside an
|
|
2632
|
+
`(2a+1) \times (2b+1) \times 2c` box is equal to
|
|
2633
|
+
|
|
2634
|
+
.. MATH::
|
|
2635
|
+
|
|
2636
|
+
\left(\prod_{i=1}^{a+1} \prod_{j=1}^{b} \frac{i+j+c-1}{i+j-1} \right)
|
|
2637
|
+
\left(\prod_{i=1}^{a} \prod_{j=1}^{b+1} \frac{i+j+c-1}{i+j-1} \right).
|
|
2638
|
+
|
|
2639
|
+
EXAMPLES::
|
|
2640
|
+
|
|
2641
|
+
sage: P = PlanePartitions([4,4,4], symmetry='SCPP')
|
|
2642
|
+
sage: P.cardinality()
|
|
2643
|
+
400
|
|
2644
|
+
|
|
2645
|
+
sage: P = PlanePartitions([5,4,4], symmetry='SCPP')
|
|
2646
|
+
sage: P.cardinality()
|
|
2647
|
+
1000
|
|
2648
|
+
sage: P = PlanePartitions([4,5,4], symmetry='SCPP')
|
|
2649
|
+
sage: P.cardinality()
|
|
2650
|
+
1000
|
|
2651
|
+
sage: P = PlanePartitions([4,4,5], symmetry='SCPP')
|
|
2652
|
+
sage: P.cardinality()
|
|
2653
|
+
1000
|
|
2654
|
+
|
|
2655
|
+
sage: P = PlanePartitions([5,5,4], symmetry='SCPP')
|
|
2656
|
+
sage: P.cardinality()
|
|
2657
|
+
2500
|
|
2658
|
+
sage: P = PlanePartitions([5,4,5], symmetry='SCPP')
|
|
2659
|
+
sage: P.cardinality()
|
|
2660
|
+
2500
|
|
2661
|
+
sage: P = PlanePartitions([4,5,5], symmetry='SCPP')
|
|
2662
|
+
sage: P.cardinality()
|
|
2663
|
+
2500
|
|
2664
|
+
"""
|
|
2665
|
+
r = self._box[0]
|
|
2666
|
+
s = self._box[1]
|
|
2667
|
+
t = self._box[2]
|
|
2668
|
+
if r % 2 == 0:
|
|
2669
|
+
R = r // 2
|
|
2670
|
+
if s % 2 == 0:
|
|
2671
|
+
S = s // 2
|
|
2672
|
+
if t % 2 == 0:
|
|
2673
|
+
T = t // 2
|
|
2674
|
+
return Integer(prod(Integer(i+j+k-1) / Integer(i+j+k-2)
|
|
2675
|
+
for i in range(1, R+1) for j in range(1, S+1) for k in range(1, T+1))
|
|
2676
|
+
* prod(Integer(i+j+k-1) / Integer(i+j+k-2)
|
|
2677
|
+
for i in range(1, R+1) for j in range(1, S+1) for k in range(1, T+1)))
|
|
2678
|
+
else:
|
|
2679
|
+
T = (t-1) // 2
|
|
2680
|
+
return Integer(prod(Integer(i+j+k-1) / Integer(i+j+k-2)
|
|
2681
|
+
for i in range(1, R+1) for j in range(1, S+1) for k in range(1, T+1))
|
|
2682
|
+
* prod(Integer(i+j+k-1) / Integer(i+j+k-2)
|
|
2683
|
+
for i in range(1, R+1) for j in range(1, S+1) for k in range(1, T+2)))
|
|
2684
|
+
else:
|
|
2685
|
+
S = (s-1) // 2
|
|
2686
|
+
if t % 2 == 0:
|
|
2687
|
+
T = t // 2
|
|
2688
|
+
return Integer(prod(Integer(i+j+k-1) / Integer(i+j+k-2)
|
|
2689
|
+
for i in range(1, R+1) for j in range(1, S+1) for k in range(1, T+1))
|
|
2690
|
+
* prod(Integer(i+j+k-1) / Integer(i+j+k-2)
|
|
2691
|
+
for i in range(1, R+1) for j in range(1, S+2) for k in range(1, T+1)))
|
|
2692
|
+
else:
|
|
2693
|
+
T = (t-1) // 2
|
|
2694
|
+
return Integer(prod(Integer(i+j+k-1) / Integer(i+j+k-2)
|
|
2695
|
+
for i in range(1, R+1) for j in range(1, S+2) for k in range(1, T+1))
|
|
2696
|
+
* prod(Integer(i+j+k-1) / Integer(i+j+k-2)
|
|
2697
|
+
for i in range(1, R+1) for j in range(1, S+1) for k in range(1, T+2)))
|
|
2698
|
+
# r is odd
|
|
2699
|
+
R = (r-1) // 2
|
|
2700
|
+
if s % 2 == 0:
|
|
2701
|
+
S = s // 2
|
|
2702
|
+
if t % 2 == 0:
|
|
2703
|
+
T = t // 2
|
|
2704
|
+
return Integer(prod(Integer(i+j+k-1) / Integer(i+j+k-2)
|
|
2705
|
+
for i in range(1, R+1) for j in range(1, S+1) for k in range(1, T+1))
|
|
2706
|
+
* prod(Integer(i+j+k-1) / Integer(i+j+k-2)
|
|
2707
|
+
for i in range(1, R+2) for j in range(1, S+1) for k in range(1, T+1)))
|
|
2708
|
+
else:
|
|
2709
|
+
T = (t-1) // 2
|
|
2710
|
+
return Integer(prod(Integer(i+j+k-1) / Integer(i+j+k-2)
|
|
2711
|
+
for i in range(1, R+2) for j in range(1, S+1) for k in range(1, T+1))
|
|
2712
|
+
* prod(Integer(i+j+k-1) / Integer(i+j+k-2)
|
|
2713
|
+
for i in range(1, R+1) for j in range(1, S+1) for k in range(1, T+2)))
|
|
2714
|
+
# r and s are both odd
|
|
2715
|
+
S = (s-1) // 2
|
|
2716
|
+
if t % 2 == 0:
|
|
2717
|
+
T = t // 2
|
|
2718
|
+
return Integer(prod(Integer(i+j+k-1) / Integer(i+j+k-2)
|
|
2719
|
+
for i in range(1, R+2) for j in range(1, S+1) for k in range(1, T+1))
|
|
2720
|
+
* prod(Integer(i+j+k-1) / Integer(i+j+k-2)
|
|
2721
|
+
for i in range(1, R+1) for j in range(1, S+2) for k in range(1, T+1)))
|
|
2722
|
+
|
|
2723
|
+
# Should never reach here as r, s, t are all odd, which the constructor should reject
|
|
2724
|
+
return Integer(0)
|
|
2725
|
+
|
|
2726
|
+
|
|
2727
|
+
# Class 6
|
|
2728
|
+
# Transpose-complement Plane Partitions
|
|
2729
|
+
class PlanePartitions_TCPP(PlanePartitions):
|
|
2730
|
+
r"""
|
|
2731
|
+
Plane partitions that fit inside a box of a specified size that are
|
|
2732
|
+
transpose-complement.
|
|
2733
|
+
"""
|
|
2734
|
+
def __init__(self, box_size):
|
|
2735
|
+
"""
|
|
2736
|
+
Initialize ``self``.
|
|
2737
|
+
|
|
2738
|
+
TESTS::
|
|
2739
|
+
|
|
2740
|
+
sage: PP = PlanePartitions([3,3,2], symmetry='TCPP')
|
|
2741
|
+
sage: TestSuite(PP).run() # needs sage.graphs sage.modules
|
|
2742
|
+
|
|
2743
|
+
sage: PlanePartitions([3,3,3], symmetry='TCPP')
|
|
2744
|
+
Traceback (most recent call last):
|
|
2745
|
+
...
|
|
2746
|
+
ValueError: z dimension (3) must be even
|
|
2747
|
+
|
|
2748
|
+
sage: PlanePartitions([4,3,2], symmetry='TCPP')
|
|
2749
|
+
Traceback (most recent call last):
|
|
2750
|
+
...
|
|
2751
|
+
ValueError: x and y dimensions (4 and 3) must be equal
|
|
2752
|
+
"""
|
|
2753
|
+
if box_size[2] % 2 == 1:
|
|
2754
|
+
raise ValueError("z dimension ({}) must be even".format(box_size[2]))
|
|
2755
|
+
if box_size[0] != box_size[1]:
|
|
2756
|
+
raise ValueError("x and y dimensions ({} and {}) must be equal".format(box_size[0], box_size[1]))
|
|
2757
|
+
super().__init__(box_size, "TCPP", category=FiniteEnumeratedSets())
|
|
2758
|
+
|
|
2759
|
+
def _repr_(self) -> str:
|
|
2760
|
+
"""
|
|
2761
|
+
EXAMPLES::
|
|
2762
|
+
|
|
2763
|
+
sage: PlanePartitions([3,3,2], symmetry='TCPP')
|
|
2764
|
+
Transpose complement plane partitions inside a 3 x 3 x 2 box
|
|
2765
|
+
"""
|
|
2766
|
+
return "Transpose complement plane partitions inside a {} x {} x {} box".format(
|
|
2767
|
+
self._box[0], self._box[1], self._box[2])
|
|
2768
|
+
|
|
2769
|
+
def __iter__(self) -> Iterator:
|
|
2770
|
+
r"""
|
|
2771
|
+
Iterate over all transpose complement plane partitions.
|
|
2772
|
+
|
|
2773
|
+
EXAMPLES::
|
|
2774
|
+
|
|
2775
|
+
sage: list(PlanePartitions([3,3,2], symmetry='TCPP')) # needs sage.modules
|
|
2776
|
+
[Plane partition [[2, 2, 1], [2, 1], [1]],
|
|
2777
|
+
Plane partition [[2, 1, 1], [2, 1, 1], [1]],
|
|
2778
|
+
Plane partition [[2, 2, 1], [1, 1], [1, 1]],
|
|
2779
|
+
Plane partition [[2, 1, 1], [1, 1, 1], [1, 1]],
|
|
2780
|
+
Plane partition [[1, 1, 1], [1, 1, 1], [1, 1, 1]]]
|
|
2781
|
+
"""
|
|
2782
|
+
for p in PlanePartitions(self._box):
|
|
2783
|
+
if p.is_TCPP():
|
|
2784
|
+
yield self.element_class(self, p)
|
|
2785
|
+
|
|
2786
|
+
def cardinality(self) -> Integer:
|
|
2787
|
+
r"""
|
|
2788
|
+
Return the cardinality of ``self``.
|
|
2789
|
+
|
|
2790
|
+
The number of transpose complement plane partitions inside an
|
|
2791
|
+
`a \times a \times 2b` box is equal to
|
|
2792
|
+
|
|
2793
|
+
.. MATH::
|
|
2794
|
+
|
|
2795
|
+
\binom{b+1-1}{a-1} \prod_{1\leq i,j \leq a-2}
|
|
2796
|
+
\frac{i + j + 2b - 1}{i + j - 1}.
|
|
2797
|
+
|
|
2798
|
+
EXAMPLES::
|
|
2799
|
+
|
|
2800
|
+
sage: P = PlanePartitions([3,3,2], symmetry='TCPP')
|
|
2801
|
+
sage: P.cardinality()
|
|
2802
|
+
5
|
|
2803
|
+
"""
|
|
2804
|
+
a = self._box[0]
|
|
2805
|
+
c = self._box[2]
|
|
2806
|
+
return Integer(binomial(c // 2 + a - 1, a - 1)
|
|
2807
|
+
* prod(c + i + j + 1
|
|
2808
|
+
for j in range(1, a - 1) for i in range(1, 1 + j))
|
|
2809
|
+
// prod(i + j + 1
|
|
2810
|
+
for j in range(1, a - 1) for i in range(1, 1 + j)))
|
|
2811
|
+
|
|
2812
|
+
|
|
2813
|
+
# Class 7
|
|
2814
|
+
# Symmetric Self-complementary Plane Partitions
|
|
2815
|
+
class PlanePartitions_SSCPP(PlanePartitions):
|
|
2816
|
+
r"""
|
|
2817
|
+
Plane partitions that fit inside a box of a specified size that are
|
|
2818
|
+
symmetric self-complementary.
|
|
2819
|
+
"""
|
|
2820
|
+
def __init__(self, box_size):
|
|
2821
|
+
"""
|
|
2822
|
+
Initialize ``self``.
|
|
2823
|
+
|
|
2824
|
+
TESTS::
|
|
2825
|
+
|
|
2826
|
+
sage: PP = PlanePartitions([2, 2, 4], symmetry='SSCPP')
|
|
2827
|
+
sage: TestSuite(PP).run() # needs sage.graphs sage.modules
|
|
2828
|
+
|
|
2829
|
+
sage: PP = PlanePartitions([4, 4, 2], symmetry='SSCPP')
|
|
2830
|
+
sage: TestSuite(PP).run() # long time # needs sage.graphs sage.modules
|
|
2831
|
+
|
|
2832
|
+
sage: PlanePartitions([4, 2, 2], symmetry='SSCPP')
|
|
2833
|
+
Traceback (most recent call last):
|
|
2834
|
+
...
|
|
2835
|
+
ValueError: x and y dimensions (4 and 2) must be equal
|
|
2836
|
+
|
|
2837
|
+
sage: PlanePartitions([4, 4, 3], symmetry='SSCPP')
|
|
2838
|
+
Traceback (most recent call last):
|
|
2839
|
+
...
|
|
2840
|
+
ValueError: z dimension (3) must be even
|
|
2841
|
+
"""
|
|
2842
|
+
if box_size[0] != box_size[1]:
|
|
2843
|
+
raise ValueError("x and y dimensions ({} and {}) must be equal".format(box_size[0], box_size[1]))
|
|
2844
|
+
if (box_size[2] % 2 == 1):
|
|
2845
|
+
raise ValueError("z dimension ({}) must be even".format(box_size[2]))
|
|
2846
|
+
super().__init__(box_size, "SSCPP", category=FiniteEnumeratedSets())
|
|
2847
|
+
|
|
2848
|
+
def _repr_(self) -> str:
|
|
2849
|
+
"""
|
|
2850
|
+
EXAMPLES::
|
|
2851
|
+
|
|
2852
|
+
sage: PlanePartitions([4, 4, 2], symmetry='SSCPP')
|
|
2853
|
+
Symmetric self-complementary plane partitions inside a 4 x 4 x 2 box
|
|
2854
|
+
"""
|
|
2855
|
+
return "Symmetric self-complementary plane partitions inside a {} x {} x {} box".format(
|
|
2856
|
+
self._box[0], self._box[1], self._box[2])
|
|
2857
|
+
|
|
2858
|
+
def __iter__(self) -> Iterator:
|
|
2859
|
+
"""
|
|
2860
|
+
Iterate over all symmetric self-complementary plane partitions.
|
|
2861
|
+
|
|
2862
|
+
EXAMPLES::
|
|
2863
|
+
|
|
2864
|
+
sage: list(PlanePartitions([4,4,2], symmetry='SSCPP')) # needs sage.graphs sage.modules
|
|
2865
|
+
[Plane partition [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]],
|
|
2866
|
+
Plane partition [[2, 2, 2, 1], [2, 1, 1], [2, 1, 1], [1]],
|
|
2867
|
+
Plane partition [[2, 2, 1, 1], [2, 2, 1, 1], [1, 1], [1, 1]],
|
|
2868
|
+
Plane partition [[2, 2, 2, 1], [2, 2, 1], [2, 1], [1]],
|
|
2869
|
+
Plane partition [[2, 2, 1, 1], [2, 1, 1, 1], [1, 1, 1], [1, 1]],
|
|
2870
|
+
Plane partition [[2, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1]]]
|
|
2871
|
+
|
|
2872
|
+
TESTS::
|
|
2873
|
+
|
|
2874
|
+
sage: all(len(set(PP)) == PP.cardinality() # needs sage.graphs sage.modules
|
|
2875
|
+
....: for a, b in cartesian_product([range(5), range(0, 5, 2)])
|
|
2876
|
+
....: if (PP := PlanePartitions([a, a, b], symmetry='SSCPP')))
|
|
2877
|
+
True
|
|
2878
|
+
"""
|
|
2879
|
+
# any SSCPP is a SPP
|
|
2880
|
+
for p in PlanePartitions(self._box, symmetry='SPP'):
|
|
2881
|
+
if p.is_SSCPP():
|
|
2882
|
+
yield self.element_class(self, p)
|
|
2883
|
+
|
|
2884
|
+
def cardinality(self) -> Integer:
|
|
2885
|
+
r"""
|
|
2886
|
+
Return the cardinality of ``self``.
|
|
2887
|
+
|
|
2888
|
+
The number of symmetric self-complementary plane partitions inside a
|
|
2889
|
+
`2a \times 2a \times 2b` box is equal to
|
|
2890
|
+
|
|
2891
|
+
.. MATH::
|
|
2892
|
+
|
|
2893
|
+
\prod_{i=1}^a \prod_{j=1}^a \frac{i + j + b - 1}{i + j - 1}.
|
|
2894
|
+
|
|
2895
|
+
The number of symmetric self-complementary plane partitions inside a
|
|
2896
|
+
`(2a+1) \times (2a+1) \times 2b` box is equal to
|
|
2897
|
+
|
|
2898
|
+
.. MATH::
|
|
2899
|
+
|
|
2900
|
+
\prod_{i=1}^a \prod_{j=1}^{a+1} \frac{i + j + b - 1}{i + j - 1}.
|
|
2901
|
+
|
|
2902
|
+
EXAMPLES::
|
|
2903
|
+
|
|
2904
|
+
sage: P = PlanePartitions([4,4,2], symmetry='SSCPP')
|
|
2905
|
+
sage: P.cardinality()
|
|
2906
|
+
6
|
|
2907
|
+
sage: Q = PlanePartitions([3,3,2], symmetry='SSCPP')
|
|
2908
|
+
sage: Q.cardinality()
|
|
2909
|
+
3
|
|
2910
|
+
"""
|
|
2911
|
+
a = self._box[0]
|
|
2912
|
+
c = self._box[2]
|
|
2913
|
+
num = prod(i + j + k - 1
|
|
2914
|
+
for i in range(1, 1 + a // 2)
|
|
2915
|
+
for j in range(1, 1 + (a + 1) // 2)
|
|
2916
|
+
for k in range(1, 1 + c // 2))
|
|
2917
|
+
den = prod(i + j + k - 2
|
|
2918
|
+
for i in range(1, 1 + a // 2)
|
|
2919
|
+
for j in range(1, 1 + (a + 1) // 2)
|
|
2920
|
+
for k in range(1, 1 + c // 2))
|
|
2921
|
+
return Integer(num // den)
|
|
2922
|
+
|
|
2923
|
+
|
|
2924
|
+
# Class 8
|
|
2925
|
+
# Cyclically Symmetric Transpose-complement Partitions
|
|
2926
|
+
class PlanePartitions_CSTCPP(PlanePartitions):
|
|
2927
|
+
r"""
|
|
2928
|
+
Plane partitions that fit inside a box of a specified size that are
|
|
2929
|
+
cyclically symmetric and transpose-complement.
|
|
2930
|
+
"""
|
|
2931
|
+
def __init__(self, box_size):
|
|
2932
|
+
"""
|
|
2933
|
+
TESTS::
|
|
2934
|
+
|
|
2935
|
+
sage: PP = PlanePartitions([2,2,2], symmetry='CSTCPP')
|
|
2936
|
+
sage: TestSuite(PP).run() # needs sage.graphs sage.modules
|
|
2937
|
+
|
|
2938
|
+
sage: PlanePartitions([4,3,2], symmetry='CSTCPP')
|
|
2939
|
+
Traceback (most recent call last):
|
|
2940
|
+
...
|
|
2941
|
+
ValueError: x, y, and z dimensions (4,3,2) must all be equal
|
|
2942
|
+
|
|
2943
|
+
sage: PlanePartitions([3,3,3], symmetry='CSTCPP')
|
|
2944
|
+
Traceback (most recent call last):
|
|
2945
|
+
...
|
|
2946
|
+
ValueError: x, y, and z dimensions (3,3,3) must all be even
|
|
2947
|
+
"""
|
|
2948
|
+
if box_size[0] != box_size[1] or box_size[1] != box_size[2]:
|
|
2949
|
+
raise ValueError("x, y, and z dimensions ({},{},{}) must all be equal".format(*box_size))
|
|
2950
|
+
if box_size[0] % 2 == 1:
|
|
2951
|
+
raise ValueError("x, y, and z dimensions ({},{},{}) must all be even".format(*box_size))
|
|
2952
|
+
super().__init__(box_size, "CSTPP", category=FiniteEnumeratedSets())
|
|
2953
|
+
|
|
2954
|
+
def _repr_(self) -> str:
|
|
2955
|
+
"""
|
|
2956
|
+
EXAMPLES::
|
|
2957
|
+
|
|
2958
|
+
sage: PlanePartitions([4,4,4], symmetry='CSTCPP')
|
|
2959
|
+
Cyclically symmetric transpose complement plane partitions inside a 4 x 4 x 4 box
|
|
2960
|
+
"""
|
|
2961
|
+
return "Cyclically symmetric transpose complement plane partitions inside a {} x {} x {} box".format(
|
|
2962
|
+
self._box[0], self._box[1], self._box[2])
|
|
2963
|
+
|
|
2964
|
+
def __iter__(self) -> Iterator:
|
|
2965
|
+
"""
|
|
2966
|
+
Iterate over all cyclically symmetry transpose complement plane partitions.
|
|
2967
|
+
|
|
2968
|
+
EXAMPLES::
|
|
2969
|
+
|
|
2970
|
+
sage: list(PlanePartitions([2,2,2], symmetry='CSTCPP')) # needs sage.graphs sage.modules
|
|
2971
|
+
[Plane partition [[2, 1], [1]]]
|
|
2972
|
+
|
|
2973
|
+
TESTS::
|
|
2974
|
+
|
|
2975
|
+
sage: all(len(set(PP)) == PP.cardinality() # needs sage.graphs sage.modules
|
|
2976
|
+
....: for n in range(0, 5, 2)
|
|
2977
|
+
....: if (PP := PlanePartitions([n]*3, symmetry='CSTCPP')))
|
|
2978
|
+
True
|
|
2979
|
+
"""
|
|
2980
|
+
# any CSTCPP is a TSPP, a SSCPP and a CSSCPP
|
|
2981
|
+
for p in PlanePartitions(self._box, symmetry='TSPP'):
|
|
2982
|
+
if p.is_CSTCPP():
|
|
2983
|
+
yield self.element_class(self, p)
|
|
2984
|
+
|
|
2985
|
+
def cardinality(self) -> Integer:
|
|
2986
|
+
r"""
|
|
2987
|
+
Return the cardinality of ``self``.
|
|
2988
|
+
|
|
2989
|
+
The number of cyclically symmetric transpose complement plane partitions
|
|
2990
|
+
inside a `2a \times 2a \times 2a` box is equal to
|
|
2991
|
+
|
|
2992
|
+
.. MATH::
|
|
2993
|
+
|
|
2994
|
+
\prod_{i=0}^{a-1} \frac{(3i+1)(6i)!(2i)!}{(4i+1)!(4i)!}.
|
|
2995
|
+
|
|
2996
|
+
EXAMPLES::
|
|
2997
|
+
|
|
2998
|
+
sage: P = PlanePartitions([6,6,6], symmetry='CSTCPP')
|
|
2999
|
+
sage: P.cardinality()
|
|
3000
|
+
11
|
|
3001
|
+
"""
|
|
3002
|
+
a = self._box[0] // 2
|
|
3003
|
+
num = prod((3*i + 1) * factorial(6*i) * factorial(2*i) for i in range(a))
|
|
3004
|
+
den = prod((factorial(4*i + 1) * factorial(4*i)) for i in range(a))
|
|
3005
|
+
return Integer(num // den)
|
|
3006
|
+
|
|
3007
|
+
|
|
3008
|
+
# Class 9
|
|
3009
|
+
# Cyclically Symmetric Self-complementary Plane Partitions
|
|
3010
|
+
class PlanePartitions_CSSCPP(PlanePartitions):
|
|
3011
|
+
r"""
|
|
3012
|
+
Plane partitions that fit inside a box of a specified size that are
|
|
3013
|
+
cyclically symmetric self-complementary.
|
|
3014
|
+
"""
|
|
3015
|
+
def __init__(self, box_size):
|
|
3016
|
+
r"""
|
|
3017
|
+
Initialize ``self``.
|
|
3018
|
+
|
|
3019
|
+
TESTS::
|
|
3020
|
+
|
|
3021
|
+
sage: PP = PlanePartitions([2,2,2], symmetry='CSSCPP')
|
|
3022
|
+
sage: TestSuite(PP).run() # needs sage.graphs sage.modules
|
|
3023
|
+
sage: PlanePartitions([4,3,2], symmetry='CSSCPP')
|
|
3024
|
+
Traceback (most recent call last):
|
|
3025
|
+
...
|
|
3026
|
+
ValueError: x, y, and z dimensions (4,3,2) must all be equal
|
|
3027
|
+
sage: PlanePartitions([3,3,3], symmetry='CSSCPP')
|
|
3028
|
+
Traceback (most recent call last):
|
|
3029
|
+
...
|
|
3030
|
+
ValueError: x, y, and z dimensions (3,3,3) must all be even
|
|
3031
|
+
"""
|
|
3032
|
+
if box_size[0] != box_size[1] or box_size[1] != box_size[2]:
|
|
3033
|
+
raise ValueError("x, y, and z dimensions ({},{},{}) must all be equal".format(*box_size))
|
|
3034
|
+
if box_size[0] % 2 == 1:
|
|
3035
|
+
raise ValueError("x, y, and z dimensions ({},{},{}) must all be even".format(*box_size))
|
|
3036
|
+
super().__init__(box_size, "CSSCPP", category=FiniteEnumeratedSets())
|
|
3037
|
+
|
|
3038
|
+
def _repr_(self) -> str:
|
|
3039
|
+
"""
|
|
3040
|
+
EXAMPLES::
|
|
3041
|
+
|
|
3042
|
+
sage: PlanePartitions([4,4,4], symmetry='CSSCPP')
|
|
3043
|
+
Cyclically symmetric self-complementary plane partitions inside a 4 x 4 x 4 box
|
|
3044
|
+
"""
|
|
3045
|
+
return "Cyclically symmetric self-complementary plane partitions inside a {} x {} x {} box".format(
|
|
3046
|
+
self._box[0], self._box[1], self._box[2])
|
|
3047
|
+
|
|
3048
|
+
def __iter__(self) -> Iterator:
|
|
3049
|
+
"""
|
|
3050
|
+
Iterate over all cyclically symmetric self-complementary plane partitions.
|
|
3051
|
+
|
|
3052
|
+
EXAMPLES::
|
|
3053
|
+
|
|
3054
|
+
sage: list(PlanePartitions([2,2,2], symmetry='CSSCPP')) # needs sage.graphs sage.modules
|
|
3055
|
+
[Plane partition [[2, 1], [1]]]
|
|
3056
|
+
"""
|
|
3057
|
+
# any CSSCPP is a SCPP and an CSPP, there are much fewer CSPP
|
|
3058
|
+
for p in PlanePartitions(self._box, symmetry='CSPP'):
|
|
3059
|
+
if p.is_CSSCPP():
|
|
3060
|
+
yield self.element_class(self, p)
|
|
3061
|
+
|
|
3062
|
+
def cardinality(self) -> Integer:
|
|
3063
|
+
r"""
|
|
3064
|
+
Return the cardinality of ``self``.
|
|
3065
|
+
|
|
3066
|
+
The number of cyclically symmetric self-complementary plane partitions
|
|
3067
|
+
inside a `2a \times 2a \times 2a` box is equal to
|
|
3068
|
+
|
|
3069
|
+
.. MATH::
|
|
3070
|
+
|
|
3071
|
+
\left( \prod_{i=0}^{a-1} \frac{(3i+1)!}{(a+i)!} \right)^2.
|
|
3072
|
+
|
|
3073
|
+
EXAMPLES::
|
|
3074
|
+
|
|
3075
|
+
sage: P = PlanePartitions([6,6,6], symmetry='CSSCPP')
|
|
3076
|
+
sage: P.cardinality()
|
|
3077
|
+
49
|
|
3078
|
+
"""
|
|
3079
|
+
a = self._box[0] // 2
|
|
3080
|
+
num = prod(factorial(3*i + 1)**2 for i in range(a))
|
|
3081
|
+
den = prod(factorial(a + i)**2 for i in range(a))
|
|
3082
|
+
return Integer(num // den)
|
|
3083
|
+
|
|
3084
|
+
|
|
3085
|
+
# Class 10
|
|
3086
|
+
# Totally Symmetric Self-complementary Plane Partitions
|
|
3087
|
+
class PlanePartitions_TSSCPP(PlanePartitions):
|
|
3088
|
+
r"""
|
|
3089
|
+
Plane partitions that fit inside a box of a specified size that are
|
|
3090
|
+
totally symmetric self-complementary.
|
|
3091
|
+
"""
|
|
3092
|
+
def __init__(self, box_size):
|
|
3093
|
+
"""
|
|
3094
|
+
TESTS::
|
|
3095
|
+
|
|
3096
|
+
sage: PP = PlanePartitions([4,4,4], symmetry='TSSCPP')
|
|
3097
|
+
sage: TestSuite(PP).run() # needs sage.graphs sage.modules
|
|
3098
|
+
sage: PlanePartitions([4,3,2], symmetry='TSSCPP')
|
|
3099
|
+
Traceback (most recent call last):
|
|
3100
|
+
...
|
|
3101
|
+
ValueError: x, y, and z dimensions (4,3,2) must all be equal
|
|
3102
|
+
sage: PlanePartitions([3,3,3], symmetry='TSSCPP')
|
|
3103
|
+
Traceback (most recent call last):
|
|
3104
|
+
...
|
|
3105
|
+
ValueError: x, y, and z dimensions (3,3,3) must all be even
|
|
3106
|
+
"""
|
|
3107
|
+
if box_size[0] != box_size[1] or box_size[1] != box_size[2]:
|
|
3108
|
+
raise ValueError("x, y, and z dimensions ({},{},{}) must all be equal".format(*box_size))
|
|
3109
|
+
if box_size[0] % 2 == 1:
|
|
3110
|
+
raise ValueError("x, y, and z dimensions ({},{},{}) must all be even".format(*box_size))
|
|
3111
|
+
super().__init__(box_size, "TSSCPP", category=FiniteEnumeratedSets())
|
|
3112
|
+
|
|
3113
|
+
def _repr_(self) -> str:
|
|
3114
|
+
r"""
|
|
3115
|
+
EXAMPLES::
|
|
3116
|
+
|
|
3117
|
+
sage: PlanePartitions([4,4,4], symmetry='TSSCPP')
|
|
3118
|
+
Totally symmetric self-complementary plane partitions inside a 4 x 4 x 4 box
|
|
3119
|
+
"""
|
|
3120
|
+
return "Totally symmetric self-complementary plane partitions inside a {} x {} x {} box".format(
|
|
3121
|
+
self._box[0], self._box[1], self._box[2])
|
|
3122
|
+
|
|
3123
|
+
def to_poset(self):
|
|
3124
|
+
r"""
|
|
3125
|
+
Return a poset whose order ideals are in bijection with
|
|
3126
|
+
totally symmetric self-complementary plane partitions.
|
|
3127
|
+
|
|
3128
|
+
EXAMPLES::
|
|
3129
|
+
|
|
3130
|
+
sage: PP = PlanePartitions([6,6,6], symmetry='TSSCPP')
|
|
3131
|
+
sage: PP.to_poset() # needs sage.graphs sage.modules
|
|
3132
|
+
Finite poset containing 4 elements
|
|
3133
|
+
sage: PP.to_poset().order_ideals_lattice().cardinality() == PP.cardinality() # needs sage.graphs sage.modules
|
|
3134
|
+
True
|
|
3135
|
+
"""
|
|
3136
|
+
from sage.combinat.posets.posets import Poset
|
|
3137
|
+
a = self._box[0]
|
|
3138
|
+
b = self._box[1]
|
|
3139
|
+
c = self._box[2]
|
|
3140
|
+
if a != b or b != c or a != c:
|
|
3141
|
+
return Poset()
|
|
3142
|
+
|
|
3143
|
+
def comp(x, y):
|
|
3144
|
+
return all(xx <= yy for xx, yy in zip(x, y))
|
|
3145
|
+
|
|
3146
|
+
A = a // 2
|
|
3147
|
+
pl = [(x, y, z) for x in range(A-1) for y in range(x, A-1)
|
|
3148
|
+
for z in range(A-1) if z <= A - 2 - y]
|
|
3149
|
+
return Poset((pl, comp))
|
|
3150
|
+
|
|
3151
|
+
def from_antichain(self, acl) -> PP:
|
|
3152
|
+
r"""
|
|
3153
|
+
Return the totally symmetric self-complementary plane partition
|
|
3154
|
+
corresponding to an antichain in the poset given in :meth:`to_poset()`.
|
|
3155
|
+
|
|
3156
|
+
EXAMPLES::
|
|
3157
|
+
|
|
3158
|
+
sage: PP = PlanePartitions([6,6,6], symmetry='TSSCPP')
|
|
3159
|
+
sage: A = [(0, 0, 1), (1, 1, 0)]
|
|
3160
|
+
sage: PP.from_antichain(A)
|
|
3161
|
+
Plane partition [[6, 6, 6, 5, 5, 3], [6, 5, 5, 4, 3, 1], [6, 5, 4, 3, 2, 1],
|
|
3162
|
+
[5, 4, 3, 2, 1], [5, 3, 2, 1, 1], [3, 1, 1]]
|
|
3163
|
+
"""
|
|
3164
|
+
# ac format ex: [x,y,z]
|
|
3165
|
+
a = self._box[0]
|
|
3166
|
+
b = self._box[1]
|
|
3167
|
+
c = self._box[2]
|
|
3168
|
+
n = a
|
|
3169
|
+
N = n // 2
|
|
3170
|
+
pp_matrix = [[0] * (c) for i in range(b)]
|
|
3171
|
+
# creates a matrix for the plane partition populated by 0s
|
|
3172
|
+
# EX: [[0,0,0], [0,0,0], [0,0,0]]
|
|
3173
|
+
width = N - 1
|
|
3174
|
+
height = N - 1
|
|
3175
|
+
|
|
3176
|
+
# generate inner triangle
|
|
3177
|
+
# FIXME: Make this iterator more efficient
|
|
3178
|
+
for i in range(width):
|
|
3179
|
+
for j in range(i, height):
|
|
3180
|
+
for ac in acl:
|
|
3181
|
+
if ac[0] == i and ac[1] == j:
|
|
3182
|
+
zVal = ac[2]
|
|
3183
|
+
matrixVal = pp_matrix[j+N][i+N]
|
|
3184
|
+
if zVal + 1 > matrixVal:
|
|
3185
|
+
pp_matrix[j+N][i+N] = zVal + 1
|
|
3186
|
+
|
|
3187
|
+
# fill back
|
|
3188
|
+
for i in range(width):
|
|
3189
|
+
i = width - (i + 1)
|
|
3190
|
+
i = i + N
|
|
3191
|
+
for j in range(height):
|
|
3192
|
+
j = height - (j + 1)
|
|
3193
|
+
j = j + N
|
|
3194
|
+
if pp_matrix[i][j] == 0:
|
|
3195
|
+
if i >= j:
|
|
3196
|
+
iValue = 0
|
|
3197
|
+
jValue = 0
|
|
3198
|
+
if i < n:
|
|
3199
|
+
iValue = pp_matrix[i+1][j]
|
|
3200
|
+
if j < n:
|
|
3201
|
+
jValue = pp_matrix[i][j+1]
|
|
3202
|
+
pp_matrix[i][j] = max(iValue, jValue)
|
|
3203
|
+
|
|
3204
|
+
# fill half of triangle symmetrically
|
|
3205
|
+
for i in range(width):
|
|
3206
|
+
i += N
|
|
3207
|
+
for j in range(height):
|
|
3208
|
+
j += N
|
|
3209
|
+
if i >= j:
|
|
3210
|
+
pp_matrix[j][i] = pp_matrix[i][j]
|
|
3211
|
+
|
|
3212
|
+
# upper left box
|
|
3213
|
+
for i in range(N):
|
|
3214
|
+
for j in range(N):
|
|
3215
|
+
pp_matrix[i][j] = n - pp_matrix[n-(i+1)][n-(j+1)]
|
|
3216
|
+
|
|
3217
|
+
# fill in lower left cube with values n/2
|
|
3218
|
+
for i in range(N):
|
|
3219
|
+
for j in range(N):
|
|
3220
|
+
x = i
|
|
3221
|
+
y = j
|
|
3222
|
+
if pp_matrix[x][y+N] == 0:
|
|
3223
|
+
pp_matrix[x][y+N] = N
|
|
3224
|
+
if pp_matrix[x+N][y] == 0:
|
|
3225
|
+
pp_matrix[x+N][y] = N
|
|
3226
|
+
|
|
3227
|
+
# add and subtract values from lower left cube to be rotation of lower right cube
|
|
3228
|
+
for i in range(N):
|
|
3229
|
+
for j in range(N):
|
|
3230
|
+
x = i + N
|
|
3231
|
+
y = j + N
|
|
3232
|
+
if pp_matrix[x][y] > 0:
|
|
3233
|
+
z = pp_matrix[x][y]
|
|
3234
|
+
for cVal in range(z):
|
|
3235
|
+
# build onto lower left cube
|
|
3236
|
+
pp_matrix[x][0+cVal] += 1
|
|
3237
|
+
# carve out of lower left cube
|
|
3238
|
+
pp_matrix[n-(1+cVal)][N-(j+1)] -= 1
|
|
3239
|
+
|
|
3240
|
+
# fill in upper right cube symmetrically with lower left
|
|
3241
|
+
for i in range(N):
|
|
3242
|
+
for j in range(N):
|
|
3243
|
+
pp_matrix[j][i+N] = pp_matrix[i+N][j]
|
|
3244
|
+
return self.element_class(self, pp_matrix)
|
|
3245
|
+
|
|
3246
|
+
def from_order_ideal(self, I) -> PP:
|
|
3247
|
+
r"""
|
|
3248
|
+
Return the totally symmetric self-complementary plane partition
|
|
3249
|
+
corresponding to an order ideal in the poset given in :meth:`to_poset`.
|
|
3250
|
+
|
|
3251
|
+
EXAMPLES::
|
|
3252
|
+
|
|
3253
|
+
sage: PP = PlanePartitions([6,6,6], symmetry='TSSCPP') # needs sage.graphs
|
|
3254
|
+
sage: I = [(0, 0, 0), (0, 1, 0), (1, 1, 0)]
|
|
3255
|
+
sage: PP.from_order_ideal(I) # needs sage.graphs
|
|
3256
|
+
Plane partition [[6, 6, 6, 5, 5, 3], [6, 5, 5, 3, 3, 1], [6, 5, 5, 3, 3, 1],
|
|
3257
|
+
[5, 3, 3, 1, 1], [5, 3, 3, 1, 1], [3, 1, 1]]
|
|
3258
|
+
"""
|
|
3259
|
+
return self.from_antichain(self.to_poset().order_ideal_generators(I))
|
|
3260
|
+
|
|
3261
|
+
def __iter__(self) -> Iterator:
|
|
3262
|
+
"""
|
|
3263
|
+
Iterate over all totally symmetric self-complementary plane partitions.
|
|
3264
|
+
|
|
3265
|
+
EXAMPLES::
|
|
3266
|
+
|
|
3267
|
+
sage: list(PlanePartitions([4,4,4], symmetry='TSSCPP')) # needs sage.graphs sage.modules
|
|
3268
|
+
[Plane partition [[4, 4, 2, 2], [4, 4, 2, 2], [2, 2], [2, 2]],
|
|
3269
|
+
Plane partition [[4, 4, 3, 2], [4, 3, 2, 1], [3, 2, 1], [2, 1]]]
|
|
3270
|
+
|
|
3271
|
+
TESTS::
|
|
3272
|
+
|
|
3273
|
+
sage: all(len(set(PP)) == PP.cardinality() for n in range(0,11,2) # needs sage.graphs sage.modules
|
|
3274
|
+
....: if (PP := PlanePartitions([n]*3, symmetry='TSSCPP')))
|
|
3275
|
+
True
|
|
3276
|
+
"""
|
|
3277
|
+
for acl in self.to_poset().antichains_iterator():
|
|
3278
|
+
yield self.from_antichain(acl)
|
|
3279
|
+
|
|
3280
|
+
def cardinality(self) -> Integer:
|
|
3281
|
+
r"""
|
|
3282
|
+
Return the cardinality of ``self``.
|
|
3283
|
+
|
|
3284
|
+
The number of totally symmetric self-complementary plane partitions
|
|
3285
|
+
inside a `2a \times 2a \times 2a` box is equal to
|
|
3286
|
+
|
|
3287
|
+
.. MATH::
|
|
3288
|
+
|
|
3289
|
+
\prod_{i=0}^{a-1} \frac{(3i+1)!}{(a+i)!}.
|
|
3290
|
+
|
|
3291
|
+
EXAMPLES::
|
|
3292
|
+
|
|
3293
|
+
sage: P = PlanePartitions([6,6,6], symmetry='TSSCPP')
|
|
3294
|
+
sage: P.cardinality()
|
|
3295
|
+
7
|
|
3296
|
+
"""
|
|
3297
|
+
a = self._box[0] // 2
|
|
3298
|
+
num = prod(factorial(3*i + 1) for i in range(a))
|
|
3299
|
+
den = prod(factorial(a + i) for i in range(a))
|
|
3300
|
+
return Integer(num // den)
|