passagemath-combinat 10.6.42__cp314-cp314-musllinux_1_2_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (400) hide show
  1. passagemath_combinat/__init__.py +3 -0
  2. passagemath_combinat-10.6.42.dist-info/METADATA +160 -0
  3. passagemath_combinat-10.6.42.dist-info/RECORD +400 -0
  4. passagemath_combinat-10.6.42.dist-info/WHEEL +5 -0
  5. passagemath_combinat-10.6.42.dist-info/top_level.txt +3 -0
  6. passagemath_combinat.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
  7. passagemath_combinat.libs/libsymmetrica-81fe8739.so.3.0.0 +0 -0
  8. sage/algebras/affine_nil_temperley_lieb.py +263 -0
  9. sage/algebras/all.py +24 -0
  10. sage/algebras/all__sagemath_combinat.py +35 -0
  11. sage/algebras/askey_wilson.py +935 -0
  12. sage/algebras/associated_graded.py +345 -0
  13. sage/algebras/cellular_basis.py +350 -0
  14. sage/algebras/cluster_algebra.py +2766 -0
  15. sage/algebras/down_up_algebra.py +860 -0
  16. sage/algebras/free_algebra.py +1698 -0
  17. sage/algebras/free_algebra_element.py +345 -0
  18. sage/algebras/free_algebra_quotient.py +405 -0
  19. sage/algebras/free_algebra_quotient_element.py +295 -0
  20. sage/algebras/free_zinbiel_algebra.py +885 -0
  21. sage/algebras/hall_algebra.py +783 -0
  22. sage/algebras/hecke_algebras/all.py +4 -0
  23. sage/algebras/hecke_algebras/ariki_koike_algebra.py +1796 -0
  24. sage/algebras/hecke_algebras/ariki_koike_specht_modules.py +475 -0
  25. sage/algebras/hecke_algebras/cubic_hecke_algebra.py +3520 -0
  26. sage/algebras/hecke_algebras/cubic_hecke_base_ring.py +1473 -0
  27. sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py +1079 -0
  28. sage/algebras/iwahori_hecke_algebra.py +3095 -0
  29. sage/algebras/jordan_algebra.py +1773 -0
  30. sage/algebras/lie_conformal_algebras/abelian_lie_conformal_algebra.py +113 -0
  31. sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py +156 -0
  32. sage/algebras/lie_conformal_algebras/all.py +18 -0
  33. sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra.py +134 -0
  34. sage/algebras/lie_conformal_algebras/examples.py +43 -0
  35. sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra.py +131 -0
  36. sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py +139 -0
  37. sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py +174 -0
  38. sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py +167 -0
  39. sage/algebras/lie_conformal_algebras/freely_generated_lie_conformal_algebra.py +107 -0
  40. sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py +135 -0
  41. sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py +353 -0
  42. sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +236 -0
  43. sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py +78 -0
  44. sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py +328 -0
  45. sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra.py +117 -0
  46. sage/algebras/lie_conformal_algebras/neveu_schwarz_lie_conformal_algebra.py +86 -0
  47. sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra.py +82 -0
  48. sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra.py +205 -0
  49. sage/algebras/nil_coxeter_algebra.py +191 -0
  50. sage/algebras/q_commuting_polynomials.py +673 -0
  51. sage/algebras/q_system.py +608 -0
  52. sage/algebras/quantum_clifford.py +959 -0
  53. sage/algebras/quantum_groups/ace_quantum_onsager.py +693 -0
  54. sage/algebras/quantum_groups/all.py +9 -0
  55. sage/algebras/quantum_groups/fock_space.py +2219 -0
  56. sage/algebras/quantum_groups/q_numbers.py +207 -0
  57. sage/algebras/quantum_groups/quantum_group_gap.py +2695 -0
  58. sage/algebras/quantum_groups/representations.py +591 -0
  59. sage/algebras/quantum_matrix_coordinate_algebra.py +1006 -0
  60. sage/algebras/quantum_oscillator.py +623 -0
  61. sage/algebras/quaternion_algebra.py +20 -0
  62. sage/algebras/quaternion_algebra_element.py +55 -0
  63. sage/algebras/rational_cherednik_algebra.py +525 -0
  64. sage/algebras/schur_algebra.py +670 -0
  65. sage/algebras/shuffle_algebra.py +1011 -0
  66. sage/algebras/splitting_algebra.py +779 -0
  67. sage/algebras/tensor_algebra.py +709 -0
  68. sage/algebras/yangian.py +1082 -0
  69. sage/algebras/yokonuma_hecke_algebra.py +1018 -0
  70. sage/all__sagemath_combinat.py +35 -0
  71. sage/combinat/SJT.py +255 -0
  72. sage/combinat/affine_permutation.py +2405 -0
  73. sage/combinat/algebraic_combinatorics.py +55 -0
  74. sage/combinat/all.py +53 -0
  75. sage/combinat/all__sagemath_combinat.py +195 -0
  76. sage/combinat/alternating_sign_matrix.py +2063 -0
  77. sage/combinat/baxter_permutations.py +346 -0
  78. sage/combinat/bijectionist.py +3220 -0
  79. sage/combinat/binary_recurrence_sequences.py +1180 -0
  80. sage/combinat/blob_algebra.py +685 -0
  81. sage/combinat/catalog_partitions.py +27 -0
  82. sage/combinat/chas/all.py +23 -0
  83. sage/combinat/chas/fsym.py +1180 -0
  84. sage/combinat/chas/wqsym.py +2601 -0
  85. sage/combinat/cluster_complex.py +326 -0
  86. sage/combinat/colored_permutations.py +2039 -0
  87. sage/combinat/colored_permutations_representations.py +964 -0
  88. sage/combinat/composition_signed.py +142 -0
  89. sage/combinat/composition_tableau.py +855 -0
  90. sage/combinat/constellation.py +1729 -0
  91. sage/combinat/core.py +751 -0
  92. sage/combinat/counting.py +12 -0
  93. sage/combinat/crystals/affine.py +742 -0
  94. sage/combinat/crystals/affine_factorization.py +518 -0
  95. sage/combinat/crystals/affinization.py +331 -0
  96. sage/combinat/crystals/alcove_path.py +2013 -0
  97. sage/combinat/crystals/all.py +22 -0
  98. sage/combinat/crystals/bkk_crystals.py +141 -0
  99. sage/combinat/crystals/catalog.py +115 -0
  100. sage/combinat/crystals/catalog_elementary_crystals.py +18 -0
  101. sage/combinat/crystals/catalog_infinity_crystals.py +33 -0
  102. sage/combinat/crystals/catalog_kirillov_reshetikhin.py +18 -0
  103. sage/combinat/crystals/crystals.py +257 -0
  104. sage/combinat/crystals/direct_sum.py +260 -0
  105. sage/combinat/crystals/elementary_crystals.py +1251 -0
  106. sage/combinat/crystals/fast_crystals.py +441 -0
  107. sage/combinat/crystals/fully_commutative_stable_grothendieck.py +1205 -0
  108. sage/combinat/crystals/generalized_young_walls.py +1076 -0
  109. sage/combinat/crystals/highest_weight_crystals.py +436 -0
  110. sage/combinat/crystals/induced_structure.py +695 -0
  111. sage/combinat/crystals/infinity_crystals.py +730 -0
  112. sage/combinat/crystals/kac_modules.py +863 -0
  113. sage/combinat/crystals/kirillov_reshetikhin.py +4196 -0
  114. sage/combinat/crystals/kyoto_path_model.py +497 -0
  115. sage/combinat/crystals/letters.cpython-314-x86_64-linux-musl.so +0 -0
  116. sage/combinat/crystals/letters.pxd +79 -0
  117. sage/combinat/crystals/letters.pyx +3056 -0
  118. sage/combinat/crystals/littelmann_path.py +1518 -0
  119. sage/combinat/crystals/monomial_crystals.py +1262 -0
  120. sage/combinat/crystals/multisegments.py +462 -0
  121. sage/combinat/crystals/mv_polytopes.py +467 -0
  122. sage/combinat/crystals/pbw_crystal.py +511 -0
  123. sage/combinat/crystals/pbw_datum.cpython-314-x86_64-linux-musl.so +0 -0
  124. sage/combinat/crystals/pbw_datum.pxd +4 -0
  125. sage/combinat/crystals/pbw_datum.pyx +487 -0
  126. sage/combinat/crystals/polyhedral_realization.py +372 -0
  127. sage/combinat/crystals/spins.cpython-314-x86_64-linux-musl.so +0 -0
  128. sage/combinat/crystals/spins.pxd +21 -0
  129. sage/combinat/crystals/spins.pyx +756 -0
  130. sage/combinat/crystals/star_crystal.py +290 -0
  131. sage/combinat/crystals/subcrystal.py +464 -0
  132. sage/combinat/crystals/tensor_product.py +1177 -0
  133. sage/combinat/crystals/tensor_product_element.cpython-314-x86_64-linux-musl.so +0 -0
  134. sage/combinat/crystals/tensor_product_element.pxd +35 -0
  135. sage/combinat/crystals/tensor_product_element.pyx +1870 -0
  136. sage/combinat/crystals/virtual_crystal.py +420 -0
  137. sage/combinat/cyclic_sieving_phenomenon.py +204 -0
  138. sage/combinat/debruijn_sequence.cpython-314-x86_64-linux-musl.so +0 -0
  139. sage/combinat/debruijn_sequence.pyx +355 -0
  140. sage/combinat/decorated_permutation.py +270 -0
  141. sage/combinat/degree_sequences.cpython-314-x86_64-linux-musl.so +0 -0
  142. sage/combinat/degree_sequences.pyx +588 -0
  143. sage/combinat/derangements.py +527 -0
  144. sage/combinat/descent_algebra.py +1008 -0
  145. sage/combinat/diagram.py +1551 -0
  146. sage/combinat/diagram_algebras.py +5886 -0
  147. sage/combinat/dyck_word.py +4349 -0
  148. sage/combinat/e_one_star.py +1623 -0
  149. sage/combinat/enumerated_sets.py +123 -0
  150. sage/combinat/expnums.cpython-314-x86_64-linux-musl.so +0 -0
  151. sage/combinat/expnums.pyx +148 -0
  152. sage/combinat/fast_vector_partitions.cpython-314-x86_64-linux-musl.so +0 -0
  153. sage/combinat/fast_vector_partitions.pyx +346 -0
  154. sage/combinat/fqsym.py +1977 -0
  155. sage/combinat/free_dendriform_algebra.py +954 -0
  156. sage/combinat/free_prelie_algebra.py +1141 -0
  157. sage/combinat/fully_commutative_elements.py +1077 -0
  158. sage/combinat/fully_packed_loop.py +1523 -0
  159. sage/combinat/gelfand_tsetlin_patterns.py +1409 -0
  160. sage/combinat/gray_codes.py +311 -0
  161. sage/combinat/grossman_larson_algebras.py +667 -0
  162. sage/combinat/growth.py +4352 -0
  163. sage/combinat/hall_polynomial.py +188 -0
  164. sage/combinat/hillman_grassl.py +866 -0
  165. sage/combinat/integer_matrices.py +329 -0
  166. sage/combinat/integer_vectors_mod_permgroup.py +1238 -0
  167. sage/combinat/k_tableau.py +4564 -0
  168. sage/combinat/kazhdan_lusztig.py +215 -0
  169. sage/combinat/key_polynomial.py +885 -0
  170. sage/combinat/knutson_tao_puzzles.py +2286 -0
  171. sage/combinat/lr_tableau.py +311 -0
  172. sage/combinat/matrices/all.py +24 -0
  173. sage/combinat/matrices/hadamard_matrix.py +3790 -0
  174. sage/combinat/matrices/latin.py +2912 -0
  175. sage/combinat/misc.py +401 -0
  176. sage/combinat/multiset_partition_into_sets_ordered.py +3541 -0
  177. sage/combinat/ncsf_qsym/all.py +21 -0
  178. sage/combinat/ncsf_qsym/combinatorics.py +317 -0
  179. sage/combinat/ncsf_qsym/generic_basis_code.py +1427 -0
  180. sage/combinat/ncsf_qsym/ncsf.py +5637 -0
  181. sage/combinat/ncsf_qsym/qsym.py +4053 -0
  182. sage/combinat/ncsf_qsym/tutorial.py +447 -0
  183. sage/combinat/ncsym/all.py +21 -0
  184. sage/combinat/ncsym/bases.py +855 -0
  185. sage/combinat/ncsym/dual.py +593 -0
  186. sage/combinat/ncsym/ncsym.py +2076 -0
  187. sage/combinat/necklace.py +551 -0
  188. sage/combinat/non_decreasing_parking_function.py +634 -0
  189. sage/combinat/nu_dyck_word.py +1474 -0
  190. sage/combinat/output.py +861 -0
  191. sage/combinat/parallelogram_polyomino.py +4326 -0
  192. sage/combinat/parking_functions.py +1602 -0
  193. sage/combinat/partition_algebra.py +1998 -0
  194. sage/combinat/partition_kleshchev.py +1982 -0
  195. sage/combinat/partition_shifting_algebras.py +584 -0
  196. sage/combinat/partition_tuple.py +3114 -0
  197. sage/combinat/path_tableaux/all.py +13 -0
  198. sage/combinat/path_tableaux/catalog.py +29 -0
  199. sage/combinat/path_tableaux/dyck_path.py +380 -0
  200. sage/combinat/path_tableaux/frieze.py +476 -0
  201. sage/combinat/path_tableaux/path_tableau.py +728 -0
  202. sage/combinat/path_tableaux/semistandard.py +510 -0
  203. sage/combinat/perfect_matching.py +779 -0
  204. sage/combinat/plane_partition.py +3300 -0
  205. sage/combinat/q_bernoulli.cpython-314-x86_64-linux-musl.so +0 -0
  206. sage/combinat/q_bernoulli.pyx +128 -0
  207. sage/combinat/quickref.py +81 -0
  208. sage/combinat/recognizable_series.py +2051 -0
  209. sage/combinat/regular_sequence.py +4316 -0
  210. sage/combinat/regular_sequence_bounded.py +543 -0
  211. sage/combinat/restricted_growth.py +81 -0
  212. sage/combinat/ribbon.py +20 -0
  213. sage/combinat/ribbon_shaped_tableau.py +489 -0
  214. sage/combinat/ribbon_tableau.py +1180 -0
  215. sage/combinat/rigged_configurations/all.py +46 -0
  216. sage/combinat/rigged_configurations/bij_abstract_class.py +548 -0
  217. sage/combinat/rigged_configurations/bij_infinity.py +370 -0
  218. sage/combinat/rigged_configurations/bij_type_A.py +163 -0
  219. sage/combinat/rigged_configurations/bij_type_A2_dual.py +338 -0
  220. sage/combinat/rigged_configurations/bij_type_A2_even.py +218 -0
  221. sage/combinat/rigged_configurations/bij_type_A2_odd.py +199 -0
  222. sage/combinat/rigged_configurations/bij_type_B.py +900 -0
  223. sage/combinat/rigged_configurations/bij_type_C.py +267 -0
  224. sage/combinat/rigged_configurations/bij_type_D.py +771 -0
  225. sage/combinat/rigged_configurations/bij_type_D_tri.py +392 -0
  226. sage/combinat/rigged_configurations/bij_type_D_twisted.py +576 -0
  227. sage/combinat/rigged_configurations/bij_type_E67.py +402 -0
  228. sage/combinat/rigged_configurations/bijection.py +143 -0
  229. sage/combinat/rigged_configurations/kleber_tree.py +1475 -0
  230. sage/combinat/rigged_configurations/kr_tableaux.py +1898 -0
  231. sage/combinat/rigged_configurations/rc_crystal.py +461 -0
  232. sage/combinat/rigged_configurations/rc_infinity.py +540 -0
  233. sage/combinat/rigged_configurations/rigged_configuration_element.py +2403 -0
  234. sage/combinat/rigged_configurations/rigged_configurations.py +1918 -0
  235. sage/combinat/rigged_configurations/rigged_partition.cpython-314-x86_64-linux-musl.so +0 -0
  236. sage/combinat/rigged_configurations/rigged_partition.pxd +15 -0
  237. sage/combinat/rigged_configurations/rigged_partition.pyx +680 -0
  238. sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +499 -0
  239. sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py +428 -0
  240. sage/combinat/rsk.py +3438 -0
  241. sage/combinat/schubert_polynomial.py +508 -0
  242. sage/combinat/set_partition.py +3318 -0
  243. sage/combinat/set_partition_iterator.cpython-314-x86_64-linux-musl.so +0 -0
  244. sage/combinat/set_partition_iterator.pyx +136 -0
  245. sage/combinat/set_partition_ordered.py +1590 -0
  246. sage/combinat/sf/abreu_nigro.py +346 -0
  247. sage/combinat/sf/all.py +52 -0
  248. sage/combinat/sf/character.py +576 -0
  249. sage/combinat/sf/classical.py +319 -0
  250. sage/combinat/sf/dual.py +996 -0
  251. sage/combinat/sf/elementary.py +549 -0
  252. sage/combinat/sf/hall_littlewood.py +1028 -0
  253. sage/combinat/sf/hecke.py +336 -0
  254. sage/combinat/sf/homogeneous.py +464 -0
  255. sage/combinat/sf/jack.py +1428 -0
  256. sage/combinat/sf/k_dual.py +1458 -0
  257. sage/combinat/sf/kfpoly.py +447 -0
  258. sage/combinat/sf/llt.py +789 -0
  259. sage/combinat/sf/macdonald.py +2019 -0
  260. sage/combinat/sf/monomial.py +525 -0
  261. sage/combinat/sf/multiplicative.py +113 -0
  262. sage/combinat/sf/new_kschur.py +1786 -0
  263. sage/combinat/sf/ns_macdonald.py +964 -0
  264. sage/combinat/sf/orthogonal.py +246 -0
  265. sage/combinat/sf/orthotriang.py +355 -0
  266. sage/combinat/sf/powersum.py +963 -0
  267. sage/combinat/sf/schur.py +880 -0
  268. sage/combinat/sf/sf.py +1653 -0
  269. sage/combinat/sf/sfa.py +7053 -0
  270. sage/combinat/sf/symplectic.py +253 -0
  271. sage/combinat/sf/witt.py +721 -0
  272. sage/combinat/shifted_primed_tableau.py +2735 -0
  273. sage/combinat/shuffle.py +830 -0
  274. sage/combinat/sidon_sets.py +146 -0
  275. sage/combinat/similarity_class_type.py +1721 -0
  276. sage/combinat/sine_gordon.py +618 -0
  277. sage/combinat/six_vertex_model.py +784 -0
  278. sage/combinat/skew_partition.py +2053 -0
  279. sage/combinat/skew_tableau.py +2989 -0
  280. sage/combinat/sloane_functions.py +8935 -0
  281. sage/combinat/specht_module.py +1403 -0
  282. sage/combinat/species/all.py +48 -0
  283. sage/combinat/species/characteristic_species.py +321 -0
  284. sage/combinat/species/composition_species.py +273 -0
  285. sage/combinat/species/cycle_species.py +284 -0
  286. sage/combinat/species/empty_species.py +155 -0
  287. sage/combinat/species/functorial_composition_species.py +148 -0
  288. sage/combinat/species/generating_series.py +673 -0
  289. sage/combinat/species/library.py +148 -0
  290. sage/combinat/species/linear_order_species.py +169 -0
  291. sage/combinat/species/misc.py +83 -0
  292. sage/combinat/species/partition_species.py +290 -0
  293. sage/combinat/species/permutation_species.py +268 -0
  294. sage/combinat/species/product_species.py +423 -0
  295. sage/combinat/species/recursive_species.py +476 -0
  296. sage/combinat/species/set_species.py +192 -0
  297. sage/combinat/species/species.py +820 -0
  298. sage/combinat/species/structure.py +539 -0
  299. sage/combinat/species/subset_species.py +243 -0
  300. sage/combinat/species/sum_species.py +225 -0
  301. sage/combinat/subword.py +564 -0
  302. sage/combinat/subword_complex.py +2122 -0
  303. sage/combinat/subword_complex_c.cpython-314-x86_64-linux-musl.so +0 -0
  304. sage/combinat/subword_complex_c.pyx +119 -0
  305. sage/combinat/super_tableau.py +821 -0
  306. sage/combinat/superpartition.py +1154 -0
  307. sage/combinat/symmetric_group_algebra.py +3774 -0
  308. sage/combinat/symmetric_group_representations.py +1830 -0
  309. sage/combinat/t_sequences.py +877 -0
  310. sage/combinat/tableau.py +9506 -0
  311. sage/combinat/tableau_residues.py +860 -0
  312. sage/combinat/tableau_tuple.py +5353 -0
  313. sage/combinat/tiling.py +2432 -0
  314. sage/combinat/triangles_FHM.py +777 -0
  315. sage/combinat/tutorial.py +1857 -0
  316. sage/combinat/vector_partition.py +337 -0
  317. sage/combinat/words/abstract_word.py +1722 -0
  318. sage/combinat/words/all.py +59 -0
  319. sage/combinat/words/alphabet.py +268 -0
  320. sage/combinat/words/finite_word.py +7201 -0
  321. sage/combinat/words/infinite_word.py +113 -0
  322. sage/combinat/words/lyndon_word.py +652 -0
  323. sage/combinat/words/morphic.py +351 -0
  324. sage/combinat/words/morphism.py +3878 -0
  325. sage/combinat/words/paths.py +2932 -0
  326. sage/combinat/words/shuffle_product.py +278 -0
  327. sage/combinat/words/suffix_trees.py +1873 -0
  328. sage/combinat/words/word.py +769 -0
  329. sage/combinat/words/word_char.cpython-314-x86_64-linux-musl.so +0 -0
  330. sage/combinat/words/word_char.pyx +847 -0
  331. sage/combinat/words/word_datatypes.cpython-314-x86_64-linux-musl.so +0 -0
  332. sage/combinat/words/word_datatypes.pxd +4 -0
  333. sage/combinat/words/word_datatypes.pyx +1067 -0
  334. sage/combinat/words/word_generators.py +2026 -0
  335. sage/combinat/words/word_infinite_datatypes.py +1218 -0
  336. sage/combinat/words/word_options.py +99 -0
  337. sage/combinat/words/words.py +2396 -0
  338. sage/data_structures/all__sagemath_combinat.py +1 -0
  339. sage/databases/all__sagemath_combinat.py +13 -0
  340. sage/databases/findstat.py +4897 -0
  341. sage/databases/oeis.py +2058 -0
  342. sage/databases/sloane.py +393 -0
  343. sage/dynamics/all__sagemath_combinat.py +14 -0
  344. sage/dynamics/cellular_automata/all.py +7 -0
  345. sage/dynamics/cellular_automata/catalog.py +34 -0
  346. sage/dynamics/cellular_automata/elementary.py +612 -0
  347. sage/dynamics/cellular_automata/glca.py +477 -0
  348. sage/dynamics/cellular_automata/solitons.py +1463 -0
  349. sage/dynamics/finite_dynamical_system.py +1249 -0
  350. sage/dynamics/finite_dynamical_system_catalog.py +382 -0
  351. sage/games/all.py +7 -0
  352. sage/games/hexad.py +704 -0
  353. sage/games/quantumino.py +591 -0
  354. sage/games/sudoku.py +889 -0
  355. sage/games/sudoku_backtrack.cpython-314-x86_64-linux-musl.so +0 -0
  356. sage/games/sudoku_backtrack.pyx +189 -0
  357. sage/groups/all__sagemath_combinat.py +1 -0
  358. sage/groups/indexed_free_group.py +489 -0
  359. sage/libs/all__sagemath_combinat.py +6 -0
  360. sage/libs/lrcalc/__init__.py +1 -0
  361. sage/libs/lrcalc/lrcalc.py +525 -0
  362. sage/libs/symmetrica/__init__.py +7 -0
  363. sage/libs/symmetrica/all.py +101 -0
  364. sage/libs/symmetrica/kostka.pxi +168 -0
  365. sage/libs/symmetrica/part.pxi +193 -0
  366. sage/libs/symmetrica/plet.pxi +42 -0
  367. sage/libs/symmetrica/sab.pxi +196 -0
  368. sage/libs/symmetrica/sb.pxi +332 -0
  369. sage/libs/symmetrica/sc.pxi +192 -0
  370. sage/libs/symmetrica/schur.pxi +956 -0
  371. sage/libs/symmetrica/symmetrica.cpython-314-x86_64-linux-musl.so +0 -0
  372. sage/libs/symmetrica/symmetrica.pxi +1172 -0
  373. sage/libs/symmetrica/symmetrica.pyx +39 -0
  374. sage/monoids/all.py +13 -0
  375. sage/monoids/automatic_semigroup.py +1054 -0
  376. sage/monoids/free_abelian_monoid.py +315 -0
  377. sage/monoids/free_abelian_monoid_element.cpython-314-x86_64-linux-musl.so +0 -0
  378. sage/monoids/free_abelian_monoid_element.pxd +16 -0
  379. sage/monoids/free_abelian_monoid_element.pyx +397 -0
  380. sage/monoids/free_monoid.py +335 -0
  381. sage/monoids/free_monoid_element.py +431 -0
  382. sage/monoids/hecke_monoid.py +65 -0
  383. sage/monoids/string_monoid.py +817 -0
  384. sage/monoids/string_monoid_element.py +547 -0
  385. sage/monoids/string_ops.py +143 -0
  386. sage/monoids/trace_monoid.py +972 -0
  387. sage/rings/all__sagemath_combinat.py +2 -0
  388. sage/sat/all.py +4 -0
  389. sage/sat/boolean_polynomials.py +405 -0
  390. sage/sat/converters/__init__.py +6 -0
  391. sage/sat/converters/anf2cnf.py +14 -0
  392. sage/sat/converters/polybori.py +611 -0
  393. sage/sat/solvers/__init__.py +5 -0
  394. sage/sat/solvers/cryptominisat.py +287 -0
  395. sage/sat/solvers/dimacs.py +783 -0
  396. sage/sat/solvers/picosat.py +228 -0
  397. sage/sat/solvers/sat_lp.py +156 -0
  398. sage/sat/solvers/satsolver.cpython-314-x86_64-linux-musl.so +0 -0
  399. sage/sat/solvers/satsolver.pxd +3 -0
  400. sage/sat/solvers/satsolver.pyx +405 -0
@@ -0,0 +1,3318 @@
1
+ # sage_setup: distribution = sagemath-combinat
2
+ r"""
3
+ Set partitions
4
+
5
+ This module defines a class for immutable partitioning of a set. For
6
+ mutable version see :func:`DisjointSet`.
7
+
8
+ AUTHORS:
9
+
10
+ - Mike Hansen
11
+ - MuPAD-Combinat developers (for algorithms and design inspiration).
12
+ - Travis Scrimshaw (2013-02-28): Removed ``CombinatorialClass`` and added
13
+ entry point through :class:`SetPartition`.
14
+ - Martin Rubey (2017-10-10): Cleanup, add crossings and nestings, add
15
+ random generation.
16
+ """
17
+ # ****************************************************************************
18
+ # Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>,
19
+ #
20
+ # Distributed under the terms of the GNU General Public License (GPL)
21
+ #
22
+ # This code is distributed in the hope that it will be useful,
23
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
24
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25
+ # General Public License for more details.
26
+ #
27
+ # The full text of the GPL is available at:
28
+ #
29
+ # https://www.gnu.org/licenses/
30
+ # ****************************************************************************
31
+ import itertools
32
+ from itertools import repeat
33
+ from sage.sets.set import Set, Set_generic
34
+
35
+ from sage.structure.parent import Parent
36
+ from sage.structure.unique_representation import UniqueRepresentation
37
+ from sage.structure.list_clone import ClonableArray
38
+ from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets
39
+ from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
40
+ from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass
41
+ from sage.misc.lazy_import import lazy_import
42
+ from sage.rings.infinity import infinity
43
+ from sage.rings.integer import Integer
44
+ from sage.combinat.combinatorial_map import combinatorial_map
45
+ from sage.combinat.set_partition_iterator import (set_partition_iterator,
46
+ set_partition_iterator_blocks)
47
+ from sage.combinat.partition import Partition, Partitions
48
+ from sage.combinat.combinat import bell_number, stirling_number2 as stirling2
49
+ from sage.combinat.permutation import Permutation
50
+ from sage.arith.misc import factorial
51
+ from sage.misc.prandom import random, randint, sample
52
+ from sage.sets.disjoint_set import DisjointSet
53
+
54
+ lazy_import('sage.combinat.posets.hasse_diagram', 'HasseDiagram')
55
+ lazy_import('sage.probability.probability_distribution', 'GeneralDiscreteDistribution')
56
+
57
+
58
+ class AbstractSetPartition(ClonableArray,
59
+ metaclass=InheritComparisonClasscallMetaclass):
60
+ r"""
61
+ Methods of set partitions which are independent of the base set
62
+ """
63
+
64
+ def _repr_(self):
65
+ """
66
+ Return a string representation of ``self``.
67
+
68
+ EXAMPLES::
69
+
70
+ sage: S = SetPartitions(4)
71
+ sage: S([[1,3],[2,4]])
72
+ {{1, 3}, {2, 4}}
73
+ """
74
+ try:
75
+ s = [sorted(x) for x in self]
76
+ except TypeError:
77
+ s = [sorted(x, key=str) for x in self]
78
+ return '{' + ', '.join('{' + repr(x)[1:-1] + '}' for x in s) + '}'
79
+
80
+ def __hash__(self):
81
+ """
82
+ Return the hash of ``self``.
83
+
84
+ The parent is not included as part of the hash.
85
+
86
+ EXAMPLES::
87
+
88
+ sage: P = SetPartitions(4)
89
+ sage: A = SetPartition([[1], [2,3], [4]])
90
+ sage: B = P([[1], [2,3], [4]])
91
+ sage: hash(A) == hash(B)
92
+ True
93
+ """
94
+ return sum(hash(x) for x in self)
95
+
96
+ def __eq__(self, y):
97
+ """
98
+ Check equality of ``self`` and ``y``.
99
+
100
+ The parent is not included as part of the equality check.
101
+
102
+ EXAMPLES::
103
+
104
+ sage: P = SetPartitions(4)
105
+ sage: A = SetPartition([[1], [2,3], [4]])
106
+ sage: B = P([[1], [2,3], [4]])
107
+ sage: A == B
108
+ True
109
+ sage: C = P([[2, 3], [1], [4]])
110
+ sage: A == C
111
+ True
112
+ sage: D = P([[1], [2, 4], [3]])
113
+ sage: A == D
114
+ False
115
+
116
+ Note that this may give incorrect answers if the base set is not totally ordered::
117
+
118
+ sage: a,b = frozenset([0,1]), frozenset([2,3])
119
+ sage: p1 = SetPartition([[a], [b]])
120
+ sage: p2 = SetPartition([[b], [a]])
121
+ sage: p1 == p2
122
+ False
123
+ """
124
+ if not isinstance(y, AbstractSetPartition):
125
+ return False
126
+ return list(self) == list(y)
127
+
128
+ def __ne__(self, y):
129
+ """
130
+ Check lack of equality of ``self`` and ``y``.
131
+
132
+ The parent is not included as part of the equality check.
133
+
134
+ EXAMPLES::
135
+
136
+ sage: P = SetPartitions(4)
137
+ sage: A = SetPartition([[1], [2,3], [4]])
138
+ sage: B = P([[1], [2,3], [4]])
139
+ sage: A != B
140
+ False
141
+ sage: C = P([[2, 3], [1], [4]])
142
+ sage: A != C
143
+ False
144
+ sage: D = P([[1], [2, 4], [3]])
145
+ sage: A != D
146
+ True
147
+
148
+ Note that this may give incorrect answers if the base set is not totally ordered::
149
+
150
+ sage: a,b = frozenset([0,1]), frozenset([2,3])
151
+ sage: p1 = SetPartition([[a], [b]])
152
+ sage: p2 = SetPartition([[b], [a]])
153
+ sage: p1 != p2
154
+ True
155
+ """
156
+ return not (self == y)
157
+
158
+ def __lt__(self, y):
159
+ """
160
+ Check that ``self`` is less than ``y``.
161
+
162
+ The ordering used is lexicographic, where:
163
+
164
+ - a set partition is considered as the list of its parts
165
+ sorted by increasing smallest element;
166
+
167
+ - each part is regarded as a list of its elements, sorted
168
+ in increasing order;
169
+
170
+ - the parts themselves are compared lexicographically.
171
+
172
+ EXAMPLES::
173
+
174
+ sage: P = SetPartitions(4)
175
+ sage: A = P([[1], [2,3], [4]])
176
+ sage: B = SetPartition([[1,2,3], [4]])
177
+ sage: A < B
178
+ True
179
+ sage: C = P([[1,2,4], [3]])
180
+ sage: B < C
181
+ True
182
+ sage: B < B
183
+ False
184
+ sage: D = P([[1,4], [2], [3]])
185
+ sage: E = P([[1,4], [2,3]])
186
+ sage: D < E
187
+ True
188
+ sage: F = P([[1,2,4], [3]])
189
+ sage: E < C
190
+ False
191
+ sage: A < E
192
+ True
193
+ sage: A < C
194
+ True
195
+ """
196
+ if not isinstance(y, AbstractSetPartition):
197
+ return False
198
+ return [sorted(i) for i in self] < [sorted(i) for i in y]
199
+
200
+ def __gt__(self, y):
201
+ """
202
+ Check that ``self`` is greater than ``y``.
203
+
204
+ The ordering used is lexicographic, where:
205
+
206
+ - a set partition is considered as the list of its parts
207
+ sorted by increasing smallest element;
208
+
209
+ - each part is regarded as a list of its elements, sorted
210
+ in increasing order;
211
+
212
+ - the parts themselves are compared lexicographically.
213
+
214
+ EXAMPLES::
215
+
216
+ sage: P = SetPartitions(4)
217
+ sage: A = P([[1], [2,3], [4]])
218
+ sage: B = SetPartition([[1,2,3], [4]])
219
+ sage: B > A
220
+ True
221
+ sage: A > B
222
+ False
223
+ """
224
+ if not isinstance(y, AbstractSetPartition):
225
+ return False
226
+ return [sorted(i) for i in self] > [sorted(i) for i in y]
227
+
228
+ def __le__(self, y):
229
+ """
230
+ Check that ``self`` is less than or equals ``y``.
231
+
232
+ The ordering used is lexicographic, where:
233
+
234
+ - a set partition is considered as the list of its parts
235
+ sorted by increasing smallest element;
236
+
237
+ - each part is regarded as a list of its elements, sorted
238
+ in increasing order;
239
+
240
+ - the parts themselves are compared lexicographically.
241
+
242
+ EXAMPLES::
243
+
244
+ sage: P = SetPartitions(4)
245
+ sage: A = P([[1], [2,3], [4]])
246
+ sage: B = SetPartition([[1,2,3], [4]])
247
+ sage: A <= B
248
+ True
249
+ sage: A <= A
250
+ True
251
+ """
252
+ return self == y or self < y
253
+
254
+ def __ge__(self, y):
255
+ """
256
+ Check that ``self`` is greater than or equals ``y``.
257
+
258
+ The ordering used is lexicographic, where:
259
+
260
+ - a set partition is considered as the list of its parts
261
+ sorted by increasing smallest element;
262
+
263
+ - each part is regarded as a list of its elements, sorted
264
+ in increasing order;
265
+
266
+ - the parts themselves are compared lexicographically.
267
+
268
+ EXAMPLES::
269
+
270
+ sage: P = SetPartitions(4)
271
+ sage: A = P([[1], [2,3], [4]])
272
+ sage: B = SetPartition([[1,2,3], [4]])
273
+ sage: B >= A
274
+ True
275
+ sage: B >= B
276
+ True
277
+ """
278
+ return self == y or self > y
279
+
280
+ def __mul__(self, other):
281
+ r"""
282
+ The product of the set partitions ``self`` and ``other``.
283
+
284
+ The product of two set partitions `B` and `C` is defined as the
285
+ set partition whose parts are the nonempty intersections between
286
+ each part of `B` and each part of `C`. This product is also
287
+ the infimum of `B` and `C` in the classical set partition
288
+ lattice (that is, the coarsest set partition which is finer than
289
+ each of `B` and `C`). Consequently, ``inf`` acts as an alias for
290
+ this method.
291
+
292
+ .. SEEALSO::
293
+
294
+ :meth:`sup`
295
+
296
+ EXAMPLES::
297
+
298
+ sage: x = SetPartition([ [1,2], [3,5,4] ])
299
+ sage: y = SetPartition(( (3,1,2), (5,4) ))
300
+ sage: x * y
301
+ {{1, 2}, {3}, {4, 5}}
302
+
303
+ sage: S = SetPartitions(4)
304
+ sage: sp1 = S([[2,3,4], [1]])
305
+ sage: sp2 = S([[1,3], [2,4]])
306
+ sage: s = S([[2,4], [3], [1]])
307
+ sage: sp1.inf(sp2) == s
308
+ True
309
+
310
+ TESTS:
311
+
312
+ Here is a different implementation of the ``__mul__`` method
313
+ (one that was formerly used for the ``inf`` method, before it
314
+ was realized that the methods do the same thing)::
315
+
316
+ sage: def mul2(s, t):
317
+ ....: temp = [ss.intersection(ts) for ss in s for ts in t]
318
+ ....: temp = filter(bool, temp)
319
+ ....: return s.__class__(s.parent(), temp)
320
+
321
+ Let us check that this gives the same as ``__mul__`` on set
322
+ partitions of `\{1, 2, 3, 4\}`::
323
+
324
+ sage: all( all( mul2(s, t) == s * t for s in SetPartitions(4) )
325
+ ....: for t in SetPartitions(4) )
326
+ True
327
+ """
328
+ new_composition = []
329
+ for B in self:
330
+ for C in other:
331
+ BintC = B.intersection(C)
332
+ if BintC:
333
+ new_composition.append(BintC)
334
+ return SetPartition(new_composition)
335
+
336
+ inf = __mul__
337
+
338
+ def sup(self, t):
339
+ """
340
+ Return the supremum of ``self`` and ``t`` in the classical set
341
+ partition lattice.
342
+
343
+ The supremum of two set partitions `B` and `C` is obtained as the
344
+ transitive closure of the relation which relates `i` to `j` if
345
+ and only if `i` and `j` are in the same part in at least
346
+ one of the set partitions `B` and `C`.
347
+
348
+ .. SEEALSO::
349
+
350
+ :meth:`__mul__`
351
+
352
+ EXAMPLES::
353
+
354
+ sage: S = SetPartitions(4)
355
+ sage: sp1 = S([[2,3,4], [1]])
356
+ sage: sp2 = S([[1,3], [2,4]])
357
+ sage: s = S([[1,2,3,4]])
358
+ sage: sp1.sup(sp2) == s
359
+ True
360
+ """
361
+ res = list(self)
362
+ for p in t:
363
+ # find blocks in res which intersect p
364
+ inters = [(i, q) for i, q in enumerate(res)
365
+ if any(a in q for a in p)]
366
+ # remove these blocks from res
367
+ for i, _ in reversed(inters):
368
+ del res[i]
369
+ # add the union
370
+ res.append([e for _, q in inters for e in q])
371
+ return self.parent()(res)
372
+
373
+ def standard_form(self):
374
+ r"""
375
+ Return ``self`` as a list of lists.
376
+
377
+ When the ground set is totally ordered, the elements of each
378
+ block are listed in increasing order.
379
+
380
+ This is not related to standard set partitions (which simply
381
+ means set partitions of `[n] = \{ 1, 2, \ldots , n \}` for some
382
+ integer `n`) or standardization (:meth:`standardization`).
383
+
384
+ EXAMPLES::
385
+
386
+ sage: [x.standard_form() for x in SetPartitions(4, [2,2])] # needs sage.graphs sage.rings.finite_rings
387
+ [[[1, 2], [3, 4]], [[1, 4], [2, 3]], [[1, 3], [2, 4]]]
388
+
389
+ TESTS::
390
+
391
+ sage: SetPartition([(1, 9, 8), (2, 3, 4, 5, 6, 7)]).standard_form()
392
+ [[1, 8, 9], [2, 3, 4, 5, 6, 7]]
393
+ """
394
+ return [sorted(i) for i in self]
395
+
396
+ def base_set(self):
397
+ """
398
+ Return the base set of ``self``, which is the union of all parts
399
+ of ``self``.
400
+
401
+ EXAMPLES::
402
+
403
+ sage: SetPartition([[1], [2,3], [4]]).base_set()
404
+ {1, 2, 3, 4}
405
+ sage: SetPartition([[1,2,3,4]]).base_set()
406
+ {1, 2, 3, 4}
407
+ sage: SetPartition([]).base_set()
408
+ {}
409
+ """
410
+ return Set(e for p in self for e in p)
411
+
412
+ def base_set_cardinality(self):
413
+ """
414
+ Return the cardinality of the base set of ``self``, which is the sum
415
+ of the sizes of the parts of ``self``.
416
+
417
+ This is also known as the *size* (sometimes the *weight*) of
418
+ a set partition.
419
+
420
+ EXAMPLES::
421
+
422
+ sage: SetPartition([[1], [2,3], [4]]).base_set_cardinality()
423
+ 4
424
+ sage: SetPartition([[1,2,3,4]]).base_set_cardinality()
425
+ 4
426
+ """
427
+ return sum(len(x) for x in self)
428
+
429
+ def coarsenings(self):
430
+ """
431
+ Return a list of coarsenings of ``self``.
432
+
433
+ .. SEEALSO::
434
+
435
+ :meth:`refinements`
436
+
437
+ EXAMPLES::
438
+
439
+ sage: SetPartition([[1,3],[2,4]]).coarsenings()
440
+ [{{1, 2, 3, 4}}, {{1, 3}, {2, 4}}]
441
+ sage: SetPartition([[1],[2,4],[3]]).coarsenings()
442
+ [{{1, 2, 3, 4}},
443
+ {{1, 2, 4}, {3}},
444
+ {{1, 3}, {2, 4}},
445
+ {{1}, {2, 3, 4}},
446
+ {{1}, {2, 4}, {3}}]
447
+ sage: SetPartition([]).coarsenings()
448
+ [{}]
449
+ """
450
+ SP = SetPartitions(len(self))
451
+
452
+ def union(s):
453
+ # Return the partition obtained by combining, for every
454
+ # part of s, those parts of self which are indexed by
455
+ # the elements of this part of s into a single part.
456
+ ret = []
457
+ for part in s:
458
+ cur = []
459
+ for i in part:
460
+ cur.extend(self[i - 1]) # -1 for indexing
461
+ ret.append(cur)
462
+ return ret
463
+ return [self.parent()(union(s)) for s in SP]
464
+
465
+ def max_block_size(self):
466
+ r"""
467
+ The maximum block size of the diagram.
468
+
469
+ EXAMPLES::
470
+
471
+ sage: # needs sage.modules
472
+ sage: from sage.combinat.diagram_algebras import PartitionDiagram, PartitionDiagrams
473
+ sage: pd = PartitionDiagram([[1,-3,-5],[2,4],[3,-1,-2],[5],[-4]])
474
+ sage: pd.max_block_size()
475
+ 3
476
+ sage: sorted(d.max_block_size() for d in PartitionDiagrams(2))
477
+ [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4]
478
+ sage: sorted(sp.max_block_size() for sp in SetPartitions(3))
479
+ [1, 2, 2, 2, 3]
480
+ """
481
+ return max(len(block) for block in self)
482
+
483
+ def conjugate(self):
484
+ r"""
485
+ An involution exchanging singletons and circular adjacencies.
486
+
487
+ This method implements the definition of the conjugate of
488
+ a set partition defined in [Cal2005]_.
489
+
490
+ INPUT:
491
+
492
+ - ``self`` -- set partition of an ordered set
493
+
494
+ OUTPUT: a set partition
495
+
496
+ EXAMPLES::
497
+
498
+ sage: SetPartition([[1,6,7],[2,8],[3,4,5]]).conjugate()
499
+ {{1, 4, 7}, {2, 8}, {3}, {5}, {6}}
500
+ sage: all(sp.conjugate().conjugate()==sp for sp in SetPartitions([1,3,5,7]))
501
+ True
502
+ sage: SetPartition([]).conjugate()
503
+ {}
504
+ """
505
+ def next_one(a, support):
506
+ return support[(support.index(a) + 1) % len(support)]
507
+
508
+ def addback(S, terminals, rsupport):
509
+ out = list(S)
510
+ for a in terminals * 2:
511
+ if a not in out and next_one(a, rsupport) in out:
512
+ out.append(a)
513
+ return out
514
+
515
+ def pre_conjugate(sp):
516
+ if len(sp) <= 1:
517
+ return SetPartition([[a] for S in sp for a in S])
518
+ if sp.max_block_size() == 1:
519
+ return SetPartition([sp.base_set()])
520
+ support = sorted(a for S in sp for a in S)
521
+ initials = [a for S in sp for a in S if next_one(a, support) in S]
522
+ singletons = [a for S in sp for a in S if len(S) == 1]
523
+ if not initials and not singletons:
524
+ return sp
525
+ rho = pre_conjugate(
526
+ SetPartition([[a for a in S if a not in initials]
527
+ for S in sp if len(S) > 1 and any(a not in initials for a in S)]))
528
+ # add back initials as singletons and singletons as terminals
529
+ return SetPartition([addback(S, singletons, support[::-1])
530
+ for S in rho] + [[a] for a in initials])
531
+ support = sorted(a for S in self for a in S)
532
+ return SetPartition([[support[-support.index(a) - 1] for a in S]
533
+ for S in pre_conjugate(self)])
534
+
535
+
536
+ class SetPartition(AbstractSetPartition,
537
+ metaclass=InheritComparisonClasscallMetaclass):
538
+ r"""
539
+ A partition of a set.
540
+
541
+ A set partition `p` of a set `S` is a partition of `S` into subsets
542
+ called parts and represented as a set of sets. By extension, a set
543
+ partition of a nonnegative integer `n` is the set partition of the
544
+ integers from 1 to `n`. The number of set partitions of `n` is called
545
+ the `n`-th Bell number.
546
+
547
+ There is a natural integer partition associated with a set partition,
548
+ namely the nonincreasing sequence of sizes of all its parts.
549
+
550
+ There is a classical lattice associated with all set partitions of
551
+ `n`. The infimum of two set partitions is the set partition obtained
552
+ by intersecting all the parts of both set partitions. The supremum
553
+ is obtained by transitive closure of the relation `i` related to `j`
554
+ if and only if they are in the same part in at least one of the set
555
+ partitions.
556
+
557
+ We will use terminology from partitions, in particular the *length* of
558
+ a set partition `A = \{A_1, \ldots, A_k\}` is the number of parts of `A`
559
+ and is denoted by `|A| := k`. The *size* of `A` is the cardinality of `S`.
560
+ We will also sometimes use the notation `[n] := \{1, 2, \ldots, n\}`.
561
+
562
+ EXAMPLES:
563
+
564
+ There are 5 set partitions of the set `\{1,2,3\}`::
565
+
566
+ sage: SetPartitions(3).cardinality() # needs sage.libs.flint
567
+ 5
568
+
569
+ Here is the list of them::
570
+
571
+ sage: SetPartitions(3).list() # needs sage.graphs
572
+ [{{1, 2, 3}}, {{1, 2}, {3}}, {{1, 3}, {2}}, {{1}, {2, 3}}, {{1}, {2}, {3}}]
573
+
574
+ There are 6 set partitions of `\{1,2,3,4\}` whose underlying partition is
575
+ `[2, 1, 1]`::
576
+
577
+ sage: SetPartitions(4, [2,1,1]).list() # needs sage.graphs sage.rings.finite_rings
578
+ [{{1}, {2, 4}, {3}},
579
+ {{1}, {2}, {3, 4}},
580
+ {{1, 4}, {2}, {3}},
581
+ {{1, 3}, {2}, {4}},
582
+ {{1, 2}, {3}, {4}},
583
+ {{1}, {2, 3}, {4}}]
584
+
585
+ Since :issue:`14140`, we can create a set partition directly by
586
+ :class:`SetPartition`, which creates the base set by taking the
587
+ union of the parts passed in::
588
+
589
+ sage: s = SetPartition([[1,3],[2,4]]); s
590
+ {{1, 3}, {2, 4}}
591
+ sage: s.parent()
592
+ Set partitions
593
+ """
594
+ @staticmethod
595
+ def __classcall_private__(cls, parts, check=True):
596
+ """
597
+ Create a set partition from ``parts`` with the appropriate parent.
598
+
599
+ EXAMPLES::
600
+
601
+ sage: s = SetPartition([[1,3],[2,4]]); s
602
+ {{1, 3}, {2, 4}}
603
+ sage: s.parent()
604
+ Set partitions
605
+ """
606
+ P = SetPartitions()
607
+ return P.element_class(P, parts, check=check)
608
+
609
+ def __init__(self, parent, s, check=True):
610
+ """
611
+ Initialize ``self``.
612
+
613
+ Internally, a set partition is stored as iterable of blocks,
614
+ sorted by minimal element.
615
+
616
+ EXAMPLES::
617
+
618
+ sage: S = SetPartitions(4)
619
+ sage: s = S([[1,3],[2,4]])
620
+ sage: TestSuite(s).run()
621
+ sage: SetPartition([])
622
+ {}
623
+ """
624
+ self._latex_options = {}
625
+ try:
626
+ s = sorted(map(frozenset, s), key=min)
627
+ except TypeError:
628
+ s = sorted(map(frozenset, s), key=lambda b: min(str(b)))
629
+ ClonableArray.__init__(self, parent, s, check=check)
630
+
631
+ def check(self):
632
+ """
633
+ Check that we are a valid set partition.
634
+
635
+ EXAMPLES::
636
+
637
+ sage: S = SetPartitions(4)
638
+ sage: s = S([[1, 3], [2, 4]])
639
+ sage: s.check()
640
+
641
+ TESTS::
642
+
643
+ sage: s = S([[1, 2, 3]], check=False)
644
+ sage: s.check()
645
+ Traceback (most recent call last):
646
+ ...
647
+ ValueError: {{1, 2, 3}} is not an element of Set partitions of {1, 2, 3, 4}
648
+
649
+ sage: s = S([1, 2, 3])
650
+ Traceback (most recent call last):
651
+ ...
652
+ TypeError: 'sage.rings.integer.Integer' object is not iterable
653
+ """
654
+ if self not in self.parent():
655
+ raise ValueError(f"{self} is not an element of {self.parent()}")
656
+
657
+ def set_latex_options(self, **kwargs):
658
+ r"""
659
+ Set the latex options for use in the ``_latex_`` function.
660
+
661
+ - ``tikz_scale`` -- (default: 1) scale for use with tikz package
662
+
663
+ - ``plot`` -- (default: ``None``) ``None`` returns the set notation,
664
+ ``linear`` returns a linear plot, ``cyclic`` returns a cyclic
665
+ plot
666
+
667
+ - ``color`` -- (default: ``'black'``) the arc colors
668
+
669
+ - ``fill`` -- boolean (default: ``False``); if ``True`` then fills
670
+ ``color``, else you can pass in a color to alter the fill color -
671
+ *only works with cyclic plot*
672
+
673
+ - ``show_labels`` -- boolean (default: ``True``); if ``True`` shows
674
+ labels (*only works with plots*)
675
+
676
+ - ``radius`` -- (default: ``'1cm'``) radius of circle for cyclic
677
+ plot - *only works with cyclic plot*
678
+
679
+ - ``angle`` -- (default: 0) angle for linear plot
680
+
681
+ EXAMPLES::
682
+
683
+ sage: SP = SetPartition([[1,6], [3,5,4]])
684
+ sage: SP.set_latex_options(tikz_scale=2,plot='linear',fill=True,color='blue',angle=45)
685
+ sage: SP.set_latex_options(plot='cyclic')
686
+ sage: SP.latex_options()
687
+ {'angle': 45,
688
+ 'color': 'blue',
689
+ 'fill': True,
690
+ 'plot': 'cyclic',
691
+ 'radius': '1cm',
692
+ 'show_labels': True,
693
+ 'tikz_scale': 2}
694
+ """
695
+ valid_args = ['tikz_scale', 'plot', 'color', 'fill', 'show_labels',
696
+ 'radius', 'angle']
697
+
698
+ for key in kwargs:
699
+ if key not in valid_args:
700
+ raise ValueError(f"unknown keyword argument: {key}")
701
+ if key == 'plot':
702
+ if not (kwargs['plot'] == 'cyclic'
703
+ or kwargs['plot'] == 'linear'
704
+ or kwargs['plot'] is None):
705
+ raise ValueError("plot must be None, 'cyclic', or 'linear'")
706
+
707
+ self._latex_options.update(kwargs)
708
+
709
+ def latex_options(self):
710
+ r"""
711
+ Return the latex options for use in the ``_latex_`` function as a
712
+ dictionary. The default values are set using the global options.
713
+
714
+ Options can be found in :meth:`set_latex_options`
715
+
716
+ EXAMPLES::
717
+
718
+ sage: SP = SetPartition([[1,6], [3,5,4]]); SP.latex_options()
719
+ {'angle': 0,
720
+ 'color': 'black',
721
+ 'fill': False,
722
+ 'plot': None,
723
+ 'radius': '1cm',
724
+ 'show_labels': True,
725
+ 'tikz_scale': 1}
726
+ """
727
+ opts = self._latex_options.copy()
728
+ if "tikz_scale" not in opts:
729
+ opts["tikz_scale"] = 1
730
+ if "plot" not in opts:
731
+ opts["plot"] = None
732
+ if "color" not in opts:
733
+ opts['color'] = 'black'
734
+ if "fill" not in opts:
735
+ opts["fill"] = False
736
+ if "show_labels" not in opts:
737
+ opts['show_labels'] = True
738
+ if "radius" not in opts:
739
+ opts['radius'] = "1cm"
740
+ if "angle" not in opts:
741
+ opts['angle'] = 0
742
+ return opts
743
+
744
+ def _latex_(self):
745
+ r"""
746
+ Return a `\LaTeX` string representation of ``self``.
747
+
748
+ EXAMPLES::
749
+
750
+ sage: x = SetPartition([[1,2], [3,5,4]])
751
+ sage: latex(x)
752
+ \{\{1, 2\}, \{3, 4, 5\}\}
753
+
754
+ sage: x.set_latex_options(plot='linear', angle=25, color='red')
755
+ sage: latex(x)
756
+ \begin{tikzpicture}[scale=1]
757
+ \node[below=.05cm] at (0,0) {$1$};
758
+ \node[draw,circle, inner sep=0pt, minimum width=4pt, fill=black] (0) at (0,0) {};
759
+ \node[below=.05cm] at (1,0) {$2$};
760
+ \node[draw,circle, inner sep=0pt, minimum width=4pt, fill=black] (1) at (1,0) {};
761
+ \node[below=.05cm] at (2,0) {$3$};
762
+ \node[draw,circle, inner sep=0pt, minimum width=4pt, fill=black] (2) at (2,0) {};
763
+ \node[below=.05cm] at (3,0) {$4$};
764
+ \node[draw,circle, inner sep=0pt, minimum width=4pt, fill=black] (3) at (3,0) {};
765
+ \node[below=.05cm] at (4,0) {$5$};
766
+ \node[draw,circle, inner sep=0pt, minimum width=4pt, fill=black] (4) at (4,0) {};
767
+ \draw[color=red] (1) to [out=115,in=65] (0);
768
+ \draw[color=red] (3) to [out=115,in=65] (2);
769
+ \draw[color=red] (4) to [out=115,in=65] (3);
770
+ \end{tikzpicture}
771
+
772
+ sage: p = SetPartition([['a','c'],['b','d'],['e']])
773
+ sage: p.set_latex_options(plot='cyclic', color='blue', fill=True, tikz_scale=2)
774
+ sage: latex(p)
775
+ \begin{tikzpicture}[scale=2]
776
+ \draw (0,0) circle [radius=1cm];
777
+ \node[label=90:a] (0) at (90:1cm) {};
778
+ \node[label=18:b] (1) at (18:1cm) {};
779
+ \node[label=-54:c] (2) at (-54:1cm) {};
780
+ \node[label=-126:d] (3) at (-126:1cm) {};
781
+ \node[label=-198:e] (4) at (-198:1cm) {};
782
+ \draw[-,thick,color=blue,fill=blue,fill opacity=0.1] ...
783
+ \draw[-,thick,color=blue,fill=blue,fill opacity=0.1] ...
784
+ \draw[-,thick,color=blue,fill=blue,fill opacity=0.1] ...
785
+ \fill[color=black] (0) circle (1.5pt);
786
+ \fill[color=black] (1) circle (1.5pt);
787
+ \fill[color=black] (2) circle (1.5pt);
788
+ \fill[color=black] (3) circle (1.5pt);
789
+ \fill[color=black] (4) circle (1.5pt);
790
+ \end{tikzpicture}
791
+ """
792
+ latex_options = self.latex_options()
793
+ if latex_options["plot"] is None:
794
+ return repr(self).replace("{", r"\{").replace("}", r"\}")
795
+
796
+ from sage.misc.latex import latex
797
+ latex.add_package_to_preamble_if_available("tikz")
798
+ res = "\\begin{{tikzpicture}}[scale={}]\n".format(latex_options['tikz_scale'])
799
+
800
+ cardinality = self.base_set_cardinality()
801
+ from sage.rings.integer_ring import ZZ
802
+ if all(x in ZZ for x in self.base_set()):
803
+ sort_key = ZZ
804
+ else:
805
+ sort_key = str
806
+ base_set = sorted(self.base_set(), key=sort_key)
807
+ color = latex_options['color']
808
+
809
+ # If we want cyclic plots
810
+ if latex_options['plot'] == 'cyclic':
811
+ degrees = 360 // cardinality
812
+ radius = latex_options['radius']
813
+
814
+ res += "\\draw (0,0) circle [radius={}];\n".format(radius)
815
+
816
+ # Add nodes
817
+ for k, i in enumerate(base_set):
818
+ location = (cardinality - k) * degrees - 270
819
+ if latex_options['show_labels']:
820
+ res += "\\node[label={}:{}]".format(location, i)
821
+ else:
822
+ res += "\\node"
823
+ res += " ({}) at ({}:{}) {{}};\n".format(k, location, radius)
824
+
825
+ # Setup partitions
826
+ for partition in sorted(self, key=str):
827
+ res += "\\draw[-,thick,color=" + color
828
+ if latex_options['fill'] is not False:
829
+ if isinstance(latex_options['fill'], str):
830
+ res += ",fill=" + latex_options['fill']
831
+ else:
832
+ res += ",fill={},fill opacity=0.1".format(color)
833
+ res += "] "
834
+ res += " -- ".join("({}.center)".format(base_set.index(j))
835
+ for j in sorted(partition, key=sort_key))
836
+ res += " -- cycle;\n"
837
+
838
+ # Draw the circles on top
839
+ for k in range(len(base_set)):
840
+ res += "\\fill[color=black] ({}) circle (1.5pt);\n".format(k)
841
+
842
+ # If we want line plots
843
+ elif latex_options['plot'] == 'linear':
844
+ angle = latex_options['angle']
845
+ # setup line
846
+ for k, i in enumerate(base_set):
847
+ if latex_options['show_labels']:
848
+ res += "\\node[below=.05cm] at ({},0) {{${}$}};\n".format(k, i)
849
+ res += "\\node[draw,circle, inner sep=0pt, minimum width=4pt, fill=black] "
850
+ res += "({k}) at ({k},0) {{}};\n".format(k=k)
851
+
852
+ # setup arcs
853
+ for partition in sorted(self, key=str):
854
+ p = sorted(partition, key=sort_key)
855
+ if len(p) <= 1:
856
+ continue
857
+ for k in range(1, len(p)):
858
+ res += "\\draw[color={}] ({})".format(color, base_set.index(p[k]))
859
+ res += " to [out={},in={}] ".format(90 + angle, 90 - angle)
860
+ res += "({});\n".format(base_set.index(p[k - 1]))
861
+ else:
862
+ raise ValueError("plot must be None, 'cyclic', or 'linear'")
863
+
864
+ res += "\\end{tikzpicture}"
865
+ return res
866
+
867
+ cardinality = ClonableArray.__len__
868
+
869
+ size = AbstractSetPartition.base_set_cardinality
870
+
871
+ def pipe(self, other):
872
+ r"""
873
+ Return the pipe of the set partitions ``self`` and ``other``.
874
+
875
+ The pipe of two set partitions is defined as follows:
876
+
877
+ For any integer `k` and any subset `I` of `\ZZ`, let `I + k`
878
+ denote the subset of `\ZZ` obtained by adding `k` to every
879
+ element of `k`.
880
+
881
+ If `B` and `C` are set partitions of `[n]` and `[m]`,
882
+ respectively, then the pipe of `B` and `C` is defined as the
883
+ set partition
884
+
885
+ .. MATH::
886
+
887
+ \{ B_1, B_2, \ldots, B_b,
888
+ C_1 + n, C_2 + n, \ldots, C_c + n \}
889
+
890
+ of `[n+m]`, where `B = \{ B_1, B_2, \ldots, B_b \}` and
891
+ `C = \{ C_1, C_2, \ldots, C_c \}`. This pipe is denoted by
892
+ `B | C`.
893
+
894
+ EXAMPLES::
895
+
896
+ sage: SetPartition([[1,3],[2,4]]).pipe(SetPartition([[1,3],[2]]))
897
+ {{1, 3}, {2, 4}, {5, 7}, {6}}
898
+ sage: SetPartition([]).pipe(SetPartition([[1,2],[3,5],[4]]))
899
+ {{1, 2}, {3, 5}, {4}}
900
+ sage: SetPartition([[1,2],[3,5],[4]]).pipe(SetPartition([]))
901
+ {{1, 2}, {3, 5}, {4}}
902
+ sage: SetPartition([[1,2],[3]]).pipe(SetPartition([[1]]))
903
+ {{1, 2}, {3}, {4}}
904
+ """
905
+ # Note: GIGO if self and other are not standard.
906
+ parts = list(self)
907
+ n = self.base_set_cardinality()
908
+ for newpart in other:
909
+ raised_newpart = Set(i + n for i in newpart)
910
+ parts.append(raised_newpart)
911
+ return SetPartition(parts)
912
+
913
+ @combinatorial_map(name='shape')
914
+ def shape(self):
915
+ r"""
916
+ Return the integer partition whose parts are the sizes of the sets
917
+ in ``self``.
918
+
919
+ EXAMPLES::
920
+
921
+ sage: S = SetPartitions(5)
922
+ sage: x = S([[1,2], [3,5,4]])
923
+ sage: x.shape()
924
+ [3, 2]
925
+ sage: y = S([[2], [3,1], [5,4]])
926
+ sage: y.shape()
927
+ [2, 2, 1]
928
+ """
929
+ return Partition(sorted(map(len, self), reverse=True))
930
+
931
+ # we define aliases for shape()
932
+ shape_partition = shape
933
+ to_partition = shape
934
+
935
+ @combinatorial_map(name='to permutation')
936
+ def to_permutation(self):
937
+ r"""
938
+ Convert a set partition of `\{1,...,n\}` to a permutation by considering
939
+ the blocks of the partition as cycles.
940
+
941
+ The cycles are such that the number of excedences is maximised, that is,
942
+ each cycle is of the form `(a_1,a_2, ...,a_k)` with `a_1<a_2<...<a_k`.
943
+
944
+ EXAMPLES::
945
+
946
+ sage: s = SetPartition([[1,3],[2,4]])
947
+ sage: s.to_permutation()
948
+ [3, 4, 1, 2]
949
+ """
950
+ return Permutation(tuple(map(tuple, self.standard_form())))
951
+
952
+ def to_restricted_growth_word(self, bijection='blocks'):
953
+ r"""
954
+ Convert a set partition of `\{1,...,n\}` to a word of length `n`
955
+ with letters in the nonnegative integers such that each
956
+ letter is at most 1 larger than all the letters before.
957
+
958
+ INPUT:
959
+
960
+ - ``bijection`` -- (default: ``blocks``) defines the map from
961
+ set partitions to restricted growth functions. These are
962
+ currently:
963
+
964
+ - ``blocks``: :meth:`to_restricted_growth_word_blocks`.
965
+
966
+ - ``intertwining``: :meth:`to_restricted_growth_word_intertwining`.
967
+
968
+ OUTPUT: a restricted growth word
969
+
970
+ .. SEEALSO::
971
+
972
+ :meth:`SetPartitions.from_restricted_growth_word`
973
+
974
+ EXAMPLES::
975
+
976
+ sage: P = SetPartition([[1,4],[2,8],[3,5,6,9],[7]])
977
+ sage: P.to_restricted_growth_word()
978
+ [0, 1, 2, 0, 2, 2, 3, 1, 2]
979
+
980
+ sage: P.to_restricted_growth_word("intertwining")
981
+ [0, 1, 2, 2, 1, 0, 3, 3, 2]
982
+
983
+ sage: P = SetPartition([[1,2,4,7],[3,9],[5,6,10,11,13],[8],[12]])
984
+ sage: P.to_restricted_growth_word()
985
+ [0, 0, 1, 0, 2, 2, 0, 3, 1, 2, 2, 4, 2]
986
+
987
+ sage: P.to_restricted_growth_word("intertwining")
988
+ [0, 0, 1, 1, 2, 0, 1, 3, 3, 3, 0, 4, 1]
989
+
990
+ TESTS::
991
+
992
+ sage: P = SetPartition([])
993
+ sage: P.to_restricted_growth_word()
994
+ []
995
+ sage: P.to_restricted_growth_word("intertwining")
996
+ []
997
+ sage: S = SetPartitions(5, 2)
998
+ sage: all(S.from_restricted_growth_word(P.to_restricted_growth_word()) == P for P in S)
999
+ True
1000
+
1001
+ sage: S = SetPartitions(5, 2)
1002
+ sage: all(S.from_restricted_growth_word(P.to_restricted_growth_word("intertwining"), "intertwining") == P for P in S)
1003
+ True
1004
+ """
1005
+ if bijection == "blocks":
1006
+ return self.to_restricted_growth_word_blocks()
1007
+ if bijection == "intertwining":
1008
+ return self.to_restricted_growth_word_intertwining()
1009
+ raise ValueError("the given bijection is not valid")
1010
+
1011
+ def to_restricted_growth_word_blocks(self):
1012
+ r"""
1013
+ Convert a set partition of `\{1,...,n\}` to a word of length `n`
1014
+ with letters in the nonnegative integers such that each
1015
+ letter is at most 1 larger than all the letters before.
1016
+
1017
+ The word is obtained by sorting the blocks by their minimal
1018
+ element and setting the letters at the positions of the
1019
+ elements in the `i`-th block to `i`.
1020
+
1021
+ OUTPUT: a restricted growth word
1022
+
1023
+ .. SEEALSO::
1024
+
1025
+ :meth:`to_restricted_growth_word`
1026
+ :meth:`SetPartitions.from_restricted_growth_word`
1027
+
1028
+ EXAMPLES::
1029
+
1030
+ sage: P = SetPartition([[1,4],[2,8],[3,5,6,9],[7]])
1031
+ sage: P.to_restricted_growth_word_blocks()
1032
+ [0, 1, 2, 0, 2, 2, 3, 1, 2]
1033
+ """
1034
+ w = [0] * self.size()
1035
+ # we can assume that the blocks are sorted by minimal element
1036
+ for i, B in enumerate(self):
1037
+ for j in B:
1038
+ w[j - 1] = i
1039
+ return w
1040
+
1041
+ def to_restricted_growth_word_intertwining(self):
1042
+ r"""
1043
+ Convert a set partition of `\{1,...,n\}` to a word of length `n`
1044
+ with letters in the nonnegative integers such that each
1045
+ letter is at most 1 larger than all the letters before.
1046
+
1047
+ The `i`-th letter of the word is the numbers of crossings of
1048
+ the arc (or half-arc) in the extended arc diagram ending at
1049
+ `i`, with arcs (or half-arcs) beginning at a smaller element
1050
+ and ending at a larger element.
1051
+
1052
+ OUTPUT: a restricted growth word
1053
+
1054
+ .. SEEALSO::
1055
+
1056
+ :meth:`to_restricted_growth_word`
1057
+ :meth:`SetPartitions.from_restricted_growth_word`
1058
+
1059
+ EXAMPLES::
1060
+
1061
+ sage: P = SetPartition([[1,4],[2,8],[3,5,6,9],[7]])
1062
+ sage: P.to_restricted_growth_word_intertwining()
1063
+ [0, 1, 2, 2, 1, 0, 3, 3, 2]
1064
+ """
1065
+ A = sorted(self.arcs())
1066
+ O = (min(B) for B in self) # openers
1067
+ C = [max(B) for B in self] # closers
1068
+ I = [0] * self.size()
1069
+ for i in O:
1070
+ I[i - 1] = sum(1 for k, l in A if k < i < l) + sum(1 for k in C if k < i)
1071
+ for i, j in A:
1072
+ I[j - 1] = sum(1 for k, l in A if i < k < j < l) + sum(1 for k in C if i < k < j)
1073
+ return I
1074
+
1075
+ def openers(self):
1076
+ """
1077
+ Return the minimal elements of the blocks.
1078
+
1079
+ EXAMPLES::
1080
+
1081
+ sage: P = SetPartition([[1,2,4,7],[3,9],[5,6,10,11,13],[8],[12]])
1082
+ sage: P.openers()
1083
+ [1, 3, 5, 8, 12]
1084
+ """
1085
+ return sorted([min(B) for B in self])
1086
+
1087
+ def closers(self):
1088
+ """
1089
+ Return the maximal elements of the blocks.
1090
+
1091
+ EXAMPLES::
1092
+
1093
+ sage: P = SetPartition([[1,2,4,7],[3,9],[5,6,10,11,13],[8],[12]])
1094
+ sage: P.closers()
1095
+ [7, 8, 9, 12, 13]
1096
+ """
1097
+ return sorted([max(B) for B in self])
1098
+
1099
+ def to_rook_placement(self, bijection='arcs'):
1100
+ r"""
1101
+ Return a set of pairs defining a placement of non-attacking rooks
1102
+ on a triangular board.
1103
+
1104
+ The cells of the board corresponding to a set partition of
1105
+ `\{1,...,n\}` are the pairs `(i,j)` with `0 < i < j < n+1`.
1106
+
1107
+ INPUT:
1108
+
1109
+ - ``bijection`` -- (default: ``arcs``) defines the bijection
1110
+ from set partitions to rook placements. These are
1111
+ currently:
1112
+
1113
+ - ``arcs``: :meth:`arcs`
1114
+ - ``gamma``: :meth:`to_rook_placement_gamma`
1115
+ - ``rho``: :meth:`to_rook_placement_rho`
1116
+ - ``psi``: :meth:`to_rook_placement_psi`
1117
+
1118
+ .. SEEALSO::
1119
+
1120
+ :meth:`SetPartitions.from_rook_placement`
1121
+
1122
+ EXAMPLES::
1123
+
1124
+ sage: P = SetPartition([[1,2,4,7],[3,9],[5,6,10,11,13],[8],[12]])
1125
+ sage: P.to_rook_placement()
1126
+ [(1, 2), (2, 4), (4, 7), (3, 9), (5, 6), (6, 10), (10, 11), (11, 13)]
1127
+ sage: P.to_rook_placement("gamma")
1128
+ [(1, 4), (3, 5), (4, 6), (5, 8), (7, 11), (8, 9), (10, 12), (12, 13)]
1129
+ sage: P.to_rook_placement("rho")
1130
+ [(1, 2), (2, 6), (3, 4), (4, 10), (5, 9), (6, 7), (10, 11), (11, 13)]
1131
+ sage: P.to_rook_placement("psi")
1132
+ [(1, 2), (2, 6), (3, 4), (5, 9), (6, 7), (7, 10), (9, 11), (11, 13)]
1133
+ """
1134
+ if bijection == "arcs":
1135
+ return self.arcs()
1136
+ if bijection == "gamma":
1137
+ return self.to_rook_placement_gamma()
1138
+ if bijection == "rho":
1139
+ return self.to_rook_placement_rho()
1140
+ if bijection == "psi":
1141
+ return self.to_rook_placement_psi()
1142
+ raise ValueError("the given map is not valid")
1143
+
1144
+ def to_rook_placement_gamma(self):
1145
+ """
1146
+ Return the rook diagram obtained by placing rooks according to
1147
+ Wachs and White's bijection gamma.
1148
+
1149
+ Note that our index convention differs from the convention in
1150
+ [WW1991]_: regarding the rook board as a lower-right
1151
+ triangular grid, we refer with `(i,j)` to the cell in the
1152
+ `i`-th column from the right and the `j`-th row from the top.
1153
+
1154
+ The algorithm proceeds as follows: non-attacking rooks are
1155
+ placed beginning at the left column. If `n+1-i` is an
1156
+ opener, column `i` remains empty. Otherwise, we place a rook
1157
+ into column `i`, such that the number of cells below the
1158
+ rook, which are not yet attacked by another rook, equals the
1159
+ index of the block to which `n+1-i` belongs.
1160
+
1161
+ OUTPUT: list of coordinates
1162
+
1163
+ .. SEEALSO::
1164
+
1165
+ - :meth:`to_rook_placement`
1166
+ - :meth:`SetPartitions.from_rook_placement`
1167
+ - :meth:`SetPartitions.from_rook_placement_gamma`
1168
+
1169
+ EXAMPLES::
1170
+
1171
+ sage: P = SetPartition([[1,4],[2,8],[3,5,6,9],[7]])
1172
+ sage: P.to_rook_placement_gamma()
1173
+ [(1, 3), (2, 7), (4, 5), (5, 6), (6, 9)]
1174
+
1175
+ Figure 5 in [WW1991]_::
1176
+
1177
+ sage: P = SetPartition([[1,2,4,7],[3,9],[5,6,10,11,13],[8],[12]])
1178
+ sage: r = P.to_rook_placement_gamma(); r
1179
+ [(1, 4), (3, 5), (4, 6), (5, 8), (7, 11), (8, 9), (10, 12), (12, 13)]
1180
+
1181
+ TESTS::
1182
+
1183
+ sage: P = SetPartition([])
1184
+ sage: P.to_rook_placement_gamma()
1185
+ []
1186
+ sage: S = SetPartitions(5, 2)
1187
+ sage: all(S.from_rook_placement(P.to_rook_placement("gamma"), "gamma") == P for P in S)
1188
+ True
1189
+ """
1190
+ n = self.size()
1191
+ if n == 0:
1192
+ return []
1193
+ w = self.to_restricted_growth_word_blocks()
1194
+ # the set of openers - leftmost occurrences of a letter in w
1195
+ EC = sorted([w.index(i) for i in range(max(w) + 1)])
1196
+ rooks = [] # pairs (row i, column j)
1197
+ R = [] # attacked rows
1198
+ for c in range(n): # columns from left to right
1199
+ if c not in EC:
1200
+ r = 0
1201
+ w_c = w[c]
1202
+ while w_c > 0 or r in R:
1203
+ if r not in R:
1204
+ w_c -= 1
1205
+ r += 1
1206
+ rooks.append((n - c, n - r))
1207
+ R.append(r)
1208
+ return sorted(rooks)
1209
+
1210
+ def to_rook_placement_rho(self):
1211
+ """
1212
+ Return the rook diagram obtained by placing rooks according to
1213
+ Wachs and White's bijection rho.
1214
+
1215
+ Note that our index convention differs from the convention in
1216
+ [WW1991]_: regarding the rook board as a lower-right
1217
+ triangular grid, we refer with `(i,j)` to the cell in the
1218
+ `i`-th column from the right and the `j`-th row from the top.
1219
+
1220
+ The algorithm proceeds as follows: non-attacking rooks are
1221
+ placed beginning at the top row. The columns corresponding
1222
+ to the closers of the set partition remain empty. Let `rs_j`
1223
+ be the number of closers which are larger than `j` and
1224
+ whose block is before the block of `j`.
1225
+
1226
+ We then place a rook into row `j`, such that the number of
1227
+ cells to the left of the rook, which are not yet attacked by
1228
+ another rook and are not in a column corresponding to a
1229
+ closer, equals `rs_j`, unless there are not enough cells in
1230
+ this row available, in which case the row remains empty.
1231
+
1232
+ One can show that the precisely those rows which correspond
1233
+ to openers of the set partition remain empty.
1234
+
1235
+ OUTPUT: list of coordinates
1236
+
1237
+ .. SEEALSO::
1238
+
1239
+ - :meth:`to_rook_placement`
1240
+ - :meth:`SetPartitions.from_rook_placement`
1241
+ - :meth:`SetPartitions.from_rook_placement_rho`
1242
+
1243
+ EXAMPLES::
1244
+
1245
+ sage: P = SetPartition([[1,4],[2,8],[3,5,6,9],[7]])
1246
+ sage: P.to_rook_placement_rho()
1247
+ [(1, 5), (2, 6), (3, 4), (5, 9), (6, 8)]
1248
+
1249
+ Figure 6 in [WW1991]_::
1250
+
1251
+ sage: P = SetPartition([[1,2,4,7],[3,9],[5,6,10,11,13],[8],[12]])
1252
+ sage: r = P.to_rook_placement_rho(); r
1253
+ [(1, 2), (2, 6), (3, 4), (4, 10), (5, 9), (6, 7), (10, 11), (11, 13)]
1254
+
1255
+ sage: sorted(P.closers() + [i for i, _ in r]) == list(range(1,14))
1256
+ True
1257
+ sage: sorted(P.openers() + [j for _, j in r]) == list(range(1,14))
1258
+ True
1259
+
1260
+ TESTS::
1261
+
1262
+ sage: P = SetPartition([])
1263
+ sage: P.to_rook_placement_rho()
1264
+ []
1265
+ sage: S = SetPartitions(5, 2)
1266
+ sage: all(S.from_rook_placement(P.to_rook_placement("rho"), "rho") == P for P in S)
1267
+ True
1268
+ """
1269
+ n = self.size()
1270
+ if n == 0:
1271
+ return []
1272
+ w = self.to_restricted_growth_word_blocks()
1273
+ w_rev = w[::-1]
1274
+ # the set of closers - rightmost occurrences of a letter in w
1275
+ R = sorted([n - w_rev.index(i) - 1 for i in range(max(w) + 1)])
1276
+ # the number of closers which are larger than i and whose
1277
+ # block is before the block of i
1278
+ rs = [sum(1 for j in R if j > i and w[j] < w[i]) for i in range(n)]
1279
+ EC = [n - j for j in R] # empty columns
1280
+ rooks = [] # pairs (row i, column j)
1281
+ for i in range(1, n):
1282
+ U = [j for j in range(n + 1 - i, n + 1) if j not in EC]
1283
+ if rs[i] < len(U):
1284
+ j = U[rs[i]]
1285
+ rooks.append((n + 1 - j, i + 1))
1286
+ EC.append(j)
1287
+ return sorted(rooks)
1288
+
1289
+ def to_rook_placement_psi(self):
1290
+ r"""
1291
+ Return the rook diagram obtained by placing rooks according to
1292
+ Yip's bijection psi.
1293
+
1294
+ OUTPUT: list of coordinates
1295
+
1296
+ .. SEEALSO::
1297
+
1298
+ - :meth:`to_rook_placement`
1299
+ - :meth:`SetPartitions.from_rook_placement`
1300
+ - :meth:`SetPartitions.from_rook_placement_psi`
1301
+
1302
+ EXAMPLES:
1303
+
1304
+ Example 36 (arXiv version: Example 4.5) in [Yip2018]_::
1305
+
1306
+ sage: P = SetPartition([[1, 5], [2], [3, 8, 9], [4], [6, 7]])
1307
+ sage: P.to_rook_placement_psi()
1308
+ [(1, 7), (3, 8), (4, 5), (7, 9)]
1309
+
1310
+ Note that the columns corresponding to the minimal elements
1311
+ of the blocks remain empty.
1312
+
1313
+ TESTS::
1314
+
1315
+ sage: P = SetPartition([])
1316
+ sage: P.to_rook_placement_psi()
1317
+ []
1318
+ sage: S = SetPartitions(5,2)
1319
+ sage: all(S.from_rook_placement(P.to_rook_placement("psi"), "psi") == P for P in S)
1320
+ True
1321
+ """
1322
+ # Yip draws the diagram as an upper triangular matrix, thus
1323
+ # we refer to the cell in row i and column j with (i, j)
1324
+ n = self.size()
1325
+ degrees = []
1326
+ P = [sorted(e) for e in self]
1327
+ for j in range(n, 0, -1):
1328
+ # find the block number into which c was placed by first
1329
+ # removing j and then sorting the blocks
1330
+ B = next(B for B in P if B[-1] == j)
1331
+ if len(B) == 1:
1332
+ P.remove(B)
1333
+ else:
1334
+ del B[-1]
1335
+ P = sorted(P, key=lambda B: (-len(B), min(B)))
1336
+ b = P.index(B)
1337
+ i = j - b - 1
1338
+ degrees.append((j, i))
1339
+ # reconstruct rooks from degree sequence
1340
+ rooks = []
1341
+ attacked_rows = []
1342
+ for j, d in reversed(degrees):
1343
+ i = 1
1344
+ while d > i + sum(1 for r in attacked_rows if r > i):
1345
+ i += 1
1346
+ attacked_rows.append(i)
1347
+ rooks.append((i, j))
1348
+ return sorted(rooks)
1349
+
1350
+ def apply_permutation(self, p):
1351
+ r"""
1352
+ Apply ``p`` to the underlying set of ``self``.
1353
+
1354
+ INPUT:
1355
+
1356
+ - ``p`` -- a permutation
1357
+
1358
+ EXAMPLES::
1359
+
1360
+ sage: x = SetPartition([[1,2], [3,5,4]])
1361
+ sage: p = Permutation([2,1,4,5,3])
1362
+ sage: x.apply_permutation(p)
1363
+ {{1, 2}, {3, 4, 5}}
1364
+ sage: q = Permutation([3,2,1,5,4])
1365
+ sage: x.apply_permutation(q)
1366
+ {{1, 4, 5}, {2, 3}}
1367
+
1368
+ sage: m = PerfectMatching([(1,4),(2,6),(3,5)])
1369
+ sage: m.apply_permutation(Permutation([4,1,5,6,3,2]))
1370
+ [(1, 2), (3, 5), (4, 6)]
1371
+ """
1372
+ return self.__class__(self.parent(), [Set(map(p, B)) for B in self])
1373
+
1374
+ def crossings_iterator(self):
1375
+ r"""
1376
+ Return the crossing arcs of a set partition on a totally ordered set.
1377
+
1378
+ OUTPUT:
1379
+
1380
+ We place the elements of the ground set in order on a
1381
+ line and draw the set partition by linking consecutive
1382
+ elements of each block in the upper half-plane. This
1383
+ function returns an iterator over the pairs of crossing
1384
+ lines (as a line correspond to a pair, the iterator
1385
+ produces pairs of pairs).
1386
+
1387
+ EXAMPLES::
1388
+
1389
+ sage: p = SetPartition([[1,4],[2,5,7],[3,6]])
1390
+ sage: next(p.crossings_iterator())
1391
+ ((1, 4), (2, 5))
1392
+
1393
+ TESTS::
1394
+
1395
+ sage: p = SetPartition([]); p.crossings()
1396
+ []
1397
+ """
1398
+ # each arc is sorted, but the set of arcs might not be
1399
+ arcs = sorted(self.arcs(), key=min)
1400
+ while arcs:
1401
+ i1, j1 = arcs.pop(0)
1402
+ for i2, j2 in arcs:
1403
+ # we know that i1 < i2 and i1 < j1 and i2 < j2
1404
+ if i2 < j1 < j2:
1405
+ yield ((i1, j1), (i2, j2))
1406
+
1407
+ def crossings(self):
1408
+ r"""
1409
+ Return the crossing arcs of a set partition on a totally ordered set.
1410
+
1411
+ OUTPUT:
1412
+
1413
+ We place the elements of the ground set in order on a
1414
+ line and draw the set partition by linking consecutive
1415
+ elements of each block in the upper half-plane. This
1416
+ function returns a list of the pairs of crossing lines
1417
+ (as a line correspond to a pair, it returns a list of
1418
+ pairs of pairs).
1419
+
1420
+ EXAMPLES::
1421
+
1422
+ sage: p = SetPartition([[1,4],[2,5,7],[3,6]])
1423
+ sage: p.crossings()
1424
+ [((1, 4), (2, 5)), ((1, 4), (3, 6)), ((2, 5), (3, 6)), ((3, 6), (5, 7))]
1425
+
1426
+ TESTS::
1427
+
1428
+ sage: p = SetPartition([]); p.crossings()
1429
+ []
1430
+ """
1431
+ return list(self.crossings_iterator())
1432
+
1433
+ def number_of_crossings(self):
1434
+ r"""
1435
+ Return the number of crossings.
1436
+
1437
+ OUTPUT:
1438
+
1439
+ We place the elements of the ground set in order on a
1440
+ line and draw the set partition by linking consecutive
1441
+ elements of each block in the upper half-plane. This
1442
+ function returns the number the pairs of crossing lines.
1443
+
1444
+ EXAMPLES::
1445
+
1446
+ sage: p = SetPartition([[1,4],[2,5,7],[3,6]])
1447
+ sage: p.number_of_crossings()
1448
+ 4
1449
+
1450
+ sage: n = PerfectMatching([3,8,1,7,6,5,4,2]); n
1451
+ [(1, 3), (2, 8), (4, 7), (5, 6)]
1452
+ sage: n.number_of_crossings()
1453
+ 1
1454
+ """
1455
+ return Integer(len(list(self.crossings_iterator())))
1456
+
1457
+ def is_noncrossing(self) -> bool:
1458
+ r"""
1459
+ Check if ``self`` is noncrossing.
1460
+
1461
+ OUTPUT:
1462
+
1463
+ We place the elements of the ground set in order on a
1464
+ line and draw the set partition by linking consecutive
1465
+ elements of each block in the upper half-plane. This
1466
+ function returns ``True`` if the picture obtained this
1467
+ way has no crossings.
1468
+
1469
+ EXAMPLES::
1470
+
1471
+ sage: p = SetPartition([[1,4],[2,5,7],[3,6]])
1472
+ sage: p.is_noncrossing()
1473
+ False
1474
+
1475
+ sage: n = PerfectMatching([3,8,1,7,6,5,4,2]); n
1476
+ [(1, 3), (2, 8), (4, 7), (5, 6)]
1477
+ sage: n.is_noncrossing()
1478
+ False
1479
+ sage: PerfectMatching([(1, 4), (2, 3), (5, 6)]).is_noncrossing()
1480
+ True
1481
+ """
1482
+ it = self.crossings_iterator()
1483
+ try:
1484
+ next(it)
1485
+ except StopIteration:
1486
+ return True
1487
+ return False
1488
+
1489
+ def nestings_iterator(self):
1490
+ r"""
1491
+ Iterate over the nestings of ``self``.
1492
+
1493
+ OUTPUT:
1494
+
1495
+ We place the elements of the ground set in order on a
1496
+ line and draw the set partition by linking consecutive
1497
+ elements of each block in the upper half-plane. This
1498
+ function returns an iterator over the pairs of nesting
1499
+ lines (as a line correspond to a pair, the iterator
1500
+ produces pairs of pairs).
1501
+
1502
+ EXAMPLES::
1503
+
1504
+ sage: n = PerfectMatching([(1, 6), (2, 7), (3, 5), (4, 8)])
1505
+ sage: it = n.nestings_iterator()
1506
+ sage: next(it)
1507
+ ((1, 6), (3, 5))
1508
+ sage: next(it)
1509
+ ((2, 7), (3, 5))
1510
+ sage: next(it)
1511
+ Traceback (most recent call last):
1512
+ ...
1513
+ StopIteration
1514
+ """
1515
+ # each arc is sorted, but the set of arcs might not be
1516
+ arcs = sorted(self.arcs(), key=min)
1517
+ while arcs:
1518
+ i1, j1 = arcs.pop(0)
1519
+ for i2, j2 in arcs:
1520
+ # we know that i1 < i2 and i1 < j1 and i2 < j2
1521
+ if i2 < j2 < j1:
1522
+ yield ((i1, j1), (i2, j2))
1523
+
1524
+ def nestings(self):
1525
+ r"""
1526
+ Return the nestings of ``self``.
1527
+
1528
+ OUTPUT:
1529
+
1530
+ We place the elements of the ground set in order on a
1531
+ line and draw the set partition by linking consecutive
1532
+ elements of each block in the upper half-plane. This
1533
+ function returns the list of the pairs of nesting lines
1534
+ (as a line correspond to a pair, it returns a list of
1535
+ pairs of pairs).
1536
+
1537
+ EXAMPLES::
1538
+
1539
+ sage: m = PerfectMatching([(1, 6), (2, 7), (3, 5), (4, 8)])
1540
+ sage: m.nestings()
1541
+ [((1, 6), (3, 5)), ((2, 7), (3, 5))]
1542
+
1543
+ sage: n = PerfectMatching([3,8,1,7,6,5,4,2]); n
1544
+ [(1, 3), (2, 8), (4, 7), (5, 6)]
1545
+ sage: n.nestings()
1546
+ [((2, 8), (4, 7)), ((2, 8), (5, 6)), ((4, 7), (5, 6))]
1547
+
1548
+ TESTS::
1549
+
1550
+ sage: m = PerfectMatching([]); m.nestings()
1551
+ []
1552
+ """
1553
+ return list(self.nestings_iterator())
1554
+
1555
+ def number_of_nestings(self):
1556
+ r"""
1557
+ Return the number of nestings of ``self``.
1558
+
1559
+ OUTPUT:
1560
+
1561
+ We place the elements of the ground set in order on a
1562
+ line and draw the set partition by linking consecutive
1563
+ elements of each block in the upper half-plane. This
1564
+ function returns the number the pairs of nesting lines.
1565
+
1566
+ EXAMPLES::
1567
+
1568
+ sage: n = PerfectMatching([3,8,1,7,6,5,4,2]); n
1569
+ [(1, 3), (2, 8), (4, 7), (5, 6)]
1570
+ sage: n.number_of_nestings()
1571
+ 3
1572
+ """
1573
+ c = Integer(0)
1574
+ one = Integer(1)
1575
+ for _ in self.nestings_iterator():
1576
+ c += one
1577
+ return c
1578
+
1579
+ def is_nonnesting(self) -> bool:
1580
+ r"""
1581
+ Return if ``self`` is nonnesting or not.
1582
+
1583
+ OUTPUT:
1584
+
1585
+ We place the elements of the ground set in order on a
1586
+ line and draw the set partition by linking consecutive
1587
+ elements of each block in the upper half-plane. This
1588
+ function returns ``True`` if the picture obtained this
1589
+ way has no nestings.
1590
+
1591
+ EXAMPLES::
1592
+
1593
+ sage: n = PerfectMatching([3,8,1,7,6,5,4,2]); n
1594
+ [(1, 3), (2, 8), (4, 7), (5, 6)]
1595
+ sage: n.is_nonnesting()
1596
+ False
1597
+ sage: PerfectMatching([(1, 3), (2, 5), (4, 6)]).is_nonnesting()
1598
+ True
1599
+ """
1600
+ it = self.nestings_iterator()
1601
+ try:
1602
+ next(it)
1603
+ except StopIteration:
1604
+ return True
1605
+ return False
1606
+
1607
+ def is_atomic(self) -> bool:
1608
+ r"""
1609
+ Return if ``self`` is an atomic set partition.
1610
+
1611
+ A (standard) set partition `A` can be split if there exist `j < i`
1612
+ such that `\max(A_j) < \min(A_i)` where `A` is ordered by minimal
1613
+ elements. This means we can write `A = B | C` for some nonempty set
1614
+ partitions `B` and `C`. We call a set partition *atomic* if it
1615
+ cannot be split and is nonempty. Here, the pipe symbol
1616
+ `|` is as defined in method :meth:`pipe`.
1617
+
1618
+ EXAMPLES::
1619
+
1620
+ sage: SetPartition([[1,3], [2]]).is_atomic()
1621
+ True
1622
+ sage: SetPartition([[1,3], [2], [4]]).is_atomic()
1623
+ False
1624
+ sage: SetPartition([[1], [2,4], [3]]).is_atomic()
1625
+ False
1626
+ sage: SetPartition([[1,2,3,4]]).is_atomic()
1627
+ True
1628
+ sage: SetPartition([[1, 4], [2], [3]]).is_atomic()
1629
+ True
1630
+ sage: SetPartition([]).is_atomic()
1631
+ False
1632
+ """
1633
+ if len(self) == 0:
1634
+ return False
1635
+ maximum_so_far = max(self[0])
1636
+ for S in self[1:]:
1637
+ if maximum_so_far < min(S):
1638
+ return False
1639
+ maximum_so_far = max(maximum_so_far, *S)
1640
+ return True
1641
+
1642
+ def standardization(self):
1643
+ r"""
1644
+ Return the standardization of ``self``.
1645
+
1646
+ Given a set partition `A = \{A_1, \ldots, A_n\}` of an ordered
1647
+ set `S`, the standardization of `A` is the set partition of
1648
+ `\{1, 2, \ldots, |S|\}` obtained by replacing the elements of
1649
+ the parts of `A` by the integers `1, 2, \ldots, |S|` in such
1650
+ a way that their relative order is preserved (i. e., the
1651
+ smallest element in the whole set partition is replaced by
1652
+ `1`, the next-smallest by `2`, and so on).
1653
+
1654
+ EXAMPLES::
1655
+
1656
+ sage: SetPartition([[4], [1, 3]]).standardization()
1657
+ {{1, 2}, {3}}
1658
+ sage: SetPartition([[4], [6, 3]]).standardization()
1659
+ {{1, 3}, {2}}
1660
+ sage: SetPartition([]).standardization()
1661
+ {}
1662
+ sage: SetPartition([('c','b'),('d','f'),('e','a')]).standardization()
1663
+ {{1, 5}, {2, 3}, {4, 6}}
1664
+ """
1665
+ r = {e: i for i, e in enumerate(sorted(self.base_set()), 1)}
1666
+ return SetPartitions(len(r))([[r[e] for e in b] for b in self])
1667
+
1668
+ def restriction(self, I):
1669
+ """
1670
+ Return the restriction of ``self`` to a subset ``I``
1671
+ (which is given as a set or list or any other iterable).
1672
+
1673
+ EXAMPLES::
1674
+
1675
+ sage: A = SetPartition([[1], [2,3]])
1676
+ sage: A.restriction([1,2])
1677
+ {{1}, {2}}
1678
+ sage: A.restriction([2,3])
1679
+ {{2, 3}}
1680
+ sage: A.restriction([])
1681
+ {}
1682
+ sage: A.restriction([4])
1683
+ {}
1684
+ """
1685
+ ret = []
1686
+ for part in self:
1687
+ newpart = [i for i in part if i in I]
1688
+ if len(newpart) != 0:
1689
+ ret.append(newpart)
1690
+ return SetPartition(ret)
1691
+
1692
+ def ordered_set_partition_action(self, s):
1693
+ r"""
1694
+ Return the action of an ordered set partition ``s`` on ``self``.
1695
+
1696
+ Let `A = \{A_1, A_2, \ldots, A_k\}` be a set partition of some
1697
+ set `S` and `s` be an ordered set partition (i.e., set composition)
1698
+ of a subset of `[k]`. Let `A^{\downarrow}` denote the standardization
1699
+ of `A`, and `A_{\{ i_1, i_2, \ldots, i_m \}}` denote the sub-partition
1700
+ `\{A_{i_1}, A_{i_2}, \ldots, A_{i_m}\}` for any subset
1701
+ `\{i_1, \ldots, i_m\}` of `\{1, \ldots, k\}`. We define the set
1702
+ partition `s(A)` by
1703
+
1704
+ .. MATH::
1705
+
1706
+ s(A) = A_{s_1}^{\downarrow} | A_{s_2}^{\downarrow} | \cdots
1707
+ | A_{s_q}^{\downarrow}.
1708
+
1709
+ where `s = (s_1, s_2, \ldots, s_q)`. Here, the pipe symbol
1710
+ `|` is as defined in method :meth:`pipe`.
1711
+
1712
+ This is `s[A]` in section 2.3 in [LM2011]_.
1713
+
1714
+ INPUT:
1715
+
1716
+ - ``s`` -- an ordered set partition with base set a subset
1717
+ of `\{1, \ldots, k\}`
1718
+
1719
+ EXAMPLES::
1720
+
1721
+ sage: A = SetPartition([[1], [2,4], [3]])
1722
+ sage: s = OrderedSetPartition([[1,3], [2]])
1723
+ sage: A.ordered_set_partition_action(s)
1724
+ {{1}, {2}, {3, 4}}
1725
+ sage: s = OrderedSetPartition([[2,3], [1]])
1726
+ sage: A.ordered_set_partition_action(s)
1727
+ {{1, 3}, {2}, {4}}
1728
+
1729
+ We create Figure 1 in [LM2011]_ (we note that there is a typo in the
1730
+ lower-left corner of the table in the published version of the
1731
+ paper, whereas the arXiv version gives the correct partition)::
1732
+
1733
+ sage: A = SetPartition([[1,3], [2,9], [4,5,8], [7]])
1734
+ sage: B = SetPartition([[1,3], [2,8], [4,5,6], [7]])
1735
+ sage: C = SetPartition([[1,5], [2,8], [3,4,6], [7]])
1736
+ sage: s = OrderedSetPartition([[1,3], [2]])
1737
+ sage: t = OrderedSetPartition([[2], [3,4]])
1738
+ sage: u = OrderedSetPartition([[1], [2,3,4]])
1739
+ sage: A.ordered_set_partition_action(s)
1740
+ {{1, 2}, {3, 4, 5}, {6, 7}}
1741
+ sage: A.ordered_set_partition_action(t)
1742
+ {{1, 2}, {3, 4, 6}, {5}}
1743
+ sage: A.ordered_set_partition_action(u)
1744
+ {{1, 2}, {3, 8}, {4, 5, 7}, {6}}
1745
+ sage: B.ordered_set_partition_action(s)
1746
+ {{1, 2}, {3, 4, 5}, {6, 7}}
1747
+ sage: B.ordered_set_partition_action(t)
1748
+ {{1, 2}, {3, 4, 5}, {6}}
1749
+ sage: B.ordered_set_partition_action(u)
1750
+ {{1, 2}, {3, 8}, {4, 5, 6}, {7}}
1751
+ sage: C.ordered_set_partition_action(s)
1752
+ {{1, 4}, {2, 3, 5}, {6, 7}}
1753
+ sage: C.ordered_set_partition_action(t)
1754
+ {{1, 2}, {3, 4, 5}, {6}}
1755
+ sage: C.ordered_set_partition_action(u)
1756
+ {{1, 2}, {3, 8}, {4, 5, 6}, {7}}
1757
+
1758
+ REFERENCES:
1759
+
1760
+ - [LM2011]_
1761
+ """
1762
+ cur = 1
1763
+ ret = []
1764
+ for part in s:
1765
+ sub_parts = [list(self[i - 1]) for i in part] # -1 for indexing
1766
+ # Standardizing sub_parts (the cur variable not being reset
1767
+ # to 1 gives us the offset we want):
1768
+ mins = [min(i) for i in sub_parts]
1769
+ over_max = max(map(max, sub_parts)) + 1
1770
+ temp = [[] for _ in repeat(None, len(part))]
1771
+ while min(mins) != over_max:
1772
+ m = min(mins)
1773
+ i = mins.index(m)
1774
+ temp[i].append(cur)
1775
+ cur += 1
1776
+ sub_parts[i].pop(sub_parts[i].index(m))
1777
+ if len(sub_parts[i]) != 0:
1778
+ mins[i] = min(sub_parts[i])
1779
+ else:
1780
+ mins[i] = over_max
1781
+ ret += temp
1782
+ return SetPartition(ret)
1783
+
1784
+ def refinements(self):
1785
+ """
1786
+ Return a list of refinements of ``self``.
1787
+
1788
+ .. SEEALSO::
1789
+
1790
+ :meth:`coarsenings`
1791
+
1792
+ EXAMPLES::
1793
+
1794
+ sage: SetPartition([[1,3],[2,4]]).refinements() # needs sage.graphs sage.libs.flint
1795
+ [{{1, 3}, {2, 4}},
1796
+ {{1, 3}, {2}, {4}},
1797
+ {{1}, {2, 4}, {3}},
1798
+ {{1}, {2}, {3}, {4}}]
1799
+ sage: SetPartition([[1],[2,4],[3]]).refinements() # needs sage.graphs sage.libs.flint
1800
+ [{{1}, {2, 4}, {3}}, {{1}, {2}, {3}, {4}}]
1801
+ sage: SetPartition([]).refinements() # needs sage.graphs sage.libs.flint
1802
+ [{}]
1803
+ """
1804
+ L = [SetPartitions(part) for part in self]
1805
+ return [SetPartition(sum(map(list, x), [])) for x in itertools.product(*L)]
1806
+
1807
+ def strict_coarsenings(self):
1808
+ r"""
1809
+ Return all strict coarsenings of ``self``.
1810
+
1811
+ Strict coarsening is the binary relation on set partitions
1812
+ defined as the transitive-and-reflexive closure of the
1813
+ relation `\prec` defined as follows: For two set partitions
1814
+ `A` and `B`, we have `A \prec B` if there exist parts
1815
+ `A_i, A_j` of `A` such that `\max(A_i) < \min(A_j)` and
1816
+ `B = A \setminus \{A_i, A_j\} \cup \{ A_i \cup A_j \}`.
1817
+
1818
+ EXAMPLES::
1819
+
1820
+ sage: A = SetPartition([[1],[2,3],[4]])
1821
+ sage: A.strict_coarsenings()
1822
+ [{{1}, {2, 3}, {4}}, {{1, 2, 3}, {4}}, {{1, 4}, {2, 3}},
1823
+ {{1}, {2, 3, 4}}, {{1, 2, 3, 4}}]
1824
+ sage: SetPartition([[1],[2,4],[3]]).strict_coarsenings()
1825
+ [{{1}, {2, 4}, {3}}, {{1, 2, 4}, {3}}, {{1, 3}, {2, 4}}]
1826
+ sage: SetPartition([]).strict_coarsenings()
1827
+ [{}]
1828
+ """
1829
+ # This is more or less generic code for computing a
1830
+ # transitive-and-reflexive closure by depth-first search.
1831
+ todo = [self]
1832
+ visited = set([self])
1833
+ ret = [self]
1834
+ while todo:
1835
+ A = todo.pop()
1836
+ for i, part in enumerate(A):
1837
+ for j, other in enumerate(A[i + 1:]):
1838
+ if max(part) < min(other):
1839
+ next_pi = A[:i]
1840
+ next_pi.append(part.union(other))
1841
+ next_pi += A[i + 1:i + 1 + j] + A[i + j + 2:]
1842
+ next_pi = SetPartition(next_pi)
1843
+ if next_pi not in visited:
1844
+ todo.append(next_pi)
1845
+ visited.add(next_pi)
1846
+ ret.append(next_pi)
1847
+ return ret
1848
+
1849
+ def arcs(self):
1850
+ r"""
1851
+ Return ``self`` as a list of arcs.
1852
+
1853
+ Assuming that the blocks are sorted, the arcs are the pairs
1854
+ of consecutive elements in the blocks.
1855
+
1856
+ EXAMPLES::
1857
+
1858
+ sage: A = SetPartition([[1],[2,3],[4]])
1859
+ sage: A.arcs()
1860
+ [(2, 3)]
1861
+ sage: B = SetPartition([[1,3,6,7],[2,5],[4]])
1862
+ sage: B.arcs()
1863
+ [(1, 3), (3, 6), (6, 7), (2, 5)]
1864
+ """
1865
+ arcs = []
1866
+ for p in self:
1867
+ p = sorted(p)
1868
+ arcs.extend((p[i], p[i + 1]) for i in range(len(p) - 1))
1869
+ return arcs
1870
+
1871
+ def plot(self, angle=None, color='black', base_set_dict=None):
1872
+ r"""
1873
+ Return a plot of ``self``.
1874
+
1875
+ INPUT:
1876
+
1877
+ - ``angle`` -- (default: `\pi/4`) the angle at which the arcs take off
1878
+ (if angle is negative, the arcs are drawn below the horizontal line)
1879
+
1880
+ - ``color`` -- (default: ``'black'``) color of the arcs
1881
+
1882
+ - ``base_set_dict`` -- (optional) dictionary with keys elements
1883
+ of :meth:`base_set()` and values as integer or float
1884
+
1885
+ EXAMPLES::
1886
+
1887
+ sage: p = SetPartition([[1,10,11],[2,3,7],[4,5,6],[8,9]])
1888
+ sage: p.plot() # needs sage.plot sage.symbolic
1889
+ Graphics object consisting of 29 graphics primitives
1890
+
1891
+ .. PLOT::
1892
+
1893
+ p = SetPartition([[1,10,11],[2,3,7],[4,5,6],[8,9]])
1894
+ sphinx_plot(p.plot())
1895
+
1896
+ ::
1897
+
1898
+ sage: p = SetPartition([[1,3,4],[2,5]])
1899
+ sage: print(p.plot().description()) # needs sage.plot sage.symbolic
1900
+ Point set defined by 1 point(s): [(0.0, 0.0)]
1901
+ Point set defined by 1 point(s): [(1.0, 0.0)]
1902
+ Point set defined by 1 point(s): [(2.0, 0.0)]
1903
+ Point set defined by 1 point(s): [(3.0, 0.0)]
1904
+ Point set defined by 1 point(s): [(4.0, 0.0)]
1905
+ Text '1' at the point (0.0,-0.1)
1906
+ Text '2' at the point (1.0,-0.1)
1907
+ Text '3' at the point (2.0,-0.1)
1908
+ Text '4' at the point (3.0,-0.1)
1909
+ Text '5' at the point (4.0,-0.1)
1910
+ Arc with center (1.0,-1.0) radii (1.41421356237...,1.41421356237...)
1911
+ angle 0.0 inside the sector (0.785398163397...,2.35619449019...)
1912
+ Arc with center (2.5,-0.5) radii (0.70710678118...,0.70710678118...)
1913
+ angle 0.0 inside the sector (0.785398163397...,2.35619449019...)
1914
+ Arc with center (2.5,-1.5) radii (2.1213203435...,2.1213203435...)
1915
+ angle 0.0 inside the sector (0.785398163397...,2.35619449019...)
1916
+ sage: p = SetPartition([['a','c'],['b','d'],['e']])
1917
+ sage: print(p.plot().description()) # needs sage.plot sage.symbolic
1918
+ Point set defined by 1 point(s): [(0.0, 0.0)]
1919
+ Point set defined by 1 point(s): [(1.0, 0.0)]
1920
+ Point set defined by 1 point(s): [(2.0, 0.0)]
1921
+ Point set defined by 1 point(s): [(3.0, 0.0)]
1922
+ Point set defined by 1 point(s): [(4.0, 0.0)]
1923
+ Text 'a' at the point (0.0,-0.1)
1924
+ Text 'b' at the point (1.0,-0.1)
1925
+ Text 'c' at the point (2.0,-0.1)
1926
+ Text 'd' at the point (3.0,-0.1)
1927
+ Text 'e' at the point (4.0,-0.1)
1928
+ Arc with center (1.0,-1.0) radii (1.41421356237...,1.41421356237...)
1929
+ angle 0.0 inside the sector (0.785398163397...,2.35619449019...)
1930
+ Arc with center (2.0,-1.0) radii (1.41421356237...,1.41421356237...)
1931
+ angle 0.0 inside the sector (0.785398163397...,2.35619449019...)
1932
+ sage: p = SetPartition([['a','c'],['b','d'],['e']])
1933
+ sage: print(p.plot(base_set_dict={'a':0,'b':1,'c':2, # needs sage.plot sage.symbolic
1934
+ ....: 'd':-2.3,'e':5.4}).description())
1935
+ Point set defined by 1 point(s): [(-2.3, 0.0)]
1936
+ Point set defined by 1 point(s): [(0.0, 0.0)]
1937
+ Point set defined by 1 point(s): [(1.0, 0.0)]
1938
+ Point set defined by 1 point(s): [(2.0, 0.0)]
1939
+ Point set defined by 1 point(s): [(5.4, 0.0)]
1940
+ Text 'a' at the point (0.0,-0.1)
1941
+ Text 'b' at the point (1.0,-0.1)
1942
+ Text 'c' at the point (2.0,-0.1)
1943
+ Text 'd' at the point (-2.3,-0.1)
1944
+ Text 'e' at the point (5.4,-0.1)
1945
+ Arc with center (-0.6...,-1.65) radii (2.3334523779...,2.3334523779...)
1946
+ angle 0.0 inside the sector (0.785398163397...,2.35619449019...)
1947
+ Arc with center (1.0,-1.0) radii (1.4142135623...,1.4142135623...)
1948
+ angle 0.0 inside the sector (0.785398163397...,2.35619449019...)
1949
+ """
1950
+ from sage.plot.graphics import Graphics
1951
+ from sage.plot.point import point
1952
+ from sage.plot.text import text
1953
+ from sage.plot.arc import arc
1954
+ from sage.symbolic.constants import pi
1955
+ from sage.functions.trig import tan, sin
1956
+ from sage.functions.generalized import sgn
1957
+
1958
+ diag = Graphics()
1959
+ sorted_vertices_list = list(self.base_set())
1960
+ sorted_vertices_list.sort()
1961
+
1962
+ if angle is None:
1963
+ angle = pi / 4
1964
+
1965
+ if base_set_dict is not None:
1966
+ vertices_dict = base_set_dict
1967
+ else:
1968
+ vertices_dict = {val: pos for pos, val in enumerate(sorted_vertices_list)}
1969
+
1970
+ for elt in vertices_dict:
1971
+ pos = vertices_dict[elt]
1972
+ diag += point((pos, 0), size=30, color=color)
1973
+ diag += text(elt, (pos, -sgn(angle) * 0.1), color=color)
1974
+ # TODO: change 0.1 to something proportional to the height of the picture
1975
+
1976
+ for k, j in self.arcs():
1977
+ pos_k, pos_j = float(vertices_dict[k]), float(vertices_dict[j])
1978
+ center = ((pos_k + pos_j) / 2,
1979
+ -abs(pos_j - pos_k) / (2 * tan(angle)))
1980
+ r1 = abs((pos_j - pos_k) / (2 * sin(angle)))
1981
+ sector = (sgn(angle) * (pi / 2 - angle),
1982
+ sgn(angle) * (pi / 2 + angle))
1983
+ diag += arc(center=center, r1=r1, sector=sector, color=color)
1984
+
1985
+ diag.axes(False)
1986
+ return diag
1987
+
1988
+
1989
+ class SetPartitions(UniqueRepresentation, Parent):
1990
+ r"""
1991
+ An (unordered) partition of a set `S` is a set of pairwise
1992
+ disjoint nonempty subsets with union `S`, and is represented
1993
+ by a sorted list of such subsets.
1994
+
1995
+ ``SetPartitions(s)`` returns the class of all set partitions of the set
1996
+ ``s``, which can be given as a set or a string; if a string, each
1997
+ character is considered an element.
1998
+
1999
+ ``SetPartitions(n)``, where ``n`` is an integer, returns the class of
2000
+ all set partitions of the set `\{1, 2, \ldots, n\}`.
2001
+
2002
+ You may specify a second argument `k`. If `k` is an integer,
2003
+ :class:`SetPartitions` returns the class of set partitions into `k` parts;
2004
+ if it is an integer partition, :class:`SetPartitions` returns the class of
2005
+ set partitions whose block sizes correspond to that integer partition.
2006
+
2007
+ The Bell number `B_n`, named in honor of Eric Temple Bell,
2008
+ is the number of different partitions of a set with `n` elements.
2009
+
2010
+ EXAMPLES::
2011
+
2012
+ sage: S = [1,2,3,4]
2013
+ sage: SetPartitions(S, 2)
2014
+ Set partitions of {1, 2, 3, 4} with 2 parts
2015
+ sage: SetPartitions([1,2,3,4], [3,1]).list() # needs sage.graphs sage.rings.finite_rings
2016
+ [{{1}, {2, 3, 4}}, {{1, 2, 3}, {4}}, {{1, 2, 4}, {3}}, {{1, 3, 4}, {2}}]
2017
+ sage: SetPartitions(7, [3,3,1]).cardinality() # needs sage.libs.flint
2018
+ 70
2019
+
2020
+ In strings, repeated letters are not considered distinct as of
2021
+ :issue:`14140`::
2022
+
2023
+ sage: SetPartitions('abcde').cardinality() # needs sage.libs.flint
2024
+ 52
2025
+ sage: SetPartitions('aabcd').cardinality() # needs sage.libs.flint
2026
+ 15
2027
+
2028
+ If the number of parts exceeds the length of the set,
2029
+ an empty iterator is returned (:issue:`37643`)::
2030
+
2031
+ sage: SetPartitions(range(3), 4).list()
2032
+ []
2033
+ sage: SetPartitions('abcd', 6).list()
2034
+ []
2035
+
2036
+ REFERENCES:
2037
+
2038
+ - :wikipedia:`Partition_of_a_set`
2039
+ """
2040
+ @staticmethod
2041
+ def __classcall_private__(cls, s=None, part=None):
2042
+ """
2043
+ Normalize input to ensure a unique representation.
2044
+
2045
+ EXAMPLES::
2046
+
2047
+ sage: S = SetPartitions(4)
2048
+ sage: T = SetPartitions([1,2,3,4])
2049
+ sage: S is T
2050
+ True
2051
+ """
2052
+ if s is None:
2053
+ return SetPartitions_all()
2054
+ if isinstance(s, (int, Integer)):
2055
+ s = frozenset(range(1, s + 1))
2056
+ else:
2057
+ try:
2058
+ if s.cardinality() == infinity:
2059
+ raise ValueError("the set must be finite")
2060
+ except AttributeError:
2061
+ pass
2062
+ s = frozenset(s)
2063
+
2064
+ if part is None:
2065
+ return SetPartitions_set(s)
2066
+ else:
2067
+ if isinstance(part, (int, Integer)):
2068
+ return SetPartitions_setn(s, part)
2069
+ else:
2070
+ part = sorted(part, reverse=True)
2071
+ if part not in Partitions(len(s)):
2072
+ raise ValueError("part must be an integer partition of %s" % len(s))
2073
+ return SetPartitions_setparts(s, Partition(part))
2074
+
2075
+ def __contains__(self, x):
2076
+ """
2077
+ TESTS::
2078
+
2079
+ sage: S = SetPartitions(4, [2,2])
2080
+ sage: SA = SetPartitions()
2081
+ sage: all(sp in SA for sp in S) # needs sage.graphs sage.modules sage.rings.finite_rings
2082
+ True
2083
+ sage: Set([Set([1,2]),Set([3,7])]) in SA # needs sage.graphs
2084
+ True
2085
+ sage: Set([Set([1,2]),Set([2,3])]) in SA # needs sage.graphs
2086
+ False
2087
+ sage: Set([]) in SA # needs sage.graphs
2088
+ True
2089
+ """
2090
+ # x must be a set
2091
+ if not isinstance(x, (SetPartition, set, frozenset, Set_generic)):
2092
+ return False
2093
+
2094
+ # Check that all parts are disjoint
2095
+ base_set = set(e for p in x for e in p)
2096
+ if len(base_set) != sum(map(len, x)):
2097
+ return False
2098
+
2099
+ # Check to make sure each element of x is a set
2100
+ for s in x:
2101
+ if not isinstance(s, (set, frozenset, Set_generic)):
2102
+ return False
2103
+
2104
+ return True
2105
+
2106
+ def _element_constructor_(self, s, check=True):
2107
+ """
2108
+ Construct an element of ``self`` from ``s``.
2109
+
2110
+ INPUT:
2111
+
2112
+ - ``s`` -- set of sets
2113
+
2114
+ EXAMPLES::
2115
+
2116
+ sage: S = SetPartitions(4)
2117
+ sage: elt = S([[1,3],[2,4]]); elt
2118
+ {{1, 3}, {2, 4}}
2119
+ sage: P = SetPartitions()
2120
+ sage: P(elt).parent() is P
2121
+ True
2122
+ sage: S = SetPartitions([])
2123
+ sage: S([])
2124
+ {}
2125
+ """
2126
+ if isinstance(s, SetPartition):
2127
+ if isinstance(s.parent(), SetPartitions):
2128
+ return self.element_class(self, s, check=check)
2129
+ raise ValueError("cannot convert %s into an element of %s" % (s, self))
2130
+ return self.element_class(self, s, check=check)
2131
+
2132
+ Element = SetPartition
2133
+
2134
+ def from_restricted_growth_word(self, w, bijection='blocks'):
2135
+ r"""
2136
+ Convert a word of length `n` with letters in the nonnegative
2137
+ integers such that each letter is at most 1 larger than all
2138
+ the letters before to a set partition of `\{1,...,n\}`.
2139
+
2140
+ INPUT:
2141
+
2142
+ - ``w`` -- a restricted growth word
2143
+
2144
+ - ``bijection`` -- (default: ``blocks``) defines the map from
2145
+ restricted growth functions to set partitions. These are
2146
+ currently:
2147
+
2148
+ - ``blocks``: .
2149
+
2150
+ - ``intertwining``: :meth:`from_restricted_growth_word_intertwining`.
2151
+
2152
+ OUTPUT: a set partition
2153
+
2154
+ .. SEEALSO::
2155
+
2156
+ :meth:`SetPartition.to_restricted_growth_word`
2157
+
2158
+ EXAMPLES::
2159
+
2160
+ sage: SetPartitions().from_restricted_growth_word([0, 1, 2, 0, 2, 2, 3, 1, 2])
2161
+ {{1, 4}, {2, 8}, {3, 5, 6, 9}, {7}}
2162
+
2163
+ sage: SetPartitions().from_restricted_growth_word([0, 0, 1, 0, 2, 2, 0, 3, 1, 2, 2, 4, 2])
2164
+ {{1, 2, 4, 7}, {3, 9}, {5, 6, 10, 11, 13}, {8}, {12}}
2165
+
2166
+ sage: SetPartitions().from_restricted_growth_word([0, 0, 1, 0, 2, 2, 0, 3, 1, 2, 2, 4, 2], "intertwining")
2167
+ {{1, 2, 6, 7, 9}, {3, 4}, {5, 10, 13}, {8, 11}, {12}}
2168
+ """
2169
+ if bijection == "blocks":
2170
+ return self.from_restricted_growth_word_blocks(w)
2171
+ if bijection == "intertwining":
2172
+ return self.from_restricted_growth_word_intertwining(w)
2173
+ raise ValueError("the given bijection is not valid")
2174
+
2175
+ def from_restricted_growth_word_blocks(self, w):
2176
+ r"""
2177
+ Convert a word of length `n` with letters in the nonnegative
2178
+ integers such that each letter is at most 1 larger than all
2179
+ the letters before to a set partition of `\{1,...,n\}`.
2180
+
2181
+ ``w[i]`` is the index of the block containing ``i+1`` when
2182
+ sorting the blocks by their minimal element.
2183
+
2184
+ INPUT:
2185
+
2186
+ - ``w`` -- a restricted growth word
2187
+
2188
+ OUTPUT: a set partition
2189
+
2190
+ .. SEEALSO::
2191
+
2192
+ :meth:`from_restricted_growth_word`
2193
+ :meth:`SetPartition.to_restricted_growth_word`
2194
+
2195
+ EXAMPLES::
2196
+
2197
+ sage: SetPartitions().from_restricted_growth_word_blocks([0, 0, 1, 0, 2, 2, 0, 3, 1, 2, 2, 4, 2])
2198
+ {{1, 2, 4, 7}, {3, 9}, {5, 6, 10, 11, 13}, {8}, {12}}
2199
+ """
2200
+ R = []
2201
+ for i, B in enumerate(w, 1):
2202
+ if len(R) <= B:
2203
+ R.append([i])
2204
+ else:
2205
+ R[B].append(i)
2206
+ return self.element_class(self, R)
2207
+
2208
+ def from_restricted_growth_word_intertwining(self, w):
2209
+ r"""
2210
+ Convert a word of length `n` with letters in the nonnegative
2211
+ integers such that each letter is at most 1 larger than all
2212
+ the letters before to a set partition of `\{1,...,n\}`.
2213
+
2214
+ The `i`-th letter of the word is the numbers of crossings of
2215
+ the arc (or half-arc) in the extended arc diagram ending at
2216
+ `i`, with arcs (or half-arcs) beginning at a smaller element
2217
+ and ending at a larger element.
2218
+
2219
+ INPUT:
2220
+
2221
+ - ``w`` -- a restricted growth word
2222
+
2223
+ OUTPUT: a set partition
2224
+
2225
+ .. SEEALSO::
2226
+
2227
+ :meth:`from_restricted_growth_word`
2228
+ :meth:`SetPartition.to_restricted_growth_word`
2229
+
2230
+ EXAMPLES::
2231
+
2232
+ sage: SetPartitions().from_restricted_growth_word_intertwining([0, 0, 1, 0, 2, 2, 0, 3, 1, 2, 2, 4, 2])
2233
+ {{1, 2, 6, 7, 9}, {3, 4}, {5, 10, 13}, {8, 11}, {12}}
2234
+ """
2235
+ if len(w) == 0:
2236
+ return self.element_class(self, [])
2237
+ R = [[1]]
2238
+ C = [1] # closers, always reverse sorted
2239
+ m = 0 # max
2240
+ for i in range(1, len(w)):
2241
+ if w[i] == 1 + m: # i+1 is an opener
2242
+ m += 1
2243
+ R.append([i + 1])
2244
+ else:
2245
+ # add i+1 to the block, such that there are I[i] closers thereafter
2246
+ l = C[w[i]]
2247
+ B = next(B for B in R if l in B)
2248
+ B.append(i + 1)
2249
+ C.remove(l)
2250
+ C = [i + 1] + C
2251
+ return self.element_class(self, R)
2252
+
2253
+ def from_rook_placement(self, rooks, bijection='arcs', n=None):
2254
+ r"""
2255
+ Convert a rook placement of the triangular grid to a set
2256
+ partition of `\{1,...,n\}`.
2257
+
2258
+ If ``n`` is not given, it is first checked whether it can be
2259
+ determined from the parent, otherwise it is the maximal
2260
+ occurring integer in the set of rooks.
2261
+
2262
+ INPUT:
2263
+
2264
+ - ``rooks`` -- list of pairs `(i,j)` satisfying
2265
+ `0 < i < j < n+1`
2266
+
2267
+ - ``bijection`` -- (default: ``arcs``) defines the map from
2268
+ rook placements to set partitions. These are currently:
2269
+
2270
+ - ``arcs``: :meth:`from_arcs`.
2271
+ - ``gamma``: :meth:`from_rook_placement_gamma`.
2272
+ - ``rho``: :meth:`from_rook_placement_rho`.
2273
+ - ``psi``: :meth:`from_rook_placement_psi`.
2274
+
2275
+ - ``n`` -- (optional) the size of the ground set
2276
+
2277
+ .. SEEALSO::
2278
+
2279
+ :meth:`SetPartition.to_rook_placement`
2280
+
2281
+ EXAMPLES::
2282
+
2283
+ sage: SetPartitions(9).from_rook_placement([[1,4],[2,8],[3,5],[5,6],[6,9]])
2284
+ {{1, 4}, {2, 8}, {3, 5, 6, 9}, {7}}
2285
+
2286
+ sage: SetPartitions(13).from_rook_placement([[12,13],[10,12],[8,9],[7,11],[5,8],[4,6],[3,5],[1,4]], "gamma")
2287
+ {{1, 2, 4, 7}, {3, 9}, {5, 6, 10, 11, 13}, {8}, {12}}
2288
+
2289
+ TESTS::
2290
+
2291
+ sage: SetPartitions().from_rook_placement([])
2292
+ {}
2293
+ sage: SetPartitions().from_rook_placement([], "gamma")
2294
+ {}
2295
+ sage: SetPartitions().from_rook_placement([], "rho")
2296
+ {}
2297
+ sage: SetPartitions().from_rook_placement([], "psi")
2298
+ {}
2299
+ sage: SetPartitions().from_rook_placement([], n=2)
2300
+ {{1}, {2}}
2301
+ sage: SetPartitions().from_rook_placement([], "gamma", 2)
2302
+ {{1}, {2}}
2303
+ sage: SetPartitions().from_rook_placement([], "rho", 2)
2304
+ {{1}, {2}}
2305
+ sage: SetPartitions().from_rook_placement([], "psi", 2)
2306
+ {{1}, {2}}
2307
+ """
2308
+ if n is None:
2309
+ try:
2310
+ n = self.base_set_cardinality()
2311
+ except AttributeError:
2312
+ n = max((max(r) for r in rooks), default=0)
2313
+
2314
+ if bijection == "arcs":
2315
+ return self.from_arcs(rooks, n)
2316
+ if bijection == "rho":
2317
+ return self.from_rook_placement_rho(rooks, n)
2318
+ if bijection == "gamma":
2319
+ return self.from_rook_placement_gamma(rooks, n)
2320
+ if bijection == "psi":
2321
+ return self.from_rook_placement_psi(rooks, n)
2322
+ raise ValueError("the given bijection is not valid")
2323
+
2324
+ def from_arcs(self, arcs, n):
2325
+ r"""
2326
+ Return the coarsest set partition of `\{1,...,n\}` such that any
2327
+ two elements connected by an arc are in the same block.
2328
+
2329
+ INPUT:
2330
+
2331
+ - ``n`` -- integer specifying the size of the set
2332
+ partition to be produced
2333
+
2334
+ - ``arcs`` -- list of pairs specifying which elements are
2335
+ in the same block
2336
+
2337
+ .. SEEALSO::
2338
+
2339
+ - :meth:`from_rook_placement`
2340
+ - :meth:`SetPartition.to_rook_placement`
2341
+ - :meth:`SetPartition.arcs`
2342
+
2343
+ EXAMPLES::
2344
+
2345
+ sage: SetPartitions().from_arcs([(2,3)], 5)
2346
+ {{1}, {2, 3}, {4}, {5}}
2347
+ """
2348
+ P = DisjointSet(range(1, n + 1))
2349
+ for i, j in arcs:
2350
+ P.union(i, j)
2351
+ return self.element_class(self, P)
2352
+
2353
+ def from_rook_placement_gamma(self, rooks, n):
2354
+ r"""
2355
+ Return the set partition of `\{1,...,n\}` corresponding to the
2356
+ given rook placement by applying Wachs and White's bijection
2357
+ gamma.
2358
+
2359
+ Note that our index convention differs from the convention in
2360
+ [WW1991]_: regarding the rook board as a lower-right
2361
+ triangular grid, we refer with `(i,j)` to the cell in the
2362
+ `i`-th column from the right and the `j`-th row from the top.
2363
+
2364
+ INPUT:
2365
+
2366
+ - ``n`` -- integer specifying the size of the set
2367
+ partition to be produced
2368
+
2369
+ - ``rooks`` -- list of pairs `(i,j)` such that `0 < i < j < n+1`
2370
+
2371
+ OUTPUT: a set partition
2372
+
2373
+ .. SEEALSO::
2374
+
2375
+ - :meth:`from_rook_placement`
2376
+ - :meth:`SetPartition.to_rook_placement`
2377
+ - :meth:`SetPartition.to_rook_placement_gamma`
2378
+
2379
+ EXAMPLES:
2380
+
2381
+ Figure 5 in [WW1991]_ concerns the following rook placement::
2382
+
2383
+ sage: r = [(1, 4), (3, 5), (4, 6), (5, 8), (7, 11), (8, 9), (10, 12), (12, 13)]
2384
+
2385
+ Note that the rook `(1, 4)`, translated into Wachs and
2386
+ White's convention, is a rook in row 4 from the top and
2387
+ column 13 from the left. The corresponding set partition
2388
+ is::
2389
+
2390
+ sage: SetPartitions().from_rook_placement_gamma(r, 13)
2391
+ {{1, 2, 4, 7}, {3, 9}, {5, 6, 10, 11, 13}, {8}, {12}}
2392
+ """
2393
+ if n == 0:
2394
+ return self.element_class(self, [])
2395
+ # the columns of the board, beginning with column n-1
2396
+ C = [set(range(n + 1 - j, n + 1)) for j in range(1, n)]
2397
+ # delete cells north and east of each rook
2398
+ for j, i in rooks:
2399
+ # north
2400
+ C[n - j - 1].difference_update(range(j + 1, i + 1))
2401
+ # east
2402
+ for l in range(n + 1 - j, n + 1):
2403
+ C[l - 2].discard(i)
2404
+ w = [0] + [len(c) for c in C]
2405
+ return self.from_restricted_growth_word_blocks(w)
2406
+
2407
+ def from_rook_placement_rho(self, rooks, n):
2408
+ r"""
2409
+ Return the set partition of `\{1,...,n\}` corresponding to the
2410
+ given rook placement by applying Wachs and White's bijection
2411
+ rho.
2412
+
2413
+ Note that our index convention differs from the convention in
2414
+ [WW1991]_: regarding the rook board as a lower-right
2415
+ triangular grid, we refer with `(i,j)` to the cell in the
2416
+ `i`-th column from the right and the `j`-th row from the top.
2417
+
2418
+ INPUT:
2419
+
2420
+ - ``n`` -- integer specifying the size of the set
2421
+ partition to be produced
2422
+
2423
+ - ``rooks`` -- list of pairs `(i,j)` such that `0 < i < j < n+1`
2424
+
2425
+ OUTPUT: a set partition
2426
+
2427
+ .. SEEALSO::
2428
+
2429
+ - :meth:`from_rook_placement`
2430
+ - :meth:`SetPartition.to_rook_placement`
2431
+ - :meth:`SetPartition.to_rook_placement_rho`
2432
+
2433
+ EXAMPLES:
2434
+
2435
+ Figure 5 in [WW1991]_ concerns the following rook placement::
2436
+
2437
+ sage: r = [(1, 2), (2, 6), (3, 4), (4, 10), (5, 9), (6, 7), (10, 11), (11, 13)]
2438
+
2439
+ Note that the rook `(1, 2)`, translated into Wachs and
2440
+ White's convention, is a rook in row 2 from the top and
2441
+ column 13 from the left. The corresponding set partition
2442
+ is::
2443
+
2444
+ sage: SetPartitions().from_rook_placement_rho(r, 13)
2445
+ {{1, 2, 4, 7}, {3, 9}, {5, 6, 10, 11, 13}, {8}, {12}}
2446
+ """
2447
+ # the closers correspond to the empty columns
2448
+ cols = [j for j, _ in rooks]
2449
+ R = [j for j in range(1, n + 1) if j not in cols]
2450
+ # the columns of the board, beginning with column n-1
2451
+ C = [set(range(n + 1 - j, n + 1)) if n - j not in R else set()
2452
+ for j in range(1, n)]
2453
+ for j, i in rooks: # column j from right, row i from top
2454
+ # south
2455
+ C[n - j - 1].difference_update(range(i, n + 1))
2456
+ # east
2457
+ for l in range(n + 1 - j, n + 1):
2458
+ C[l - 2].discard(i)
2459
+
2460
+ C_flat = [i for c in C for i in c]
2461
+ # the number of closers which are larger than i and whose
2462
+ # block is before the block of i
2463
+ rs = [C_flat.count(i) for i in range(1, n + 1)]
2464
+ # create the blocks
2465
+ P = [[] for _ in R]
2466
+ for i in range(1, n + 1):
2467
+ k = rs[i - 1]
2468
+ # find k-th block which does not yet have a closer
2469
+ b = 0
2470
+ while k > 0 or (P[b] and P[b][-1] in R):
2471
+ if P[b][-1] not in R:
2472
+ k -= 1
2473
+ b += 1
2474
+ P[b].append(i)
2475
+ return self.element_class(self, P)
2476
+
2477
+ def from_rook_placement_psi(self, rooks, n):
2478
+ r"""
2479
+ Return the set partition of `\{1,...,n\}` corresponding to the
2480
+ given rook placement by applying Yip's bijection psi.
2481
+
2482
+ INPUT:
2483
+
2484
+ - ``n`` -- integer specifying the size of the set
2485
+ partition to be produced
2486
+
2487
+ - ``rooks`` -- list of pairs `(i,j)` such that `0 < i < j < n+1`
2488
+
2489
+ OUTPUT: a set partition
2490
+
2491
+ .. SEEALSO::
2492
+
2493
+ - :meth:`from_rook_placement`
2494
+ - :meth:`SetPartition.to_rook_placement`
2495
+ - :meth:`SetPartition.to_rook_placement_psi`
2496
+
2497
+ EXAMPLES:
2498
+
2499
+ Example 36 (arXiv version: Example 4.5) in [Yip2018]_
2500
+ concerns the following rook placement::
2501
+
2502
+ sage: r = [(4,5), (1,7), (3, 8), (7,9)]
2503
+ sage: SetPartitions().from_rook_placement_psi(r, 9)
2504
+ {{1, 5}, {2}, {3, 8, 9}, {4}, {6, 7}}
2505
+ """
2506
+ # Yip draws the diagram as an upper triangular matrix, thus
2507
+ # we refer to the cell in row i and column j with (i, j)
2508
+ P = []
2509
+ rooks_by_column = {j: i for (i, j) in rooks}
2510
+ for c in range(1, n + 1):
2511
+ # determine the weight of column c
2512
+ try:
2513
+ r = rooks_by_column[c]
2514
+ n_rooks = 1
2515
+ ne = r - 1 + sum(1 for i, j in rooks if i > r and j < c)
2516
+ except KeyError:
2517
+ n_rooks = 0
2518
+ ne = sum(1 for i, j in rooks if j < c)
2519
+
2520
+ b = c - n_rooks - ne
2521
+ if len(P) == b - 1:
2522
+ P.append([c])
2523
+ else:
2524
+ P[b - 1].append(c)
2525
+ P = sorted(P, key=lambda B: (-len(B), min(B)))
2526
+ return self.element_class(self, P)
2527
+
2528
+ def is_less_than(self, s, t) -> bool:
2529
+ r"""
2530
+ Check if `s < t` in the refinement ordering on set partitions.
2531
+
2532
+ This means that `s` is a refinement of `t` and satisfies
2533
+ `s \neq t`.
2534
+
2535
+ A set partition `s` is said to be a refinement of a set
2536
+ partition `t` of the same set if and only if each part of
2537
+ `s` is a subset of a part of `t`.
2538
+
2539
+ EXAMPLES::
2540
+
2541
+ sage: S = SetPartitions(4)
2542
+ sage: s = S([[1,3],[2,4]])
2543
+ sage: t = S([[1],[2],[3],[4]])
2544
+ sage: S.is_less_than(t, s)
2545
+ True
2546
+ sage: S.is_less_than(s, t)
2547
+ False
2548
+ sage: S.is_less_than(s, s)
2549
+ False
2550
+ """
2551
+ if hasattr(s.parent(), "_set"):
2552
+ S = s.parent()._set
2553
+ else:
2554
+ S = s.base_set()
2555
+ if hasattr(t.parent(), "_set"):
2556
+ T = t.parent()._set
2557
+ else:
2558
+ T = t.base_set()
2559
+ if S != T:
2560
+ raise ValueError("cannot compare partitions of different sets")
2561
+
2562
+ if s == t:
2563
+ return False
2564
+
2565
+ for p in s:
2566
+ x = next(iter(p))
2567
+ for t_ in t:
2568
+ if x in t_:
2569
+ break
2570
+ for p_ in p:
2571
+ if p_ not in t_:
2572
+ return False
2573
+ return True
2574
+
2575
+ lt = is_less_than
2576
+
2577
+ def is_strict_refinement(self, s, t) -> bool:
2578
+ r"""
2579
+ Return ``True`` if ``s`` is a strict refinement of ``t`` and
2580
+ satisfies `s \neq t`.
2581
+
2582
+ A set partition `s` is said to be a strict refinement of a set
2583
+ partition `t` of the same set if and only if one can obtain
2584
+ `t` from `s` by repeatedly combining pairs of parts whose
2585
+ convex hulls don't intersect (i. e., whenever we are combining
2586
+ two parts, the maximum of each of them should be smaller than
2587
+ the minimum of the other).
2588
+
2589
+ EXAMPLES::
2590
+
2591
+ sage: S = SetPartitions(4)
2592
+ sage: s = S([[1],[2],[3],[4]])
2593
+ sage: t = S([[1,3],[2,4]])
2594
+ sage: u = S([[1,2,3,4]])
2595
+ sage: S.is_strict_refinement(s, t)
2596
+ True
2597
+ sage: S.is_strict_refinement(t, u)
2598
+ False
2599
+ sage: A = SetPartition([[1,3],[2,4]])
2600
+ sage: B = SetPartition([[1,2,3,4]])
2601
+ sage: S.is_strict_refinement(s, A)
2602
+ True
2603
+ sage: S.is_strict_refinement(t, B)
2604
+ False
2605
+ """
2606
+ if hasattr(s.parent(), "_set"):
2607
+ S = s.parent()._set
2608
+ else:
2609
+ S = frozenset(s.base_set())
2610
+ if hasattr(t.parent(), "_set"):
2611
+ T = t.parent()._set
2612
+ else:
2613
+ T = frozenset(t.base_set())
2614
+ if S != T:
2615
+ raise ValueError("cannot compare partitions of different sets")
2616
+
2617
+ if s == t:
2618
+ return False
2619
+
2620
+ for p in t:
2621
+ L = [x for x in s if x.issubset(p)]
2622
+ if sum(len(x) for x in L) != len(p) \
2623
+ or any(max(L[i]) > min(L[i + 1])
2624
+ for i in range(len(L) - 1)):
2625
+ return False
2626
+ return True
2627
+
2628
+
2629
+ class SetPartitions_all(SetPartitions):
2630
+ r"""
2631
+ All set partitions.
2632
+ """
2633
+
2634
+ def __init__(self):
2635
+ """
2636
+ Initialize ``self``.
2637
+
2638
+ EXAMPLES::
2639
+
2640
+ sage: S = SetPartitions()
2641
+ sage: TestSuite(S).run()
2642
+ """
2643
+ SetPartitions.__init__(self, category=InfiniteEnumeratedSets())
2644
+
2645
+ def subset(self, size=None, **kwargs):
2646
+ """
2647
+ Return the subset of set partitions of a given size and
2648
+ additional keyword arguments.
2649
+
2650
+ EXAMPLES::
2651
+
2652
+ sage: P = SetPartitions()
2653
+ sage: P.subset(4)
2654
+ Set partitions of {1, 2, 3, 4}
2655
+ """
2656
+ if size is None:
2657
+ return self
2658
+ return SetPartitions(size, **kwargs)
2659
+
2660
+ def _repr_(self):
2661
+ """
2662
+ Return a string representation of ``self``.
2663
+
2664
+ EXAMPLES::
2665
+
2666
+ sage: SetPartitions()
2667
+ Set partitions
2668
+ """
2669
+ return "Set partitions"
2670
+
2671
+ def __iter__(self):
2672
+ """
2673
+ Iterate over ``self``.
2674
+
2675
+ EXAMPLES::
2676
+
2677
+ sage: it = SetPartitions().__iter__()
2678
+ sage: [next(it) for x in range(10)]
2679
+ [{}, {{1}}, {{1, 2}}, {{1}, {2}}, {{1, 2, 3}}, {{1, 2}, {3}},
2680
+ {{1, 3}, {2}}, {{1}, {2, 3}}, {{1}, {2}, {3}}, {{1, 2, 3, 4}}]
2681
+ """
2682
+ n = 0
2683
+ while True:
2684
+ for x in SetPartitions_set(frozenset(range(1, n + 1))):
2685
+ yield self.element_class(self, list(x))
2686
+ n += 1
2687
+
2688
+
2689
+ class SetPartitions_set(SetPartitions):
2690
+ """
2691
+ Set partitions of a fixed set `S`.
2692
+ """
2693
+ @staticmethod
2694
+ def __classcall_private__(cls, s):
2695
+ """
2696
+ Normalize ``s`` to ensure a unique representation.
2697
+
2698
+ EXAMPLES::
2699
+
2700
+ sage: S1 = SetPartitions(set([2,1,4]))
2701
+ sage: S2 = SetPartitions([4,1,2])
2702
+ sage: S3 = SetPartitions((1,2,4))
2703
+ sage: S1 is S2, S1 is S3
2704
+ (True, True)
2705
+ """
2706
+ return super().__classcall__(cls, frozenset(s))
2707
+
2708
+ def __init__(self, s):
2709
+ """
2710
+ Initialize ``self``.
2711
+
2712
+ EXAMPLES::
2713
+
2714
+ sage: S = SetPartitions(3)
2715
+ sage: TestSuite(S).run()
2716
+ sage: SetPartitions(0).list()
2717
+ [{}]
2718
+ sage: SetPartitions([]).list()
2719
+ [{}]
2720
+ """
2721
+ self._set = s
2722
+ SetPartitions.__init__(self, category=FiniteEnumeratedSets())
2723
+
2724
+ def _repr_(self):
2725
+ """
2726
+ TESTS::
2727
+
2728
+ sage: SetPartitions([1,2,3])
2729
+ Set partitions of {1, 2, 3}
2730
+ """
2731
+ return "Set partitions of %s" % (Set(self._set))
2732
+
2733
+ def __contains__(self, x):
2734
+ """
2735
+ TESTS::
2736
+
2737
+ sage: S = SetPartitions(4, [2,2])
2738
+ sage: all(sp in S for sp in S) # needs sage.graphs sage.rings.finite_rings
2739
+ True
2740
+ sage: SetPartition([[1,3],[2,4]]) in SetPartitions(3) # needs sage.graphs
2741
+ False
2742
+ sage: SetPartition([[1,3],[2,4]]) in SetPartitions(4, [3,1]) # needs sage.graphs
2743
+ False
2744
+ sage: SetPartition([[2],[1,3,4]]) in SetPartitions(4, [3,1]) # needs sage.graphs
2745
+ True
2746
+ """
2747
+ # Must pass the general check
2748
+ if not SetPartitions.__contains__(self, x):
2749
+ return False
2750
+
2751
+ # Make sure that the number of elements match up
2752
+ if sum(map(len, x)) != len(self._set):
2753
+ return False
2754
+
2755
+ # Make sure that the union of all the sets is the original set
2756
+ return Set(e for p in x for e in p) == Set(self._set)
2757
+
2758
+ def random_element(self):
2759
+ r"""
2760
+ Return a random set partition.
2761
+
2762
+ This is a very naive implementation of Knuths outline in F3B,
2763
+ 7.2.1.5.
2764
+
2765
+ EXAMPLES::
2766
+
2767
+ sage: S = SetPartitions(10)
2768
+ sage: s = S.random_element() # needs sage.symbolic
2769
+ sage: s.parent() is S # needs sage.symbolic
2770
+ True
2771
+ sage: assert s in S, s # needs sage.symbolic
2772
+
2773
+ sage: S = SetPartitions(["a", "b", "c"])
2774
+ sage: s = S.random_element() # needs sage.symbolic
2775
+ sage: s.parent() is S # needs sage.symbolic
2776
+ True
2777
+ sage: assert s in S, s # needs sage.symbolic
2778
+ """
2779
+ base_set = list(self.base_set())
2780
+ N = len(base_set)
2781
+ from sage.symbolic.constants import e
2782
+ c = float(e) * bell_number(N)
2783
+ # it would be much better to generate M in the way Knuth
2784
+ # recommends, the following is a waste
2785
+ G = GeneralDiscreteDistribution([float(m)**N / (c * factorial(m)) for m in range(4 * N)])
2786
+ M = G.get_random_element() - 1
2787
+ l = (randint(0, M) for i in range(N))
2788
+ p = {}
2789
+ for i, b in enumerate(l):
2790
+ if b in p:
2791
+ p[b].append(base_set[i])
2792
+ else:
2793
+ p[b] = [base_set[i]]
2794
+
2795
+ return self.element_class(self, p.values(), check=False)
2796
+
2797
+ def cardinality(self):
2798
+ """
2799
+ Return the number of set partitions of the set `S`.
2800
+
2801
+ The cardinality is given by the `n`-th Bell number where `n` is the
2802
+ number of elements in the set `S`.
2803
+
2804
+ EXAMPLES::
2805
+
2806
+ sage: # needs sage.libs.flint
2807
+ sage: SetPartitions([1,2,3,4]).cardinality()
2808
+ 15
2809
+ sage: SetPartitions(3).cardinality()
2810
+ 5
2811
+ sage: SetPartitions(3,2).cardinality()
2812
+ 3
2813
+ sage: SetPartitions([]).cardinality()
2814
+ 1
2815
+ """
2816
+ return bell_number(len(self._set))
2817
+
2818
+ def __iter__(self):
2819
+ """
2820
+ Iterate over ``self``.
2821
+
2822
+ EXAMPLES::
2823
+
2824
+ sage: SetPartitions(3).list()
2825
+ [{{1, 2, 3}}, {{1, 2}, {3}}, {{1, 3}, {2}}, {{1}, {2, 3}}, {{1}, {2}, {3}}]
2826
+
2827
+ sage: SetPartitions(["a", "b"]).list()
2828
+ [{{'a', 'b'}}, {{'a'}, {'b'}}]
2829
+ """
2830
+ try:
2831
+ s = sorted(self._set)
2832
+ except TypeError:
2833
+ s = sorted(self._set, key=str)
2834
+ for sp in set_partition_iterator(s):
2835
+ yield self.element_class(self, sp, check=False)
2836
+
2837
+ def base_set(self):
2838
+ """
2839
+ Return the base set of ``self``.
2840
+
2841
+ EXAMPLES::
2842
+
2843
+ sage: SetPartitions(3).base_set()
2844
+ {1, 2, 3}
2845
+
2846
+ sage: sorted(SetPartitions(["a", "b", "c"]).base_set())
2847
+ ['a', 'b', 'c']
2848
+ """
2849
+ return Set(self._set)
2850
+
2851
+ def base_set_cardinality(self):
2852
+ """
2853
+ Return the cardinality of the base set of ``self``.
2854
+
2855
+ EXAMPLES::
2856
+
2857
+ sage: SetPartitions(3).base_set_cardinality()
2858
+ 3
2859
+ """
2860
+ return len(self._set)
2861
+
2862
+
2863
+ class SetPartitions_setparts(SetPartitions_set):
2864
+ r"""
2865
+ Set partitions with fixed partition sizes corresponding to an
2866
+ integer partition `\lambda`.
2867
+ """
2868
+ @staticmethod
2869
+ def __classcall_private__(cls, s, parts):
2870
+ """
2871
+ Normalize input to ensure a unique representation.
2872
+
2873
+ EXAMPLES::
2874
+
2875
+ sage: S = SetPartitions(4, [2,2])
2876
+ sage: T = SetPartitions([1,2,3,4], Partition([2,2]))
2877
+ sage: S is T
2878
+ True
2879
+
2880
+ sage: S = SetPartitions(4, [3,1])
2881
+ sage: T = SetPartitions(4, (1,3))
2882
+ sage: S is T
2883
+ True
2884
+ """
2885
+ if isinstance(s, (int, Integer)):
2886
+ s = list(range(1, s + 1))
2887
+ return super().__classcall__(cls, frozenset(s), Partition(parts))
2888
+
2889
+ def __init__(self, s, parts):
2890
+ """
2891
+ Initialize the data structure.
2892
+
2893
+ We can assume here that ``parts`` is a :class:`Partition`.
2894
+
2895
+ TESTS::
2896
+
2897
+ sage: S = SetPartitions(4, [2,2])
2898
+ sage: TestSuite(S).run() # needs sage.graphs sage.libs.flint
2899
+ """
2900
+ SetPartitions_set.__init__(self, s)
2901
+ self._parts = parts
2902
+
2903
+ def _repr_(self):
2904
+ """
2905
+ TESTS::
2906
+
2907
+ sage: SetPartitions(4, [2,2])
2908
+ Set partitions of {1, 2, 3, 4} with sizes in [2, 2]
2909
+ """
2910
+ return "Set partitions of %s with sizes in %s" % (Set(self._set), self._parts)
2911
+
2912
+ def shape(self):
2913
+ r"""
2914
+ Return the partition of block sizes of the set partitions in ``self``.
2915
+
2916
+ EXAMPLES::
2917
+
2918
+ sage: SetPartitions(5, [2,2,1]).shape()
2919
+ [2, 2, 1]
2920
+ """
2921
+ return self._parts
2922
+
2923
+ def cardinality(self):
2924
+ r"""
2925
+ Return the cardinality of ``self``.
2926
+
2927
+ This algorithm counts for each block of the partition the
2928
+ number of ways to fill it using values from the set. Then,
2929
+ for each distinct value `v` of block size, we divide the result by
2930
+ the number of ways to arrange the blocks of size `v` in the
2931
+ set partition.
2932
+
2933
+ For example, if we want to count the number of set partitions
2934
+ of size 13 having [3,3,3,2,2] as underlying partition we
2935
+ compute the number of ways to fill each block of the
2936
+ partition, which is `\binom{13}{3} \binom{10}{3} \binom{7}{3}
2937
+ \binom{4}{2}\binom{2}{2}` and as we have three blocks of size
2938
+ `3` and two blocks of size `2`, we divide the result by
2939
+ `3!2!` which gives us `600600`.
2940
+
2941
+ EXAMPLES::
2942
+
2943
+ sage: SetPartitions(3, [2,1]).cardinality()
2944
+ 3
2945
+ sage: SetPartitions(13, Partition([3,3,3,2,2])).cardinality()
2946
+ 600600
2947
+
2948
+ TESTS::
2949
+
2950
+ sage: all((len(SetPartitions(size, part)) == SetPartitions(size, part).cardinality()
2951
+ ....: for size in range(8) for part in Partitions(size)))
2952
+ True
2953
+ sage: sum((SetPartitions(13, p).cardinality() # needs sage.libs.flint
2954
+ ....: for p in Partitions(13))) == SetPartitions(13).cardinality()
2955
+ True
2956
+ """
2957
+ from sage.misc.misc_c import prod
2958
+
2959
+ remaining_subset_size = Integer(len(self._set))
2960
+ cardinal = Integer(1)
2961
+ for subset_size in self._parts:
2962
+ cardinal *= remaining_subset_size.binomial(subset_size)
2963
+ remaining_subset_size -= subset_size
2964
+
2965
+ repetitions = (Integer(rep).factorial()
2966
+ for rep in self._parts.to_exp_dict().values()
2967
+ if rep != 1)
2968
+ cardinal /= prod(repetitions)
2969
+ return Integer(cardinal)
2970
+
2971
+ def _set_partition_poset(self):
2972
+ r"""
2973
+ Return the Hasse diagram of a poset whose linear extensions correspond
2974
+ to the set partitions with specified block sizes.
2975
+
2976
+ TESTS::
2977
+
2978
+ sage: P = SetPartitions(["a", "b", "c", "d", "e"], # needs sage.graphs
2979
+ ....: [2,2,1])._set_partition_poset()
2980
+ sage: P.cover_relations() # needs sage.graphs
2981
+ [(1, 2), (1, 3), (3, 4)]
2982
+
2983
+ sage: n = 9
2984
+ sage: all(SetPartitions(n, mu).cardinality() == # needs sage.graphs sage.modules
2985
+ ....: len(list(SetPartitions(n, mu)._set_partition_poset().linear_extensions()))
2986
+ ....: for mu in Partitions(n))
2987
+ True
2988
+ """
2989
+ c = self._parts.to_exp_dict()
2990
+ covers = {}
2991
+ i = 0
2992
+ for s in sorted(c):
2993
+ # s is the block size
2994
+ # each block is one tree in the poset
2995
+ for m in range(c[s]):
2996
+ # m is the multiplicity of blocks with size s
2997
+ #
2998
+ # the first element in each non-final block has an
2999
+ # additional cover
3000
+ first = i
3001
+ if s == 1:
3002
+ covers[i] = []
3003
+ else:
3004
+ for _ in range(s - 1):
3005
+ covers[i] = [i + 1]
3006
+ i += 1
3007
+ i += 1
3008
+ if m < c[s] - 1:
3009
+ covers[first].append(i)
3010
+ return HasseDiagram(covers)
3011
+
3012
+ def __iter__(self):
3013
+ """
3014
+ An iterator for all the set partitions of the given set with
3015
+ the given sizes.
3016
+
3017
+ EXAMPLES::
3018
+
3019
+ sage: SetPartitions(3, [2,1]).list() # needs sage.graphs sage.rings.finite_rings
3020
+ [{{1}, {2, 3}}, {{1, 2}, {3}}, {{1, 3}, {2}}]
3021
+
3022
+ sage: SetPartitions(["a", "b", "c"], [2,1]).list() # needs sage.graphs sage.rings.finite_rings
3023
+ [{{'a'}, {'b', 'c'}}, {{'a', 'b'}, {'c'}}, {{'a', 'c'}, {'b'}}]
3024
+
3025
+ TESTS::
3026
+
3027
+ sage: n = 8
3028
+ sage: all(SetPartitions(n, mu).cardinality() # needs sage.graphs sage.rings.finite_rings
3029
+ ....: == len(list(SetPartitions(n, mu))) for mu in Partitions(n))
3030
+ True
3031
+ """
3032
+ # Ruskey, Combinatorial Generation, sec. 5.10.1 and Knuth TAOCP 4A 7.2.1.5, Exercise 6
3033
+ k = len(self._parts)
3034
+ n = len(self._set)
3035
+ P = self._set_partition_poset()
3036
+ try:
3037
+ s = sorted(self._set)
3038
+ except TypeError:
3039
+ s = sorted(self._set, key=str)
3040
+
3041
+ sums = [0]
3042
+ for b in sorted(self._parts):
3043
+ sums.append(sums[-1] + b)
3044
+
3045
+ for ext in P.linear_extensions():
3046
+ pi = [None] * n
3047
+ for i in range(n):
3048
+ pi[ext[i]] = s[i]
3049
+ sp = [[pi[j] for j in range(sums[i], sums[i + 1])]
3050
+ for i in range(k)]
3051
+ yield self.element_class(self, sp, check=False)
3052
+
3053
+ def __contains__(self, x):
3054
+ """
3055
+ Check containment.
3056
+
3057
+ TESTS::
3058
+
3059
+ sage: S = SetPartitions(4, [3,1])
3060
+ sage: Set([Set([1,2,3]), Set([4])]) in S
3061
+ True
3062
+ sage: Set([Set([1,3]), Set([2,4])]) in S
3063
+ False
3064
+ sage: Set([Set([1,2,3,4])]) in S
3065
+ False
3066
+ """
3067
+ if not SetPartitions_set.__contains__(self, x):
3068
+ return False
3069
+ return sorted(map(len, x), reverse=True) == self._parts
3070
+
3071
+ def random_element(self):
3072
+ r"""
3073
+ Return a random set partition of ``self``.
3074
+
3075
+ ALGORITHM:
3076
+
3077
+ Based on the cardinality method. For each block size `k_i`,
3078
+ we choose a uniformly random subset `X_i \subseteq S_i` of
3079
+ size `k_i` of the elements `S_i` that have not yet been selected.
3080
+ Thus, we define `S_{i+1} = S_i \setminus X_i` with `S_i = S`
3081
+ being the defining set. This is not yet proven to be uniformly
3082
+ distributed, but numerical tests show this is likely uniform.
3083
+
3084
+ EXAMPLES::
3085
+
3086
+ sage: S = SetPartitions(10, [4,3,2,1])
3087
+ sage: s = S.random_element()
3088
+ sage: s.parent() is S
3089
+ True
3090
+ sage: assert s in S, s
3091
+
3092
+ sage: S = SetPartitions(["a", "b", "c", "d"], [2,2])
3093
+ sage: s = S.random_element()
3094
+ sage: s.parent() is S
3095
+ True
3096
+ sage: assert s in S, s
3097
+ """
3098
+ base_set = list(self.base_set())
3099
+ N = len(base_set)
3100
+ ret = []
3101
+ for p in self._parts:
3102
+ X = sample(range(N), p)
3103
+ ret.append([base_set[i] for i in X])
3104
+ for i in sorted(X, reverse=True):
3105
+ del base_set[i]
3106
+ N -= p
3107
+
3108
+ return self.element_class(self, ret, check=False)
3109
+
3110
+
3111
+ class SetPartitions_setn(SetPartitions_set):
3112
+ """
3113
+ Set partitions with a given number of blocks.
3114
+ """
3115
+ @staticmethod
3116
+ def __classcall_private__(cls, s, k):
3117
+ """
3118
+ Normalize ``s`` to ensure a unique representation.
3119
+
3120
+ EXAMPLES::
3121
+
3122
+ sage: S1 = SetPartitions(set([2,1,4]), 2)
3123
+ sage: S2 = SetPartitions([4,1,2], 2)
3124
+ sage: S3 = SetPartitions((1,2,4), 2)
3125
+ sage: S1 is S2, S1 is S3
3126
+ (True, True)
3127
+ """
3128
+ return super().__classcall__(cls, frozenset(s), k)
3129
+
3130
+ def __init__(self, s, k):
3131
+ """
3132
+ TESTS::
3133
+
3134
+ sage: S = SetPartitions(5, 3)
3135
+ sage: TestSuite(S).run()
3136
+ """
3137
+ self._k = k
3138
+ SetPartitions_set.__init__(self, s)
3139
+
3140
+ def _repr_(self):
3141
+ """
3142
+ TESTS::
3143
+
3144
+ sage: SetPartitions(5, 3)
3145
+ Set partitions of {1, 2, 3, 4, 5} with 3 parts
3146
+ """
3147
+ return "Set partitions of %s with %s parts" % (Set(self._set), self._k)
3148
+
3149
+ def number_of_blocks(self):
3150
+ r"""
3151
+ Return the number of blocks of the set partitions in ``self``.
3152
+
3153
+ EXAMPLES::
3154
+
3155
+ sage: SetPartitions(5, 3).number_of_blocks()
3156
+ 3
3157
+ """
3158
+ return self._k
3159
+
3160
+ def cardinality(self):
3161
+ """
3162
+ The Stirling number of the second kind is the number of partitions
3163
+ of a set of size `n` into `k` blocks.
3164
+
3165
+ EXAMPLES::
3166
+
3167
+ sage: SetPartitions(5, 3).cardinality()
3168
+ 25
3169
+ sage: stirling_number2(5,3)
3170
+ 25
3171
+ """
3172
+ return stirling2(len(self._set), self._k)
3173
+
3174
+ def __iter__(self):
3175
+ """
3176
+ Iterate over ``self``.
3177
+
3178
+ EXAMPLES::
3179
+
3180
+ sage: SetPartitions(4, 2).list()
3181
+ [{{1, 3, 4}, {2}},
3182
+ {{1, 4}, {2, 3}},
3183
+ {{1, 2, 4}, {3}},
3184
+ {{1, 3}, {2, 4}},
3185
+ {{1}, {2, 3, 4}},
3186
+ {{1, 2}, {3, 4}},
3187
+ {{1, 2, 3}, {4}}]
3188
+
3189
+ sage: SetPartitions(["a", "b", "c"], 2).list()
3190
+ [{{'a', 'c'}, {'b'}}, {{'a'}, {'b', 'c'}}, {{'a', 'b'}, {'c'}}]
3191
+ """
3192
+ try:
3193
+ s = sorted(self._set)
3194
+ except TypeError:
3195
+ s = sorted(self._set, key=str)
3196
+ for sp in set_partition_iterator_blocks(s, self._k):
3197
+ yield self.element_class(self, sp, check=False)
3198
+
3199
+ def __contains__(self, x):
3200
+ """
3201
+ Check containment.
3202
+
3203
+ TESTS::
3204
+
3205
+ sage: S = SetPartitions(4, 2)
3206
+ sage: Set([Set([1,2,3]), Set([4])]) in S
3207
+ True
3208
+ sage: Set([Set([1,3]), Set([2,4])]) in S
3209
+ True
3210
+ sage: Set([Set([1,2,3,4])]) in S
3211
+ False
3212
+ """
3213
+ if not SetPartitions_set.__contains__(self, x):
3214
+ return False
3215
+ return len(x) == self._k
3216
+
3217
+ def random_element(self):
3218
+ r"""
3219
+ Return a random set partition of ``self``.
3220
+
3221
+ See https://mathoverflow.net/questions/141999.
3222
+
3223
+ EXAMPLES::
3224
+
3225
+ sage: S = SetPartitions(10, 4)
3226
+ sage: s = S.random_element()
3227
+ sage: s.parent() is S
3228
+ True
3229
+ sage: assert s in S, s
3230
+
3231
+ sage: S = SetPartitions(["a", "b", "c"], 2)
3232
+ sage: s = S.random_element()
3233
+ sage: s.parent() is S
3234
+ True
3235
+ sage: assert s in S, s
3236
+ """
3237
+ def re(N, k):
3238
+ if N == 0:
3239
+ return [[]]
3240
+ if N == 1:
3241
+ return [[0]]
3242
+ if stirling2(N - 1, k - 1) > random() * stirling2(N, k):
3243
+ return [[N - 1]] + re(N - 1, k - 1)
3244
+
3245
+ p = re(N - 1, k)
3246
+ p[randint(0, len(p) - 1)].append(N - 1)
3247
+ return p
3248
+
3249
+ base_set = list(self.base_set())
3250
+ N = len(base_set)
3251
+ k = self._k
3252
+ p = re(N, k)
3253
+ return self.element_class(self, [[base_set[e] for e in b] for b in p], check=False)
3254
+
3255
+
3256
+ def cyclic_permutations_of_set_partition(set_part):
3257
+ """
3258
+ Return all combinations of cyclic permutations of each cell of the
3259
+ set partition.
3260
+
3261
+ AUTHORS:
3262
+
3263
+ - Robert L. Miller
3264
+
3265
+ EXAMPLES::
3266
+
3267
+ sage: from sage.combinat.set_partition import cyclic_permutations_of_set_partition
3268
+ sage: cyclic_permutations_of_set_partition([[1,2,3,4],[5,6,7]])
3269
+ [[[1, 2, 3, 4], [5, 6, 7]],
3270
+ [[1, 2, 4, 3], [5, 6, 7]],
3271
+ [[1, 3, 2, 4], [5, 6, 7]],
3272
+ [[1, 3, 4, 2], [5, 6, 7]],
3273
+ [[1, 4, 2, 3], [5, 6, 7]],
3274
+ [[1, 4, 3, 2], [5, 6, 7]],
3275
+ [[1, 2, 3, 4], [5, 7, 6]],
3276
+ [[1, 2, 4, 3], [5, 7, 6]],
3277
+ [[1, 3, 2, 4], [5, 7, 6]],
3278
+ [[1, 3, 4, 2], [5, 7, 6]],
3279
+ [[1, 4, 2, 3], [5, 7, 6]],
3280
+ [[1, 4, 3, 2], [5, 7, 6]]]
3281
+ """
3282
+ return list(cyclic_permutations_of_set_partition_iterator(set_part))
3283
+
3284
+
3285
+ def cyclic_permutations_of_set_partition_iterator(set_part):
3286
+ """
3287
+ Iterate over all combinations of cyclic permutations of each cell
3288
+ of the set partition.
3289
+
3290
+ AUTHORS:
3291
+
3292
+ - Robert L. Miller
3293
+
3294
+ EXAMPLES::
3295
+
3296
+ sage: from sage.combinat.set_partition import cyclic_permutations_of_set_partition_iterator
3297
+ sage: list(cyclic_permutations_of_set_partition_iterator([[1,2,3,4],[5,6,7]]))
3298
+ [[[1, 2, 3, 4], [5, 6, 7]],
3299
+ [[1, 2, 4, 3], [5, 6, 7]],
3300
+ [[1, 3, 2, 4], [5, 6, 7]],
3301
+ [[1, 3, 4, 2], [5, 6, 7]],
3302
+ [[1, 4, 2, 3], [5, 6, 7]],
3303
+ [[1, 4, 3, 2], [5, 6, 7]],
3304
+ [[1, 2, 3, 4], [5, 7, 6]],
3305
+ [[1, 2, 4, 3], [5, 7, 6]],
3306
+ [[1, 3, 2, 4], [5, 7, 6]],
3307
+ [[1, 3, 4, 2], [5, 7, 6]],
3308
+ [[1, 4, 2, 3], [5, 7, 6]],
3309
+ [[1, 4, 3, 2], [5, 7, 6]]]
3310
+ """
3311
+ from sage.combinat.permutation import CyclicPermutations
3312
+ if len(set_part) == 1:
3313
+ for i in CyclicPermutations(set_part[0]):
3314
+ yield [i]
3315
+ else:
3316
+ for right in cyclic_permutations_of_set_partition_iterator(set_part[1:]):
3317
+ for perm in CyclicPermutations(set_part[0]):
3318
+ yield [perm] + right