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.
Files changed (401) hide show
  1. passagemath_combinat/__init__.py +3 -0
  2. passagemath_combinat-10.6.42.dist-info/DELVEWHEEL +2 -0
  3. passagemath_combinat-10.6.42.dist-info/METADATA +160 -0
  4. passagemath_combinat-10.6.42.dist-info/RECORD +401 -0
  5. passagemath_combinat-10.6.42.dist-info/WHEEL +5 -0
  6. passagemath_combinat-10.6.42.dist-info/top_level.txt +3 -0
  7. passagemath_combinat.libs/libgmp-10-3a5f019e2510aeaad918cab2b57a689d.dll +0 -0
  8. passagemath_combinat.libs/libsymmetrica-3-7dcf900932804d0df5fd0919b4668720.dll +0 -0
  9. sage/algebras/affine_nil_temperley_lieb.py +263 -0
  10. sage/algebras/all.py +24 -0
  11. sage/algebras/all__sagemath_combinat.py +35 -0
  12. sage/algebras/askey_wilson.py +935 -0
  13. sage/algebras/associated_graded.py +345 -0
  14. sage/algebras/cellular_basis.py +350 -0
  15. sage/algebras/cluster_algebra.py +2766 -0
  16. sage/algebras/down_up_algebra.py +860 -0
  17. sage/algebras/free_algebra.py +1698 -0
  18. sage/algebras/free_algebra_element.py +345 -0
  19. sage/algebras/free_algebra_quotient.py +405 -0
  20. sage/algebras/free_algebra_quotient_element.py +295 -0
  21. sage/algebras/free_zinbiel_algebra.py +885 -0
  22. sage/algebras/hall_algebra.py +783 -0
  23. sage/algebras/hecke_algebras/all.py +4 -0
  24. sage/algebras/hecke_algebras/ariki_koike_algebra.py +1796 -0
  25. sage/algebras/hecke_algebras/ariki_koike_specht_modules.py +475 -0
  26. sage/algebras/hecke_algebras/cubic_hecke_algebra.py +3520 -0
  27. sage/algebras/hecke_algebras/cubic_hecke_base_ring.py +1473 -0
  28. sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py +1079 -0
  29. sage/algebras/iwahori_hecke_algebra.py +3095 -0
  30. sage/algebras/jordan_algebra.py +1773 -0
  31. sage/algebras/lie_conformal_algebras/abelian_lie_conformal_algebra.py +113 -0
  32. sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py +156 -0
  33. sage/algebras/lie_conformal_algebras/all.py +18 -0
  34. sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra.py +134 -0
  35. sage/algebras/lie_conformal_algebras/examples.py +43 -0
  36. sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra.py +131 -0
  37. sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py +139 -0
  38. sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py +174 -0
  39. sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py +167 -0
  40. sage/algebras/lie_conformal_algebras/freely_generated_lie_conformal_algebra.py +107 -0
  41. sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py +135 -0
  42. sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py +353 -0
  43. sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +236 -0
  44. sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py +78 -0
  45. sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py +328 -0
  46. sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra.py +117 -0
  47. sage/algebras/lie_conformal_algebras/neveu_schwarz_lie_conformal_algebra.py +86 -0
  48. sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra.py +82 -0
  49. sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra.py +205 -0
  50. sage/algebras/nil_coxeter_algebra.py +191 -0
  51. sage/algebras/q_commuting_polynomials.py +673 -0
  52. sage/algebras/q_system.py +608 -0
  53. sage/algebras/quantum_clifford.py +959 -0
  54. sage/algebras/quantum_groups/ace_quantum_onsager.py +693 -0
  55. sage/algebras/quantum_groups/all.py +9 -0
  56. sage/algebras/quantum_groups/fock_space.py +2219 -0
  57. sage/algebras/quantum_groups/q_numbers.py +207 -0
  58. sage/algebras/quantum_groups/quantum_group_gap.py +2695 -0
  59. sage/algebras/quantum_groups/representations.py +591 -0
  60. sage/algebras/quantum_matrix_coordinate_algebra.py +1006 -0
  61. sage/algebras/quantum_oscillator.py +623 -0
  62. sage/algebras/quaternion_algebra.py +20 -0
  63. sage/algebras/quaternion_algebra_element.py +55 -0
  64. sage/algebras/rational_cherednik_algebra.py +525 -0
  65. sage/algebras/schur_algebra.py +670 -0
  66. sage/algebras/shuffle_algebra.py +1011 -0
  67. sage/algebras/splitting_algebra.py +779 -0
  68. sage/algebras/tensor_algebra.py +709 -0
  69. sage/algebras/yangian.py +1082 -0
  70. sage/algebras/yokonuma_hecke_algebra.py +1018 -0
  71. sage/all__sagemath_combinat.py +44 -0
  72. sage/combinat/SJT.py +255 -0
  73. sage/combinat/affine_permutation.py +2405 -0
  74. sage/combinat/algebraic_combinatorics.py +55 -0
  75. sage/combinat/all.py +53 -0
  76. sage/combinat/all__sagemath_combinat.py +195 -0
  77. sage/combinat/alternating_sign_matrix.py +2063 -0
  78. sage/combinat/baxter_permutations.py +346 -0
  79. sage/combinat/bijectionist.py +3220 -0
  80. sage/combinat/binary_recurrence_sequences.py +1180 -0
  81. sage/combinat/blob_algebra.py +685 -0
  82. sage/combinat/catalog_partitions.py +27 -0
  83. sage/combinat/chas/all.py +23 -0
  84. sage/combinat/chas/fsym.py +1180 -0
  85. sage/combinat/chas/wqsym.py +2601 -0
  86. sage/combinat/cluster_complex.py +326 -0
  87. sage/combinat/colored_permutations.py +2039 -0
  88. sage/combinat/colored_permutations_representations.py +964 -0
  89. sage/combinat/composition_signed.py +142 -0
  90. sage/combinat/composition_tableau.py +855 -0
  91. sage/combinat/constellation.py +1729 -0
  92. sage/combinat/core.py +751 -0
  93. sage/combinat/counting.py +12 -0
  94. sage/combinat/crystals/affine.py +742 -0
  95. sage/combinat/crystals/affine_factorization.py +518 -0
  96. sage/combinat/crystals/affinization.py +331 -0
  97. sage/combinat/crystals/alcove_path.py +2013 -0
  98. sage/combinat/crystals/all.py +22 -0
  99. sage/combinat/crystals/bkk_crystals.py +141 -0
  100. sage/combinat/crystals/catalog.py +115 -0
  101. sage/combinat/crystals/catalog_elementary_crystals.py +18 -0
  102. sage/combinat/crystals/catalog_infinity_crystals.py +33 -0
  103. sage/combinat/crystals/catalog_kirillov_reshetikhin.py +18 -0
  104. sage/combinat/crystals/crystals.py +257 -0
  105. sage/combinat/crystals/direct_sum.py +260 -0
  106. sage/combinat/crystals/elementary_crystals.py +1251 -0
  107. sage/combinat/crystals/fast_crystals.py +441 -0
  108. sage/combinat/crystals/fully_commutative_stable_grothendieck.py +1205 -0
  109. sage/combinat/crystals/generalized_young_walls.py +1076 -0
  110. sage/combinat/crystals/highest_weight_crystals.py +436 -0
  111. sage/combinat/crystals/induced_structure.py +695 -0
  112. sage/combinat/crystals/infinity_crystals.py +730 -0
  113. sage/combinat/crystals/kac_modules.py +863 -0
  114. sage/combinat/crystals/kirillov_reshetikhin.py +4196 -0
  115. sage/combinat/crystals/kyoto_path_model.py +497 -0
  116. sage/combinat/crystals/letters.cp314t-win_amd64.pyd +0 -0
  117. sage/combinat/crystals/letters.pxd +79 -0
  118. sage/combinat/crystals/letters.pyx +3056 -0
  119. sage/combinat/crystals/littelmann_path.py +1518 -0
  120. sage/combinat/crystals/monomial_crystals.py +1262 -0
  121. sage/combinat/crystals/multisegments.py +462 -0
  122. sage/combinat/crystals/mv_polytopes.py +467 -0
  123. sage/combinat/crystals/pbw_crystal.py +511 -0
  124. sage/combinat/crystals/pbw_datum.cp314t-win_amd64.pyd +0 -0
  125. sage/combinat/crystals/pbw_datum.pxd +4 -0
  126. sage/combinat/crystals/pbw_datum.pyx +487 -0
  127. sage/combinat/crystals/polyhedral_realization.py +372 -0
  128. sage/combinat/crystals/spins.cp314t-win_amd64.pyd +0 -0
  129. sage/combinat/crystals/spins.pxd +21 -0
  130. sage/combinat/crystals/spins.pyx +756 -0
  131. sage/combinat/crystals/star_crystal.py +290 -0
  132. sage/combinat/crystals/subcrystal.py +464 -0
  133. sage/combinat/crystals/tensor_product.py +1177 -0
  134. sage/combinat/crystals/tensor_product_element.cp314t-win_amd64.pyd +0 -0
  135. sage/combinat/crystals/tensor_product_element.pxd +35 -0
  136. sage/combinat/crystals/tensor_product_element.pyx +1870 -0
  137. sage/combinat/crystals/virtual_crystal.py +420 -0
  138. sage/combinat/cyclic_sieving_phenomenon.py +204 -0
  139. sage/combinat/debruijn_sequence.cp314t-win_amd64.pyd +0 -0
  140. sage/combinat/debruijn_sequence.pyx +355 -0
  141. sage/combinat/decorated_permutation.py +270 -0
  142. sage/combinat/degree_sequences.cp314t-win_amd64.pyd +0 -0
  143. sage/combinat/degree_sequences.pyx +588 -0
  144. sage/combinat/derangements.py +527 -0
  145. sage/combinat/descent_algebra.py +1008 -0
  146. sage/combinat/diagram.py +1551 -0
  147. sage/combinat/diagram_algebras.py +5886 -0
  148. sage/combinat/dyck_word.py +4349 -0
  149. sage/combinat/e_one_star.py +1623 -0
  150. sage/combinat/enumerated_sets.py +123 -0
  151. sage/combinat/expnums.cp314t-win_amd64.pyd +0 -0
  152. sage/combinat/expnums.pyx +148 -0
  153. sage/combinat/fast_vector_partitions.cp314t-win_amd64.pyd +0 -0
  154. sage/combinat/fast_vector_partitions.pyx +346 -0
  155. sage/combinat/fqsym.py +1977 -0
  156. sage/combinat/free_dendriform_algebra.py +954 -0
  157. sage/combinat/free_prelie_algebra.py +1141 -0
  158. sage/combinat/fully_commutative_elements.py +1077 -0
  159. sage/combinat/fully_packed_loop.py +1523 -0
  160. sage/combinat/gelfand_tsetlin_patterns.py +1409 -0
  161. sage/combinat/gray_codes.py +311 -0
  162. sage/combinat/grossman_larson_algebras.py +667 -0
  163. sage/combinat/growth.py +4352 -0
  164. sage/combinat/hall_polynomial.py +188 -0
  165. sage/combinat/hillman_grassl.py +866 -0
  166. sage/combinat/integer_matrices.py +329 -0
  167. sage/combinat/integer_vectors_mod_permgroup.py +1238 -0
  168. sage/combinat/k_tableau.py +4564 -0
  169. sage/combinat/kazhdan_lusztig.py +215 -0
  170. sage/combinat/key_polynomial.py +885 -0
  171. sage/combinat/knutson_tao_puzzles.py +2286 -0
  172. sage/combinat/lr_tableau.py +311 -0
  173. sage/combinat/matrices/all.py +24 -0
  174. sage/combinat/matrices/hadamard_matrix.py +3790 -0
  175. sage/combinat/matrices/latin.py +2912 -0
  176. sage/combinat/misc.py +401 -0
  177. sage/combinat/multiset_partition_into_sets_ordered.py +3541 -0
  178. sage/combinat/ncsf_qsym/all.py +21 -0
  179. sage/combinat/ncsf_qsym/combinatorics.py +317 -0
  180. sage/combinat/ncsf_qsym/generic_basis_code.py +1427 -0
  181. sage/combinat/ncsf_qsym/ncsf.py +5637 -0
  182. sage/combinat/ncsf_qsym/qsym.py +4053 -0
  183. sage/combinat/ncsf_qsym/tutorial.py +447 -0
  184. sage/combinat/ncsym/all.py +21 -0
  185. sage/combinat/ncsym/bases.py +855 -0
  186. sage/combinat/ncsym/dual.py +593 -0
  187. sage/combinat/ncsym/ncsym.py +2076 -0
  188. sage/combinat/necklace.py +551 -0
  189. sage/combinat/non_decreasing_parking_function.py +634 -0
  190. sage/combinat/nu_dyck_word.py +1474 -0
  191. sage/combinat/output.py +861 -0
  192. sage/combinat/parallelogram_polyomino.py +4326 -0
  193. sage/combinat/parking_functions.py +1602 -0
  194. sage/combinat/partition_algebra.py +1998 -0
  195. sage/combinat/partition_kleshchev.py +1982 -0
  196. sage/combinat/partition_shifting_algebras.py +584 -0
  197. sage/combinat/partition_tuple.py +3114 -0
  198. sage/combinat/path_tableaux/all.py +13 -0
  199. sage/combinat/path_tableaux/catalog.py +29 -0
  200. sage/combinat/path_tableaux/dyck_path.py +380 -0
  201. sage/combinat/path_tableaux/frieze.py +476 -0
  202. sage/combinat/path_tableaux/path_tableau.py +728 -0
  203. sage/combinat/path_tableaux/semistandard.py +510 -0
  204. sage/combinat/perfect_matching.py +779 -0
  205. sage/combinat/plane_partition.py +3300 -0
  206. sage/combinat/q_bernoulli.cp314t-win_amd64.pyd +0 -0
  207. sage/combinat/q_bernoulli.pyx +128 -0
  208. sage/combinat/quickref.py +81 -0
  209. sage/combinat/recognizable_series.py +2051 -0
  210. sage/combinat/regular_sequence.py +4316 -0
  211. sage/combinat/regular_sequence_bounded.py +543 -0
  212. sage/combinat/restricted_growth.py +81 -0
  213. sage/combinat/ribbon.py +20 -0
  214. sage/combinat/ribbon_shaped_tableau.py +489 -0
  215. sage/combinat/ribbon_tableau.py +1180 -0
  216. sage/combinat/rigged_configurations/all.py +46 -0
  217. sage/combinat/rigged_configurations/bij_abstract_class.py +548 -0
  218. sage/combinat/rigged_configurations/bij_infinity.py +370 -0
  219. sage/combinat/rigged_configurations/bij_type_A.py +163 -0
  220. sage/combinat/rigged_configurations/bij_type_A2_dual.py +338 -0
  221. sage/combinat/rigged_configurations/bij_type_A2_even.py +218 -0
  222. sage/combinat/rigged_configurations/bij_type_A2_odd.py +199 -0
  223. sage/combinat/rigged_configurations/bij_type_B.py +900 -0
  224. sage/combinat/rigged_configurations/bij_type_C.py +267 -0
  225. sage/combinat/rigged_configurations/bij_type_D.py +771 -0
  226. sage/combinat/rigged_configurations/bij_type_D_tri.py +392 -0
  227. sage/combinat/rigged_configurations/bij_type_D_twisted.py +576 -0
  228. sage/combinat/rigged_configurations/bij_type_E67.py +402 -0
  229. sage/combinat/rigged_configurations/bijection.py +143 -0
  230. sage/combinat/rigged_configurations/kleber_tree.py +1475 -0
  231. sage/combinat/rigged_configurations/kr_tableaux.py +1898 -0
  232. sage/combinat/rigged_configurations/rc_crystal.py +461 -0
  233. sage/combinat/rigged_configurations/rc_infinity.py +540 -0
  234. sage/combinat/rigged_configurations/rigged_configuration_element.py +2403 -0
  235. sage/combinat/rigged_configurations/rigged_configurations.py +1918 -0
  236. sage/combinat/rigged_configurations/rigged_partition.cp314t-win_amd64.pyd +0 -0
  237. sage/combinat/rigged_configurations/rigged_partition.pxd +15 -0
  238. sage/combinat/rigged_configurations/rigged_partition.pyx +680 -0
  239. sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +499 -0
  240. sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py +428 -0
  241. sage/combinat/rsk.py +3438 -0
  242. sage/combinat/schubert_polynomial.py +508 -0
  243. sage/combinat/set_partition.py +3318 -0
  244. sage/combinat/set_partition_iterator.cp314t-win_amd64.pyd +0 -0
  245. sage/combinat/set_partition_iterator.pyx +136 -0
  246. sage/combinat/set_partition_ordered.py +1590 -0
  247. sage/combinat/sf/abreu_nigro.py +346 -0
  248. sage/combinat/sf/all.py +52 -0
  249. sage/combinat/sf/character.py +576 -0
  250. sage/combinat/sf/classical.py +319 -0
  251. sage/combinat/sf/dual.py +996 -0
  252. sage/combinat/sf/elementary.py +549 -0
  253. sage/combinat/sf/hall_littlewood.py +1028 -0
  254. sage/combinat/sf/hecke.py +336 -0
  255. sage/combinat/sf/homogeneous.py +464 -0
  256. sage/combinat/sf/jack.py +1428 -0
  257. sage/combinat/sf/k_dual.py +1458 -0
  258. sage/combinat/sf/kfpoly.py +447 -0
  259. sage/combinat/sf/llt.py +789 -0
  260. sage/combinat/sf/macdonald.py +2019 -0
  261. sage/combinat/sf/monomial.py +525 -0
  262. sage/combinat/sf/multiplicative.py +113 -0
  263. sage/combinat/sf/new_kschur.py +1786 -0
  264. sage/combinat/sf/ns_macdonald.py +964 -0
  265. sage/combinat/sf/orthogonal.py +246 -0
  266. sage/combinat/sf/orthotriang.py +355 -0
  267. sage/combinat/sf/powersum.py +963 -0
  268. sage/combinat/sf/schur.py +880 -0
  269. sage/combinat/sf/sf.py +1653 -0
  270. sage/combinat/sf/sfa.py +7053 -0
  271. sage/combinat/sf/symplectic.py +253 -0
  272. sage/combinat/sf/witt.py +721 -0
  273. sage/combinat/shifted_primed_tableau.py +2735 -0
  274. sage/combinat/shuffle.py +830 -0
  275. sage/combinat/sidon_sets.py +146 -0
  276. sage/combinat/similarity_class_type.py +1721 -0
  277. sage/combinat/sine_gordon.py +618 -0
  278. sage/combinat/six_vertex_model.py +784 -0
  279. sage/combinat/skew_partition.py +2053 -0
  280. sage/combinat/skew_tableau.py +2989 -0
  281. sage/combinat/sloane_functions.py +8935 -0
  282. sage/combinat/specht_module.py +1403 -0
  283. sage/combinat/species/all.py +48 -0
  284. sage/combinat/species/characteristic_species.py +321 -0
  285. sage/combinat/species/composition_species.py +273 -0
  286. sage/combinat/species/cycle_species.py +284 -0
  287. sage/combinat/species/empty_species.py +155 -0
  288. sage/combinat/species/functorial_composition_species.py +148 -0
  289. sage/combinat/species/generating_series.py +673 -0
  290. sage/combinat/species/library.py +148 -0
  291. sage/combinat/species/linear_order_species.py +169 -0
  292. sage/combinat/species/misc.py +83 -0
  293. sage/combinat/species/partition_species.py +290 -0
  294. sage/combinat/species/permutation_species.py +268 -0
  295. sage/combinat/species/product_species.py +423 -0
  296. sage/combinat/species/recursive_species.py +476 -0
  297. sage/combinat/species/set_species.py +192 -0
  298. sage/combinat/species/species.py +820 -0
  299. sage/combinat/species/structure.py +539 -0
  300. sage/combinat/species/subset_species.py +243 -0
  301. sage/combinat/species/sum_species.py +225 -0
  302. sage/combinat/subword.py +564 -0
  303. sage/combinat/subword_complex.py +2122 -0
  304. sage/combinat/subword_complex_c.cp314t-win_amd64.pyd +0 -0
  305. sage/combinat/subword_complex_c.pyx +119 -0
  306. sage/combinat/super_tableau.py +821 -0
  307. sage/combinat/superpartition.py +1154 -0
  308. sage/combinat/symmetric_group_algebra.py +3774 -0
  309. sage/combinat/symmetric_group_representations.py +1830 -0
  310. sage/combinat/t_sequences.py +877 -0
  311. sage/combinat/tableau.py +9506 -0
  312. sage/combinat/tableau_residues.py +860 -0
  313. sage/combinat/tableau_tuple.py +5353 -0
  314. sage/combinat/tiling.py +2432 -0
  315. sage/combinat/triangles_FHM.py +777 -0
  316. sage/combinat/tutorial.py +1857 -0
  317. sage/combinat/vector_partition.py +337 -0
  318. sage/combinat/words/abstract_word.py +1722 -0
  319. sage/combinat/words/all.py +59 -0
  320. sage/combinat/words/alphabet.py +268 -0
  321. sage/combinat/words/finite_word.py +7201 -0
  322. sage/combinat/words/infinite_word.py +113 -0
  323. sage/combinat/words/lyndon_word.py +652 -0
  324. sage/combinat/words/morphic.py +351 -0
  325. sage/combinat/words/morphism.py +3878 -0
  326. sage/combinat/words/paths.py +2932 -0
  327. sage/combinat/words/shuffle_product.py +278 -0
  328. sage/combinat/words/suffix_trees.py +1873 -0
  329. sage/combinat/words/word.py +769 -0
  330. sage/combinat/words/word_char.cp314t-win_amd64.pyd +0 -0
  331. sage/combinat/words/word_char.pyx +847 -0
  332. sage/combinat/words/word_datatypes.cp314t-win_amd64.pyd +0 -0
  333. sage/combinat/words/word_datatypes.pxd +4 -0
  334. sage/combinat/words/word_datatypes.pyx +1067 -0
  335. sage/combinat/words/word_generators.py +2026 -0
  336. sage/combinat/words/word_infinite_datatypes.py +1218 -0
  337. sage/combinat/words/word_options.py +99 -0
  338. sage/combinat/words/words.py +2396 -0
  339. sage/data_structures/all__sagemath_combinat.py +1 -0
  340. sage/databases/all__sagemath_combinat.py +13 -0
  341. sage/databases/findstat.py +4897 -0
  342. sage/databases/oeis.py +2058 -0
  343. sage/databases/sloane.py +393 -0
  344. sage/dynamics/all__sagemath_combinat.py +14 -0
  345. sage/dynamics/cellular_automata/all.py +7 -0
  346. sage/dynamics/cellular_automata/catalog.py +34 -0
  347. sage/dynamics/cellular_automata/elementary.py +612 -0
  348. sage/dynamics/cellular_automata/glca.py +477 -0
  349. sage/dynamics/cellular_automata/solitons.py +1463 -0
  350. sage/dynamics/finite_dynamical_system.py +1249 -0
  351. sage/dynamics/finite_dynamical_system_catalog.py +382 -0
  352. sage/games/all.py +7 -0
  353. sage/games/hexad.py +704 -0
  354. sage/games/quantumino.py +591 -0
  355. sage/games/sudoku.py +889 -0
  356. sage/games/sudoku_backtrack.cp314t-win_amd64.pyd +0 -0
  357. sage/games/sudoku_backtrack.pyx +189 -0
  358. sage/groups/all__sagemath_combinat.py +1 -0
  359. sage/groups/indexed_free_group.py +489 -0
  360. sage/libs/all__sagemath_combinat.py +6 -0
  361. sage/libs/lrcalc/__init__.py +1 -0
  362. sage/libs/lrcalc/lrcalc.py +525 -0
  363. sage/libs/symmetrica/__init__.py +7 -0
  364. sage/libs/symmetrica/all.py +101 -0
  365. sage/libs/symmetrica/kostka.pxi +168 -0
  366. sage/libs/symmetrica/part.pxi +193 -0
  367. sage/libs/symmetrica/plet.pxi +42 -0
  368. sage/libs/symmetrica/sab.pxi +196 -0
  369. sage/libs/symmetrica/sb.pxi +332 -0
  370. sage/libs/symmetrica/sc.pxi +192 -0
  371. sage/libs/symmetrica/schur.pxi +956 -0
  372. sage/libs/symmetrica/symmetrica.cp314t-win_amd64.pyd +0 -0
  373. sage/libs/symmetrica/symmetrica.pxi +1172 -0
  374. sage/libs/symmetrica/symmetrica.pyx +39 -0
  375. sage/monoids/all.py +13 -0
  376. sage/monoids/automatic_semigroup.py +1054 -0
  377. sage/monoids/free_abelian_monoid.py +315 -0
  378. sage/monoids/free_abelian_monoid_element.cp314t-win_amd64.pyd +0 -0
  379. sage/monoids/free_abelian_monoid_element.pxd +16 -0
  380. sage/monoids/free_abelian_monoid_element.pyx +397 -0
  381. sage/monoids/free_monoid.py +335 -0
  382. sage/monoids/free_monoid_element.py +431 -0
  383. sage/monoids/hecke_monoid.py +65 -0
  384. sage/monoids/string_monoid.py +817 -0
  385. sage/monoids/string_monoid_element.py +547 -0
  386. sage/monoids/string_ops.py +143 -0
  387. sage/monoids/trace_monoid.py +972 -0
  388. sage/rings/all__sagemath_combinat.py +2 -0
  389. sage/sat/all.py +4 -0
  390. sage/sat/boolean_polynomials.py +405 -0
  391. sage/sat/converters/__init__.py +6 -0
  392. sage/sat/converters/anf2cnf.py +14 -0
  393. sage/sat/converters/polybori.py +611 -0
  394. sage/sat/solvers/__init__.py +5 -0
  395. sage/sat/solvers/cryptominisat.py +287 -0
  396. sage/sat/solvers/dimacs.py +783 -0
  397. sage/sat/solvers/picosat.py +228 -0
  398. sage/sat/solvers/sat_lp.py +156 -0
  399. sage/sat/solvers/satsolver.cp314t-win_amd64.pyd +0 -0
  400. sage/sat/solvers/satsolver.pxd +3 -0
  401. 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)