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,3541 @@
1
+ # sage_setup: distribution = sagemath-combinat
2
+ r"""
3
+ Ordered multiset partitions into sets and the minimaj crystal
4
+
5
+ This module provides element and parent classes for ordered multiset
6
+ partitions. It also implements the minimaj crystal of Benkart et al.
7
+ [BCHOPSY2017]_. (See :class:`MinimajCrystal`.)
8
+
9
+ REFERENCES:
10
+
11
+ - [BCHOPSY2017]_
12
+ - [HRW2015]_
13
+ - [HRS2016]_
14
+ - [LM2018]_
15
+
16
+ EXAMPLES:
17
+
18
+ An ordered multiset partition into sets of the multiset `\{\{1, 3, 3, 5\}\}`::
19
+
20
+ sage: OrderedMultisetPartitionIntoSets([[5, 3], [1, 3]])
21
+ [{3,5}, {1,3}]
22
+
23
+ Ordered multiset partitions into sets of the multiset `\{\{1, 3, 3\}\}`::
24
+
25
+ sage: OrderedMultisetPartitionsIntoSets([1,1,3]).list()
26
+ [[{1}, {1}, {3}], [{1}, {1,3}], [{1}, {3}, {1}], [{1,3}, {1}], [{3}, {1}, {1}]]
27
+
28
+ Ordered multiset partitions into sets of the integer 4::
29
+
30
+ sage: OrderedMultisetPartitionsIntoSets(4).list()
31
+ [[{4}], [{1,3}], [{3}, {1}], [{1,2}, {1}], [{2}, {2}], [{2}, {1}, {1}],
32
+ [{1}, {3}], [{1}, {1,2}], [{1}, {2}, {1}], [{1}, {1}, {2}], [{1}, {1}, {1}, {1}]]
33
+
34
+ Ordered multiset partitions into sets on the alphabet `\{1, 4\}` of order 3::
35
+
36
+ sage: OrderedMultisetPartitionsIntoSets([1,4], 3).list()
37
+ [[{1,4}, {1}], [{1,4}, {4}], [{1}, {1,4}], [{4}, {1,4}], [{1}, {1}, {1}],
38
+ [{1}, {1}, {4}], [{1}, {4}, {1}], [{1}, {4}, {4}], [{4}, {1}, {1}],
39
+ [{4}, {1}, {4}], [{4}, {4}, {1}], [{4}, {4}, {4}]]
40
+
41
+ Crystal of ordered multiset partitions into sets on the alphabet `\{1,2,3\}`
42
+ with 4 letters divided into 2 blocks::
43
+
44
+ sage: crystals.Minimaj(3, 4, 2).list() # needs sage.modules
45
+ [((2, 3, 1), (1,)), ((2, 3), (1, 2)), ((2, 3), (1, 3)), ((2, 1), (1, 2)),
46
+ ((3, 1), (1, 2)), ((3, 1, 2), (2,)), ((3, 1), (1, 3)), ((3, 1), (2, 3)),
47
+ ((3, 2), (2, 3)), ((2, 1), (1, 3)), ((2,), (1, 2, 3)), ((3,), (1, 2, 3)),
48
+ ((1,), (1, 2, 3)), ((1, 2), (2, 3)), ((1, 2, 3), (3,))]
49
+
50
+ AUTHORS:
51
+
52
+ - Aaron Lauve (2018): initial implementation. First draft of minimaj crystal
53
+ code provided by Anne Schilling.
54
+ """
55
+
56
+ # ****************************************************************************
57
+ # Copyright (C) 2018 Aaron Lauve <lauve at math.luc.edu>
58
+ #
59
+ # Distributed under the terms of the GNU General Public License (GPL)
60
+ # as published by the Free Software Foundation; either version 2 of
61
+ # the License, or (at your option) any later version.
62
+ # https://www.gnu.org/licenses/
63
+ # ****************************************************************************
64
+ from functools import reduce
65
+ from itertools import chain, product
66
+
67
+ from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets
68
+ from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
69
+ from sage.categories.classical_crystals import ClassicalCrystals
70
+ from sage.categories.tensor import tensor
71
+ from sage.structure.unique_representation import UniqueRepresentation
72
+ from sage.structure.list_clone import ClonableArray
73
+ from sage.structure.parent import Parent
74
+ from sage.structure.element_wrapper import ElementWrapper
75
+ from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass
76
+ from sage.misc.misc_c import prod, running_total
77
+ from sage.misc.latex import latex
78
+ from sage.misc.lazy_import import lazy_import
79
+ from sage.sets.set import Set_object
80
+ from sage.rings.infinity import infinity
81
+ from sage.rings.integer_ring import ZZ
82
+ from sage.rings.power_series_ring import PowerSeriesRing
83
+ from sage.arith.misc import binomial
84
+
85
+ from sage.combinat.subset import Subsets_sk
86
+ from sage.combinat.composition import Composition, Compositions, composition_iterator_fast
87
+ from sage.combinat.permutation import Permutations_mset
88
+ from sage.combinat.integer_lists.invlex import IntegerListsLex
89
+ from sage.combinat.combinatorial_map import combinatorial_map
90
+ from sage.combinat.shuffle import ShuffleProduct, ShuffleProduct_overlapping
91
+
92
+ lazy_import('sage.combinat.crystals.letters', 'CrystalOfLetters', as_='Letters')
93
+ lazy_import('sage.combinat.root_system.cartan_type', 'CartanType')
94
+ lazy_import('sage.combinat.sf.sf', 'SymmetricFunctions')
95
+
96
+
97
+ class OrderedMultisetPartitionIntoSets(ClonableArray,
98
+ metaclass=InheritComparisonClasscallMetaclass):
99
+ r"""
100
+ Ordered Multiset Partition into sets.
101
+
102
+ An *ordered multiset partition into sets* `c` of a multiset `X` is a list
103
+ `[c_1, \ldots, c_r]` of nonempty subsets of `X` (note: not
104
+ sub-multisets), called the *blocks* of `c`, whose multi-union is `X`.
105
+
106
+ EXAMPLES:
107
+
108
+ The simplest way to create an ordered multiset partition into sets is by
109
+ specifying its blocks as a list or tuple::
110
+
111
+ sage: OrderedMultisetPartitionIntoSets([[3],[2,1]])
112
+ [{3}, {1,2}]
113
+ sage: OrderedMultisetPartitionIntoSets(((3,), (1,2)))
114
+ [{3}, {1,2}]
115
+ sage: OrderedMultisetPartitionIntoSets([set([i]) for i in range(2,5)])
116
+ [{2}, {3}, {4}]
117
+
118
+ REFERENCES:
119
+
120
+ - [HRW2015]_
121
+ - [HRS2016]_
122
+ - [LM2018]_
123
+ """
124
+ @staticmethod
125
+ def __classcall_private__(cls, co):
126
+ """
127
+ Create an ordered multiset partition into sets (i.e., a list of sets)
128
+ from the passed arguments with the appropriate parent.
129
+
130
+ EXAMPLES::
131
+
132
+ sage: OrderedMultisetPartitionIntoSets([[3], [2,1]])
133
+ [{3}, {1,2}]
134
+ sage: c = OrderedMultisetPartitionsIntoSets()([{2}, {3}, {4}, {5}]); c
135
+ [{2}, {3}, {4}, {5}]
136
+ sage: d = OrderedMultisetPartitionsIntoSets((1,1,1,2,3,5))([{1}, {5, 1, 3}, {2, 1}]); d
137
+ [{1}, {1,3,5}, {1,2}]
138
+
139
+ TESTS::
140
+
141
+ sage: c.parent() == OrderedMultisetPartitionsIntoSets([2,3,4,5])
142
+ False
143
+ sage: d.parent() == OrderedMultisetPartitionsIntoSets([1,1,1,2,3,5])
144
+ True
145
+ sage: repr(OrderedMultisetPartitionIntoSets([]).parent())
146
+ 'Ordered Multiset Partitions into Sets of multiset {{}}'
147
+ """
148
+ if not co:
149
+ P = OrderedMultisetPartitionsIntoSets([])
150
+ return P.element_class(P, [])
151
+ else:
152
+ X = _concatenate(co)
153
+ P = OrderedMultisetPartitionsIntoSets(_get_weight(X))
154
+ return P.element_class(P, co)
155
+
156
+ def __init__(self, parent, data):
157
+ """
158
+ Initialize ``self``.
159
+
160
+ EXAMPLES::
161
+
162
+ sage: c = OrderedMultisetPartitionsIntoSets(7)([[1,3], [1,2]])
163
+ sage: OrderedMultisetPartitionIntoSets([[1,3], [1,2]]) == c
164
+ True
165
+ sage: c.weight()
166
+ {1: 2, 2: 1, 3: 1}
167
+
168
+ TESTS::
169
+
170
+ sage: OMP = OrderedMultisetPartitionIntoSets
171
+ sage: c0 = OMP([])
172
+ sage: OMP([[]]) == c0
173
+ True
174
+ sage: TestSuite(c0).run()
175
+
176
+ sage: d = OMP([[1, 3], [1, 'a', 'b']])
177
+ sage: TestSuite(d).run()
178
+
179
+ sage: OMPs = OrderedMultisetPartitionsIntoSets()
180
+ sage: d = OMPs([['a','b','c'],['a','b'],['a']])
181
+ sage: TestSuite(d).run()
182
+
183
+ sage: c.size() == 7
184
+ True
185
+ sage: d.size() == None
186
+ True
187
+ """
188
+ # Delete empty blocks
189
+ co = [block for block in data if block]
190
+ if not _has_nonempty_sets(co):
191
+ raise ValueError("cannot view %s as an ordered partition of %s" % (co, parent._Xtup))
192
+
193
+ ClonableArray.__init__(self, parent, [frozenset(k) for k in co])
194
+ self._multiset = _get_multiset(co)
195
+ self._weight = _get_weight(self._multiset)
196
+ self._order = sum(len(block) for block in self)
197
+ if all((a in ZZ and a > 0) for a in self._multiset):
198
+ self._n = ZZ(sum(self._multiset))
199
+ else:
200
+ self._n = None
201
+
202
+ def check(self):
203
+ """
204
+ Check that we are a valid ordered multiset partition into sets.
205
+
206
+ EXAMPLES::
207
+
208
+ sage: c = OrderedMultisetPartitionsIntoSets(4)([[1], [1,2]])
209
+ sage: c.check()
210
+
211
+ sage: OMPs = OrderedMultisetPartitionsIntoSets()
212
+ sage: c = OMPs([[1], [1], ['a']])
213
+ sage: c.check()
214
+
215
+ TESTS::
216
+
217
+ sage: c = OMPs([[1, 1], [1, 4]])
218
+ Traceback (most recent call last):
219
+ ...
220
+ ValueError: cannot convert [[1, 1], [1, 4]] into an element
221
+ of Ordered Multiset Partitions into Sets
222
+ """
223
+ if self not in self.parent():
224
+ raise ValueError("{} not an element of {}".format(self, self.parent()))
225
+
226
+ def _repr_(self):
227
+ """
228
+ Return a string representation of ``self``.
229
+
230
+ EXAMPLES::
231
+
232
+ sage: A = OrderedMultisetPartitionIntoSets([[4], [1,2,4], [2,3], [1]])
233
+ sage: A
234
+ [{4}, {1,2,4}, {2,3}, {1}]
235
+ """
236
+ return self._repr_tight()
237
+
238
+ def _repr_normal(self):
239
+ r"""
240
+ Viewing ``self`` as a list `[A_1, \ldots, A_r]` of sets,
241
+ return the standard Sage string representation of `[A_1, \ldots, A_r]`.
242
+
243
+ EXAMPLES::
244
+
245
+ sage: OrderedMultisetPartitionIntoSets([[4,1,3], [3,2,5]])._repr_normal()
246
+ '[{1, 3, 4}, {2, 3, 5}]'
247
+ sage: OrderedMultisetPartitionIntoSets([[4,1,3,11], [3,'a',5]])._repr_normal()
248
+ "[{1, 11, 3, 4}, {3, 5, 'a'}]"
249
+ """
250
+ # TODO: simplify if/once ``_repr_`` method for ``Set`` sorts its elements.
251
+ if self._n:
252
+ string_parts = (str(sorted(k)) for k in self)
253
+ else:
254
+ string_parts = (str(sorted(k, key=str)) for k in self)
255
+ string = ", ".join(string_parts).replace("[", "{").replace("]", "}")
256
+ return "[" + string + "]"
257
+
258
+ def _repr_tight(self):
259
+ r"""
260
+ Starting from the standard Sage string representation of ``self``
261
+ as a list `[A_1, \ldots, A_r]` of sets, return the shorter string
262
+ gotten by deleting spaces within ``repr(A_i)``.
263
+
264
+ EXAMPLES::
265
+
266
+ sage: A = OrderedMultisetPartitionIntoSets([[4], [1,2,4], [2,3], [1]])
267
+ sage: A._repr_normal()
268
+ '[{4}, {1, 2, 4}, {2, 3}, {1}]'
269
+ sage: A._repr_tight()
270
+ '[{4}, {1,2,4}, {2,3}, {1}]'
271
+ """
272
+ repr = self._repr_normal()
273
+ # eliminate spacing within blocks
274
+ return repr.replace(", ", ",").replace("},{", "}, {")
275
+
276
+ def __hash__(self):
277
+ """
278
+ Return the hash of ``self``.
279
+
280
+ The parent is not included as part of the hash.
281
+
282
+ EXAMPLES::
283
+
284
+ sage: OMP = OrderedMultisetPartitionsIntoSets(4)
285
+ sage: A = OMP([[1], [1, 2]])
286
+ sage: B = OMP([{1}, {1, 2}])
287
+ sage: hash(A) == hash(B)
288
+ True
289
+ """
290
+ return sum(hash(x) for x in self)
291
+
292
+ def __eq__(self, y):
293
+ """
294
+ Check equality of ``self`` and ``y``.
295
+
296
+ The parent is not included as part of the equality check.
297
+
298
+ TESTS::
299
+
300
+ sage: OMP_n = OrderedMultisetPartitionsIntoSets(4)
301
+ sage: OMP_X = OrderedMultisetPartitionsIntoSets([1,1,2])
302
+ sage: OMP_Ad = OrderedMultisetPartitionsIntoSets(2, 3)
303
+ sage: mu = [[1], [1, 2]]
304
+ sage: OMP_n(mu) == OMP_X(mu) == OMP_Ad(mu)
305
+ True
306
+ sage: OMP_n(mu) == mu
307
+ False
308
+ sage: OMP_n(mu) == OMP_n([{1}, {3}])
309
+ False
310
+ sage: OMP_n(mu) == OMP_X([[1], [1,2]])
311
+ True
312
+ """
313
+ if not isinstance(y, OrderedMultisetPartitionIntoSets):
314
+ return False
315
+ return list(self) == list(y)
316
+
317
+ def __ne__(self, y):
318
+ """
319
+ Check lack of equality of ``self`` and ``y``.
320
+
321
+ The parent is not included as part of the equality check.
322
+
323
+ TESTS::
324
+
325
+ sage: OMP = OrderedMultisetPartitionsIntoSets(4)
326
+ sage: mu = [[1], [1, 2]]
327
+ sage: OMP(mu).__ne__(mu)
328
+ True
329
+ sage: nu = [[1], [2], [1]]
330
+ sage: OMP(mu).__ne__(OMP(nu))
331
+ True
332
+ """
333
+ return not (self == y)
334
+
335
+ def __add__(self, other):
336
+ """
337
+ Return the concatenation of two ordered multiset partitions into sets.
338
+
339
+ This operation represents the product in Hopf algebra of ordered multiset
340
+ partitions into sets in its natural basis [LM2018]_.
341
+
342
+ EXAMPLES::
343
+
344
+ sage: OMP = OrderedMultisetPartitionIntoSets
345
+ sage: OMP([[1],[1],[1,3]]) + OMP([[4,1],[2]])
346
+ [{1}, {1}, {1,3}, {1,4}, {2}]
347
+
348
+ TESTS::
349
+
350
+ sage: OMP([]) + OMP([]) == OMP([])
351
+ True
352
+ sage: OMP([[1],[1],[1,3]]) + OMP([]) == OMP([[1],[1],[1,3]])
353
+ True
354
+ """
355
+ co = list(self) + list(other)
356
+ X = _concatenate(co)
357
+ return OrderedMultisetPartitionsIntoSets(_get_weight(X))(co)
358
+
359
+ @combinatorial_map(order=2, name='reversal')
360
+ def reversal(self):
361
+ r"""
362
+ Return the reverse ordered multiset partition into sets of ``self``.
363
+
364
+ Given an ordered multiset partition into sets `(B_1, B_2, \ldots, B_k)`,
365
+ its reversal is defined to be the ordered multiset partition into sets
366
+ `(B_k, \ldots, B_2, B_1)`.
367
+
368
+ EXAMPLES::
369
+
370
+ sage: C = OrderedMultisetPartitionIntoSets([[1], [1, 3], [2, 3, 4]]); C
371
+ [{1}, {1,3}, {2,3,4}]
372
+ sage: C.reversal()
373
+ [{2,3,4}, {1,3}, {1}]
374
+ """
375
+ return self.parent()(list(reversed(self)))
376
+
377
+ def shape_from_cardinality(self):
378
+ """
379
+ Return a composition that records the cardinality of each block of ``self``.
380
+
381
+ EXAMPLES::
382
+
383
+ sage: C = OrderedMultisetPartitionIntoSets([[3, 4, 1], [2], [1, 2, 3, 7]]); C
384
+ [{1,3,4}, {2}, {1,2,3,7}]
385
+ sage: C.shape_from_cardinality()
386
+ [3, 1, 4]
387
+ sage: OrderedMultisetPartitionIntoSets([]).shape_from_cardinality() == Composition([])
388
+ True
389
+ """
390
+ return Composition([len(k) for k in self])
391
+
392
+ def shape_from_size(self):
393
+ """
394
+ Return a composition that records the sum of entries of each
395
+ block of ``self``.
396
+
397
+ EXAMPLES::
398
+
399
+ sage: C = OrderedMultisetPartitionIntoSets([[3, 4, 1], [2], [1, 2, 3, 7]]); C
400
+ [{1,3,4}, {2}, {1,2,3,7}]
401
+ sage: C.shape_from_size()
402
+ [8, 2, 13]
403
+
404
+ TESTS::
405
+
406
+ sage: OrderedMultisetPartitionIntoSets([]).shape_from_size() == Composition([])
407
+ True
408
+ sage: D = OrderedMultisetPartitionIntoSets([['a', 'b'], ['a']]); D
409
+ [{'a','b'}, {'a'}]
410
+ sage: D.shape_from_size() == None
411
+ True
412
+ """
413
+ if self._n is not None:
414
+ return Composition([sum(k) for k in self])
415
+
416
+ def letters(self):
417
+ """
418
+ Return the set of distinct elements occurring within the blocks
419
+ of ``self``.
420
+
421
+ EXAMPLES::
422
+
423
+ sage: C = OrderedMultisetPartitionIntoSets([[3, 4, 1], [2], [1, 2, 3, 7]]); C
424
+ [{1,3,4}, {2}, {1,2,3,7}]
425
+ sage: C.letters()
426
+ frozenset({1, 2, 3, 4, 7})
427
+ """
428
+ return _union_of_sets(list(self))
429
+
430
+ def multiset(self, as_dict=False):
431
+ """
432
+ Return the multiset corresponding to ``self``.
433
+
434
+ INPUT:
435
+
436
+ - ``as_dict`` -- boolean (default: ``False``); whether to return the multiset
437
+ as a tuple of a dict of multiplicities
438
+
439
+ EXAMPLES::
440
+
441
+ sage: C = OrderedMultisetPartitionIntoSets([[3, 4, 1], [2], [1, 2, 3, 7]]); C
442
+ [{1,3,4}, {2}, {1,2,3,7}]
443
+ sage: C.multiset()
444
+ (1, 1, 2, 2, 3, 3, 4, 7)
445
+ sage: C.multiset(as_dict=True)
446
+ {1: 2, 2: 2, 3: 2, 4: 1, 7: 1}
447
+ sage: OrderedMultisetPartitionIntoSets([]).multiset() == ()
448
+ True
449
+ """
450
+ if as_dict:
451
+ return self._weight
452
+ else:
453
+ return self._multiset
454
+
455
+ def max_letter(self):
456
+ """
457
+ Return the maximum letter appearing in ``self.letters()`` of ``self``.
458
+
459
+ EXAMPLES::
460
+
461
+ sage: C = OrderedMultisetPartitionIntoSets([[3, 4, 1], [2], [1, 2, 3, 7]])
462
+ sage: C.max_letter()
463
+ 7
464
+ sage: D = OrderedMultisetPartitionIntoSets([['a','b','c'],['a','b'],['a'],['b','c','f'],['c','d']])
465
+ sage: D.max_letter()
466
+ 'f'
467
+ sage: C = OrderedMultisetPartitionIntoSets([])
468
+ sage: C.max_letter()
469
+ """
470
+ if not self.letters():
471
+ return None
472
+ else:
473
+ return max(self.letters())
474
+
475
+ def size(self):
476
+ """
477
+ Return the size of ``self`` (that is, the sum of all integers in
478
+ all blocks) if ``self`` is a list of subsets of positive integers.
479
+
480
+ Else, return ``None``.
481
+
482
+ EXAMPLES::
483
+
484
+ sage: C = OrderedMultisetPartitionIntoSets([[3, 4, 1], [2], [1, 2, 3, 7]]); C
485
+ [{1,3,4}, {2}, {1,2,3,7}]
486
+ sage: C.size()
487
+ 23
488
+ sage: C.size() == sum(k for k in C.shape_from_size())
489
+ True
490
+ sage: OrderedMultisetPartitionIntoSets([[7,1],[3]]).size()
491
+ 11
492
+
493
+ TESTS::
494
+
495
+ sage: OrderedMultisetPartitionIntoSets([]).size() == 0
496
+ True
497
+ sage: OrderedMultisetPartitionIntoSets([['a','b'],['a','b','c']]).size() is None
498
+ True
499
+ """
500
+ return self._n
501
+
502
+ def order(self):
503
+ """
504
+ Return the total number of elements in all blocks of ``self``.
505
+
506
+ EXAMPLES::
507
+
508
+ sage: C = OrderedMultisetPartitionIntoSets([[3, 4, 1], [2], [1, 2, 3, 7]]); C
509
+ [{1,3,4}, {2}, {1,2,3,7}]
510
+ sage: C.order()
511
+ 8
512
+ sage: C.order() == sum(C.weight().values())
513
+ True
514
+ sage: C.order() == sum(k for k in C.shape_from_cardinality())
515
+ True
516
+ sage: OrderedMultisetPartitionIntoSets([[7,1],[3]]).order()
517
+ 3
518
+ """
519
+ return self._order
520
+
521
+ def length(self):
522
+ """
523
+ Return the number of blocks of ``self``.
524
+
525
+ EXAMPLES::
526
+
527
+ sage: OrderedMultisetPartitionIntoSets([[7,1],[3]]).length()
528
+ 2
529
+ """
530
+ return len(self)
531
+
532
+ def weight(self, as_weak_comp=False):
533
+ r"""
534
+ Return a dictionary, with keys being the letters in ``self.letters()``
535
+ and values being their (positive) frequency.
536
+
537
+ Alternatively, if ``as_weak_comp`` is ``True``, count the number of instances
538
+ `n_i` for each distinct positive integer `i` across all blocks of ``self``.
539
+ Return as a list `[n_1, n_2, n_3, ..., n_k]`, where `k` is the max letter
540
+ appearing in ``self.letters()``.
541
+
542
+ EXAMPLES::
543
+
544
+ sage: c = OrderedMultisetPartitionIntoSets([[6,1],[1,3],[1,3,6]])
545
+ sage: c.weight()
546
+ {1: 3, 3: 2, 6: 2}
547
+ sage: c.weight(as_weak_comp=True)
548
+ [3, 0, 2, 0, 0, 2]
549
+
550
+ TESTS::
551
+
552
+ sage: OrderedMultisetPartitionIntoSets([]).weight() == {}
553
+ True
554
+
555
+ sage: c = OrderedMultisetPartitionIntoSets([['a','b'],['a','b','c'],['b'],['b'],['c']])
556
+ sage: c.weight()
557
+ {'a': 2, 'b': 4, 'c': 2}
558
+ sage: c.weight(as_weak_comp=True)
559
+ Traceback (most recent call last):
560
+ ...
561
+ ValueError: {'a': 2, 'b': 4, 'c': 2} is not a numeric multiset
562
+ """
563
+ from pprint import pformat
564
+ w = self._weight
565
+ if as_weak_comp:
566
+ if all(v in ZZ for v in w):
567
+ w = [w.get(i, 0) for i in range(1, self.max_letter() + 1)]
568
+ else:
569
+ raise ValueError("%s is not a numeric multiset" % pformat(w))
570
+ return w
571
+
572
+ def deconcatenate(self, k=2):
573
+ r"""
574
+ Return the list of `k`-deconcatenations of ``self``.
575
+
576
+ A `k`-tuple `(C_1, \ldots, C_k)` of ordered multiset partitions into sets
577
+ represents a `k`-deconcatenation of an ordered multiset partition into sets
578
+ `C` if `C_1 + \cdots + C_k = C`.
579
+
580
+ .. NOTE::
581
+
582
+ This is not to be confused with ``self.split_blocks()``,
583
+ which splits each block of ``self`` before making `k`-tuples
584
+ of ordered multiset partitions into sets.
585
+
586
+ EXAMPLES::
587
+
588
+ sage: OrderedMultisetPartitionIntoSets([[7,1],[3,4,5]]).deconcatenate()
589
+ [([{1,7}, {3,4,5}], []), ([{1,7}], [{3,4,5}]), ([], [{1,7}, {3,4,5}])]
590
+ sage: OrderedMultisetPartitionIntoSets([['b','c'],['a']]).deconcatenate()
591
+ [([{'b','c'}, {'a'}], []), ([{'b','c'}], [{'a'}]), ([], [{'b','c'}, {'a'}])]
592
+ sage: OrderedMultisetPartitionIntoSets([['a','b','c']]).deconcatenate(3)
593
+ [([{'a','b','c'}], [], []),
594
+ ([], [{'a','b','c'}], []),
595
+ ([], [], [{'a','b','c'}])]
596
+
597
+ TESTS::
598
+
599
+ sage: C = OrderedMultisetPartitionIntoSets([['a'],['b'],['c'],['d'],['e']]); C
600
+ [{'a'}, {'b'}, {'c'}, {'d'}, {'e'}]
601
+ sage: all( len(C.deconcatenate(k))
602
+ ....: == binomial(C.length() + k-1, k-1)
603
+ ....: for k in range(1, 5) )
604
+ True
605
+ """
606
+ P = OrderedMultisetPartitionsIntoSets(alphabet=self.letters(),
607
+ max_length=self.length())
608
+ out = []
609
+ for c in IntegerListsLex(self.length(), length=k):
610
+ ps = [sum(c[:i]) for i in range(k+1)]
611
+ out.append(tuple([P(self[ps[i]:ps[i+1]]) for i in range(len(ps)-1)]))
612
+ return out
613
+
614
+ def split_blocks(self, k=2):
615
+ r"""
616
+ Return a dictionary representing the `k`-splittings of ``self``.
617
+
618
+ A `k`-tuple `(A^1, \ldots, A^k)` of ordered multiset partitions into sets
619
+ represents a `k`-splitting of an ordered multiset partition into sets
620
+ `A = [b_1, \ldots, b_r]` if one can express each block `b_i` as
621
+ an (ordered) disjoint union of sets `b_i = b^1_i \sqcup \cdots
622
+ \sqcup b^k_i` (some possibly empty) so that each `A^j` is the
623
+ ordered multiset partition into sets corresponding to the list `[b^j_1,
624
+ b^j_2, \ldots, b^j_r]`, excising empty sets appearing therein.
625
+
626
+ This operation represents the coproduct in Hopf algebra of ordered
627
+ multiset partitions into sets in its natural basis [LM2018]_.
628
+
629
+ EXAMPLES::
630
+
631
+ sage: sorted(OrderedMultisetPartitionIntoSets([[1,2],[3,4]]).split_blocks(), key=str)
632
+ [([], [{1,2}, {3,4}]),
633
+ ([{1,2}, {3,4}], []),
634
+ ([{1,2}, {3}], [{4}]),
635
+ ([{1,2}, {4}], [{3}]),
636
+ ([{1,2}], [{3,4}]),
637
+ ([{1}, {3,4}], [{2}]),
638
+ ([{1}, {3}], [{2}, {4}]),
639
+ ([{1}, {4}], [{2}, {3}]),
640
+ ([{1}], [{2}, {3,4}]),
641
+ ([{2}, {3,4}], [{1}]),
642
+ ([{2}, {3}], [{1}, {4}]),
643
+ ([{2}, {4}], [{1}, {3}]),
644
+ ([{2}], [{1}, {3,4}]),
645
+ ([{3,4}], [{1,2}]),
646
+ ([{3}], [{1,2}, {4}]),
647
+ ([{4}], [{1,2}, {3}])]
648
+ sage: sorted(OrderedMultisetPartitionIntoSets([[1,2]]).split_blocks(3), key=str)
649
+ [([], [], [{1,2}]), ([], [{1,2}], []), ([], [{1}], [{2}]),
650
+ ([], [{2}], [{1}]), ([{1,2}], [], []), ([{1}], [], [{2}]),
651
+ ([{1}], [{2}], []), ([{2}], [], [{1}]), ([{2}], [{1}], [])]
652
+ sage: OrderedMultisetPartitionIntoSets([[4],[4]]).split_blocks()
653
+ {([], [{4}, {4}]): 1, ([{4}], [{4}]): 2, ([{4}, {4}], []): 1}
654
+
655
+ TESTS::
656
+
657
+ sage: C = OrderedMultisetPartitionIntoSets([[1,2],[4,5,6]]); C
658
+ [{1,2}, {4,5,6}]
659
+ sage: sum(C.split_blocks().values()) == 2**len(C[0]) * 2**len(C[1])
660
+ True
661
+ sage: sum(C.split_blocks(3).values()) == (1+2)**len(C[0]) * (1+2)**len(C[1])
662
+ True
663
+ sage: C = OrderedMultisetPartitionIntoSets([])
664
+ sage: C.split_blocks(3) == {(C, C, C): 1}
665
+ True
666
+ """
667
+ P = OrderedMultisetPartitionsIntoSets(alphabet=self.letters(),
668
+ max_length=self.length())
669
+
670
+ # corner case
671
+ if not self:
672
+ return {tuple([self]*k): 1}
673
+
674
+ out: dict[tuple, int] = {}
675
+ for t in product(*[_split_block(block, k) for block in self]):
676
+ tt = tuple([P([l for l in c if l]) for c in zip(*t)])
677
+ out[tt] = out.get(tt, 0) + 1
678
+ return out
679
+
680
+ def finer(self, strong=False):
681
+ r"""
682
+ Return the set of ordered multiset partitions into sets that are finer
683
+ than ``self``.
684
+
685
+ An ordered multiset partition into sets `A` is finer than another `B`
686
+ if, reading left-to-right, every block of `B` is the union of some
687
+ consecutive blocks of `A`.
688
+
689
+ If optional argument ``strong`` is set to ``True``, then return
690
+ only those `A` whose blocks are deconcatenations of blocks of `B`.
691
+ (Here, we view blocks of `B` as sorted lists instead of sets.)
692
+
693
+ EXAMPLES::
694
+
695
+ sage: C = OrderedMultisetPartitionIntoSets([[3,2]]).finer()
696
+ sage: len(C)
697
+ 3
698
+ sage: sorted(C, key=str)
699
+ [[{2,3}], [{2}, {3}], [{3}, {2}]]
700
+ sage: OrderedMultisetPartitionIntoSets([]).finer()
701
+ {[]}
702
+ sage: O = OrderedMultisetPartitionsIntoSets([1, 1, 'a', 'b'])
703
+ sage: o = O([{1}, {'a', 'b'}, {1}])
704
+ sage: sorted(o.finer(), key=str)
705
+ [[{1}, {'a','b'}, {1}], [{1}, {'a'}, {'b'}, {1}], [{1}, {'b'}, {'a'}, {1}]]
706
+ sage: o.finer() & o.fatter() == set([o])
707
+ True
708
+ """
709
+ P = OrderedMultisetPartitionsIntoSets(self._multiset)
710
+
711
+ if not self:
712
+ return set([self])
713
+
714
+ CP = product(*[_refine_block(block, strong) for block in self])
715
+ return set(P(_concatenate(map(list, c))) for c in CP)
716
+
717
+ def is_finer(self, co):
718
+ """
719
+ Return ``True`` if the ordered multiset partition into sets ``self``
720
+ is finer than the composition ``co``; otherwise, return ``False``.
721
+
722
+ EXAMPLES::
723
+
724
+ sage: OrderedMultisetPartitionIntoSets([[4],[1],[2]]).is_finer([[1,4],[2]])
725
+ True
726
+ sage: OrderedMultisetPartitionIntoSets([[1],[4],[2]]).is_finer([[1,4],[2]])
727
+ True
728
+ sage: OrderedMultisetPartitionIntoSets([[1,4],[1],[1]]).is_finer([[1,4],[2]])
729
+ False
730
+ """
731
+ X = _concatenate(co)
732
+ if self.weight() != OrderedMultisetPartitionsIntoSets(_get_weight(X))(co).weight():
733
+ return False
734
+
735
+ # trim common prefix and suffix to make the search-space smaller
736
+ co1 = list(map(set, self))
737
+ co2 = list(map(set, co))
738
+ while co1[0] == co2[0]:
739
+ co1 = co1[1:]
740
+ co2 = co2[1:]
741
+ while co1[-1] == co2[-1]:
742
+ co1 = co1[:-1]
743
+ co2 = co2[:-1]
744
+
745
+ co1 = OrderedMultisetPartitionIntoSets(co1)
746
+ co2 = OrderedMultisetPartitionIntoSets(co2)
747
+ return co1 in co2.finer()
748
+
749
+ def fatten(self, grouping):
750
+ r"""
751
+ Return the ordered multiset partition into sets fatter than ``self``,
752
+ obtained by grouping together consecutive parts according to ``grouping``
753
+ (whenever this does not violate the strictness condition).
754
+
755
+ INPUT:
756
+
757
+ - ``grouping`` -- a composition (or list) whose sum is the length
758
+ of ``self``
759
+
760
+ EXAMPLES:
761
+
762
+ Let us start with the composition::
763
+
764
+ sage: C = OrderedMultisetPartitionIntoSets([[4,1,5], [2], [7,1]]); C
765
+ [{1,4,5}, {2}, {1,7}]
766
+
767
+ With ``grouping`` equal to `(1, 1, 1)`, `C` is left unchanged::
768
+
769
+ sage: C.fatten([1,1,1])
770
+ [{1,4,5}, {2}, {1,7}]
771
+
772
+ With ``grouping`` equal to `(2,1)` or `(1,2)`, a union of consecutive
773
+ parts is achieved::
774
+
775
+ sage: C.fatten([2,1])
776
+ [{1,2,4,5}, {1,7}]
777
+ sage: C.fatten([1,2])
778
+ [{1,4,5}, {1,2,7}]
779
+
780
+ However, the ``grouping`` `(3)` will throw an error, as `1` cannot
781
+ appear twice in any block of ``C``::
782
+
783
+ sage: C.fatten(Composition([3]))
784
+ Traceback (most recent call last):
785
+ ...
786
+ ValueError: [{1,4,5,2,1,7}] is not a valid ordered multiset partition into sets
787
+ """
788
+ if sum(list(grouping)) != self.length():
789
+ raise ValueError("%s is not a composition of ``self.length()`` (=%s)"
790
+ % (grouping, self.length()))
791
+
792
+ valid = True
793
+ result = []
794
+ for i in range(len(grouping)):
795
+ result_i = self[sum(grouping[:i]) : sum(grouping[:i+1])]
796
+ # check that grouping[i] is allowed, i.e., `|A\cup B| = |A| + |B|`
797
+ strict_size = sum(map(len, result_i))
798
+ size = len(_union_of_sets(result_i))
799
+ if size < strict_size:
800
+ valid = False
801
+ result.append(_concatenate(result_i))
802
+ if not valid:
803
+ str_rep = '['
804
+ for i in range(len(grouping)):
805
+ st = ",".join(str(k) for k in result[i])
806
+ str_rep += "{" + st + "}"
807
+ str_rep = str_rep.replace("}{", "}, {") + "]"
808
+ raise ValueError("%s is not a valid ordered multiset partition into sets" % (str_rep))
809
+ else:
810
+ return OrderedMultisetPartitionsIntoSets(self._multiset)(result)
811
+
812
+ def fatter(self):
813
+ """
814
+ Return the set of ordered multiset partitions into sets which are fatter
815
+ than ``self``.
816
+
817
+ An ordered multiset partition into sets `A` is fatter than another `B`
818
+ if, reading left-to-right, every block of `A` is the union of some
819
+ consecutive blocks of `B`.
820
+
821
+ EXAMPLES::
822
+
823
+ sage: C = OrderedMultisetPartitionIntoSets([{1,4,5}, {2}, {1,7}]).fatter()
824
+ sage: len(C)
825
+ 3
826
+ sage: sorted(C)
827
+ [[{1,4,5}, {2}, {1,7}], [{1,4,5}, {1,2,7}], [{1,2,4,5}, {1,7}]]
828
+ sage: sorted(OrderedMultisetPartitionIntoSets([['a','b'],['c'],['a']]).fatter())
829
+ [[{'a','b'}, {'c'}, {'a'}], [{'a','b'}, {'a','c'}], [{'a','b','c'}, {'a'}]]
830
+
831
+ Some extreme cases::
832
+
833
+ sage: list(OrderedMultisetPartitionIntoSets([['a','b','c']]).fatter())
834
+ [[{'a','b','c'}]]
835
+ sage: list(OrderedMultisetPartitionIntoSets([]).fatter())
836
+ [[]]
837
+ sage: A = OrderedMultisetPartitionIntoSets([[1], [2], [3], [4]])
838
+ sage: B = OrderedMultisetPartitionIntoSets([[1,2,3,4]])
839
+ sage: A.fatter().issubset(B.finer())
840
+ True
841
+ """
842
+ out = set()
843
+ for c in composition_iterator_fast(self.length()):
844
+ try:
845
+ out.add(self.fatten(c))
846
+ except ValueError:
847
+ pass
848
+ return out
849
+
850
+ def minimaj(self):
851
+ r"""
852
+ Return the minimaj statistic on ordered multiset partitions into sets.
853
+
854
+ We define `minimaj` via an example:
855
+
856
+ 1. Sort the block in ``self`` as prescribed by ``self.minimaj_word()``,
857
+ keeping track of the original separation into blocks::
858
+
859
+ in: [{1,5,7}, {2,4}, {5,6}, {4,6,8}, {1,3}, {1,2,3}]
860
+ out: ( 5,7,1 / 2,4 / 5,6 / 4,6,8 / 3,1 / 1,2,3 )
861
+
862
+ 2. Record the indices where descents in this word occur::
863
+
864
+ word: (5, 7, 1 / 2, 4 / 5, 6 / 4, 6, 8 / 3, 1 / 1, 2, 3)
865
+ indices: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
866
+ descents: { 2, 7, 10, 11 }
867
+
868
+ 3. Compute the sum of the descents::
869
+
870
+ minimaj = 2 + 7 + 10 + 11 = 30
871
+
872
+ REFERENCES:
873
+
874
+ - [HRW2015]_
875
+
876
+ EXAMPLES::
877
+
878
+ sage: C = OrderedMultisetPartitionIntoSets([{1,5,7}, {2,4}, {5,6}, {4,6,8}, {1,3}, {1,2,3}])
879
+ sage: C, C.minimaj_word()
880
+ ([{1,5,7}, {2,4}, {5,6}, {4,6,8}, {1,3}, {1,2,3}],
881
+ (5, 7, 1, 2, 4, 5, 6, 4, 6, 8, 3, 1, 1, 2, 3))
882
+ sage: C.minimaj()
883
+ 30
884
+ sage: C = OrderedMultisetPartitionIntoSets([{2,4}, {1,2,3}, {1,6,8}, {2,3}])
885
+ sage: C, C.minimaj_word()
886
+ ([{2,4}, {1,2,3}, {1,6,8}, {2,3}], (2, 4, 1, 2, 3, 6, 8, 1, 2, 3))
887
+ sage: C.minimaj()
888
+ 9
889
+ sage: OrderedMultisetPartitionIntoSets([]).minimaj()
890
+ 0
891
+ sage: C = OrderedMultisetPartitionIntoSets([['b','d'],['a','b','c'],['b']])
892
+ sage: C, C.minimaj_word()
893
+ ([{'b','d'}, {'a','b','c'}, {'b'}], ('d', 'b', 'c', 'a', 'b', 'b'))
894
+ sage: C.minimaj()
895
+ 4
896
+ """
897
+ D = _descents(self.minimaj_word())
898
+ return sum(D) + len(D)
899
+
900
+ def minimaj_word(self):
901
+ """
902
+ Return an ordering of ``self._multiset`` derived from the minimaj
903
+ ordering on blocks of ``self``.
904
+
905
+ .. SEEALSO::
906
+
907
+ :meth:`OrderedMultisetPartitionIntoSets.minimaj_blocks()`.
908
+
909
+ EXAMPLES::
910
+
911
+ sage: C = OrderedMultisetPartitionIntoSets([[2,1], [1,2,3], [1,2], [3], [1]]); C
912
+ [{1,2}, {1,2,3}, {1,2}, {3}, {1}]
913
+ sage: C.minimaj_blocks()
914
+ ((1, 2), (2, 3, 1), (1, 2), (3,), (1,))
915
+ sage: C.minimaj_word()
916
+ (1, 2, 2, 3, 1, 1, 2, 3, 1)
917
+ """
918
+ return _concatenate(self.minimaj_blocks())
919
+
920
+ def minimaj_blocks(self):
921
+ r"""
922
+ Return the minimaj ordering on blocks of ``self``.
923
+
924
+ We define the ordering via the example below.
925
+
926
+ Sort the blocks `[B_1,...,B_k]` of ``self`` from right to left via:
927
+
928
+ 1. Sort the last block `B_k` in increasing order, call it the word `W_k`
929
+
930
+ 2. If blocks `B_{i+1}, \ldots, B_k` have been converted to words
931
+ `W_{i+1}, \ldots, W_k`, use the letters in `B_i` to make the unique
932
+ word `W_i` that has a factorization `W_i = (u, v)` satisfying:
933
+
934
+ - letters of `u` and `v` appear in increasing order, with `v`
935
+ possibly empty;
936
+ - letters in `vu` appear in increasing order;
937
+ - ``v[-1]`` is the largest letter `a \in B_i` satisfying
938
+ ``a <= W_{i+1}[0]``.
939
+
940
+ EXAMPLES::
941
+
942
+ sage: OrderedMultisetPartitionIntoSets([[1,5,7], [2,4], [5,6], [4,6,8], [1,3], [1,2,3]])
943
+ [{1,5,7}, {2,4}, {5,6}, {4,6,8}, {1,3}, {1,2,3}]
944
+ sage: _.minimaj_blocks()
945
+ ((5, 7, 1), (2, 4), (5, 6), (4, 6, 8), (3, 1), (1, 2, 3))
946
+ sage: OrderedMultisetPartitionIntoSets([]).minimaj_blocks()
947
+ ()
948
+ """
949
+ if not self:
950
+ return ()
951
+
952
+ C = [sorted(self[-1])]
953
+ for i in range(1, len(self)):
954
+ lower = []
955
+ upper = []
956
+ for j in self[-1 - i]:
957
+ if j <= C[0][0]:
958
+ lower.append(j)
959
+ else:
960
+ upper.append(j)
961
+ C = [sorted(upper) + sorted(lower)] + C
962
+ return tuple(map(tuple, C))
963
+
964
+ def to_tableaux_words(self):
965
+ r"""
966
+ Return a sequence of lists corresponding to row words
967
+ of (skew-)tableaux.
968
+
969
+ OUTPUT:
970
+
971
+ The minimaj bijection `\phi` of [BCHOPSY2017]_
972
+ applied to ``self``.
973
+
974
+ .. TODO::
975
+
976
+ Implement option for mapping to sequence of (skew-)tableaux?
977
+
978
+ EXAMPLES::
979
+
980
+ sage: co = ((1,2,4),(4,5),(3,),(4,6,1),(2,3,1),(1,),(2,5))
981
+ sage: OrderedMultisetPartitionIntoSets(co).to_tableaux_words()
982
+ [[5, 1], [3, 1], [6], [5, 4, 2], [1, 4, 3, 4, 2, 1, 2]]
983
+ """
984
+ if not self:
985
+ return []
986
+ bb = self.minimaj_blocks()
987
+ b = [block[0] for block in bb]
988
+ beginning = [0]+running_total(self.shape_from_cardinality())
989
+ w = _concatenate(bb)
990
+ D = [0] + _descents(w) + [len(w)]
991
+ pieces = [b]
992
+ for i in range(len(D)-1):
993
+ p = [w[j] for j in range(D[i]+1,D[i+1]+1) if j not in beginning]
994
+ pieces = [p[::-1]] + pieces
995
+ return pieces
996
+
997
+ def major_index(self):
998
+ r"""
999
+ Return the major index of ``self``.
1000
+
1001
+ The major index is a statistic on ordered multiset partitions into sets,
1002
+ which we define here via an example.
1003
+
1004
+ 1. Sort each block in the list ``self`` in descending order to create
1005
+ a word `w`, keeping track of the original separation into blocks::
1006
+
1007
+ in: [{3,4,5}, {2,3,4}, {1}, {4,5}]
1008
+ out: [ 5,4,3 / 4,3,2 / 1 / 5,4 ]
1009
+
1010
+ 2. Create a sequence `v = (v_0, v_1, v_2, \ldots)` of length
1011
+ ``self.order()+1``, built recursively by:
1012
+
1013
+ 1. `v_0 = 0`
1014
+ 2. `v_j = v_{j-1} + \delta(j)`, where `\delta(j) = 1` if `j` is
1015
+ the index of an end of a block, and zero otherwise.
1016
+
1017
+ ::
1018
+
1019
+ in: [ 5,4,3 / 4,3,2 / 1 / 5,4]
1020
+ out: (0, 0,0,1, 1,1,2, 3, 3,4)
1021
+
1022
+ 3. Compute `\sum_j v_j`, restricted to descent positions in `w`, i.e.,
1023
+ sum over those `j` with `w_j > w_{j+1}`::
1024
+
1025
+ in: w: [5, 4, 3, 4, 3, 2, 1, 5, 4]
1026
+ v: (0 0, 0, 1, 1, 1, 2, 3, 3, 4)
1027
+ maj := 0 +0 +1 +1 +2 +3 = 7
1028
+
1029
+ REFERENCES:
1030
+
1031
+ - [HRW2015]_
1032
+
1033
+ EXAMPLES::
1034
+
1035
+ sage: C = OrderedMultisetPartitionIntoSets([{1,5,7}, {2,4}, {5,6}, {4,6,8}, {1,3}, {1,2,3}])
1036
+ sage: C.major_index()
1037
+ 27
1038
+ sage: C = OrderedMultisetPartitionIntoSets([{3,4,5}, {2,3,4}, {1}, {4,5}])
1039
+ sage: C.major_index()
1040
+ 7
1041
+ """
1042
+ ew = [enumerate(sorted(k)) for k in self]
1043
+ w = []
1044
+ v = [0]
1045
+ for eblock in ew:
1046
+ for (i,wj) in sorted(eblock, reverse=True):
1047
+ vj = v[-1]
1048
+ if i == 0:
1049
+ vj += 1
1050
+ v.append(vj)
1051
+ w.append(wj)
1052
+ maj = [v[j+1] for j in range(len(w)-1) if w[j] > w[j+1]]
1053
+ return sum(maj)
1054
+
1055
+ def shuffle_product(self, other, overlap=False):
1056
+ r"""
1057
+ Return the shuffles (with multiplicity) of blocks of ``self``
1058
+ with blocks of ``other``.
1059
+
1060
+ In case optional argument ``overlap`` is ``True``, instead return
1061
+ the allowable overlapping shuffles. An overlapping shuffle `C` is
1062
+ allowable if, whenever one of its blocks `c` comes from the union
1063
+ `c = a \cup b` of a block of ``self`` and a block of ``other``,
1064
+ then this union is disjoint.
1065
+
1066
+ .. SEEALSO::
1067
+
1068
+ :meth:`Composition.shuffle_product()`
1069
+
1070
+ EXAMPLES::
1071
+
1072
+ sage: A = OrderedMultisetPartitionIntoSets([[2,1,3], [1,2]]); A
1073
+ [{1,2,3}, {1,2}]
1074
+ sage: B = OrderedMultisetPartitionIntoSets([[3,4]]); B
1075
+ [{3,4}]
1076
+ sage: C = OrderedMultisetPartitionIntoSets([[4,5]]); C
1077
+ [{4,5}]
1078
+ sage: list(A.shuffle_product(B))
1079
+ [[{1,2,3}, {1,2}, {3,4}], [{3,4}, {1,2,3}, {1,2}], [{1,2,3}, {3,4}, {1,2}]]
1080
+ sage: list(A.shuffle_product(B, overlap=True))
1081
+ [[{1,2,3}, {1,2}, {3,4}], [{1,2,3}, {3,4}, {1,2}],
1082
+ [{3,4}, {1,2,3}, {1,2}], [{1,2,3}, {1,2,3,4}]]
1083
+ sage: list(A.shuffle_product(C, overlap=True))
1084
+ [[{1,2,3}, {1,2}, {4,5}], [{1,2,3}, {4,5}, {1,2}], [{4,5}, {1,2,3}, {1,2}],
1085
+ [{1,2,3,4,5}, {1,2}], [{1,2,3}, {1,2,4,5}]]
1086
+ """
1087
+ other = OrderedMultisetPartitionIntoSets(other)
1088
+ P = OrderedMultisetPartitionsIntoSets(self._multiset + other._multiset)
1089
+ if not overlap:
1090
+ for term in ShuffleProduct(self, other, element_constructor=P):
1091
+ yield term
1092
+ else:
1093
+ A = list(map(tuple, self))
1094
+ B = list(map(tuple, other))
1095
+ for term in ShuffleProduct_overlapping(A, B):
1096
+ if len(_concatenate(map(frozenset, term))) == len(P._Xtup):
1097
+ yield P(term)
1098
+
1099
+ ##############################################################
1100
+
1101
+
1102
+ class OrderedMultisetPartitionsIntoSets(UniqueRepresentation, Parent):
1103
+ r"""
1104
+ Ordered Multiset Partitions into Sets.
1105
+
1106
+ An *ordered multiset partition into sets* `c` of a multiset `X` is
1107
+ a list of nonempty subsets (not multisets), called the *blocks* of `c`,
1108
+ whose multi-union is `X`.
1109
+
1110
+ The number of blocks of `c` is called its *length*. The *order* of `c`
1111
+ is the cardinality of the multiset `X`. If, additionally, `X` is a
1112
+ multiset of positive integers, then the *size* of `c` is the sum of
1113
+ all elements of `X`.
1114
+
1115
+ The user may wish to focus on ordered multiset partitions into sets
1116
+ of a given size, or over a given alphabet. Hence, this class allows
1117
+ a variety of arguments as input.
1118
+
1119
+ INPUT:
1120
+
1121
+ Expects one or two arguments, with different behaviors resulting:
1122
+
1123
+ - One Argument:
1124
+
1125
+ + `X` -- a dictionary or list or tuple
1126
+ (representing a multiset for `c`),
1127
+ or an integer (representing the size of `c`)
1128
+
1129
+ - Two Arguments:
1130
+
1131
+ + `A` -- list (representing allowable letters within blocks of `c`),
1132
+ or a positive integer (representing the maximal allowable letter)
1133
+ + `n` -- a nonnegative integer (the total number of letters within `c`)
1134
+
1135
+ Optional keyword arguments are as follows:
1136
+ (See corresponding methods in see :class:`OrderedMultisetPartitionIntoSets` for more details.)
1137
+
1138
+ - ``weight=X`` (list or dictionary `X`) specifies the multiset for `c`
1139
+ - ``size=n`` (integer `n`) specifies the size of `c`
1140
+ - ``alphabet=A`` (iterable `A`) specifies allowable elements for the blocks of `c`
1141
+ - ``length=k`` (integer `k`) specifies the number of blocks in the partition
1142
+ - ``min_length=k`` (integer `k`) specifies minimum number of blocks in the partition
1143
+ - ``max_length=k`` (integer `k`) specifies maximum number of blocks in the partition
1144
+ - ``order=n`` (integer `n`) specifies the cardinality of the multiset that `c` partitions
1145
+ - ``min_order=n`` (integer `n`) specifies minimum number of elements in the partition
1146
+ - ``max_order=n`` (integer `n`) specifies maximum number of elements in the partition
1147
+
1148
+ EXAMPLES:
1149
+
1150
+ Passing one argument to :class:`OrderedMultisetPartitionsIntoSets`:
1151
+
1152
+ There are 5 ordered multiset partitions into sets of the multiset
1153
+ `\{\{1, 1, 4\}\}`::
1154
+
1155
+ sage: OrderedMultisetPartitionsIntoSets([1,1,4]).cardinality()
1156
+ 5
1157
+
1158
+ Here is the list of them::
1159
+
1160
+ sage: OrderedMultisetPartitionsIntoSets([1,1,4]).list()
1161
+ [[{1}, {1}, {4}], [{1}, {1,4}], [{1}, {4}, {1}], [{1,4}, {1}], [{4}, {1}, {1}]]
1162
+
1163
+ By chance, there are also 5 ordered multiset partitions into sets of
1164
+ the integer 3::
1165
+
1166
+ sage: OrderedMultisetPartitionsIntoSets(3).cardinality()
1167
+ 5
1168
+
1169
+ Here is the list of them::
1170
+
1171
+ sage: OrderedMultisetPartitionsIntoSets(3).list()
1172
+ [[{3}], [{1,2}], [{2}, {1}], [{1}, {2}], [{1}, {1}, {1}]]
1173
+
1174
+ Passing two arguments to :class:`OrderedMultisetPartitionsIntoSets`:
1175
+
1176
+ There are also 5 ordered multiset partitions into sets of order 2
1177
+ over the alphabet `\{1, 4\}`::
1178
+
1179
+ sage: OrderedMultisetPartitionsIntoSets([1, 4], 2)
1180
+ Ordered Multiset Partitions into Sets of order 2 over alphabet {1, 4}
1181
+ sage: OrderedMultisetPartitionsIntoSets([1, 4], 2).cardinality()
1182
+ 5
1183
+
1184
+ Here is the list of them::
1185
+
1186
+ sage: OrderedMultisetPartitionsIntoSets([1, 4], 2).list()
1187
+ [[{1,4}], [{1}, {1}], [{1}, {4}], [{4}, {1}], [{4}, {4}]]
1188
+
1189
+ If no arguments are passed to :class:`OrderedMultisetPartitionsIntoSets`,
1190
+ then the code returns all ordered multiset partitions into sets::
1191
+
1192
+ sage: OrderedMultisetPartitionsIntoSets()
1193
+ Ordered Multiset Partitions into Sets
1194
+ sage: [] in OrderedMultisetPartitionsIntoSets()
1195
+ True
1196
+ sage: [[2,3], [1]] in OrderedMultisetPartitionsIntoSets()
1197
+ True
1198
+ sage: [['a','b'], ['a']] in OrderedMultisetPartitionsIntoSets()
1199
+ True
1200
+ sage: [[-2,3], [3]] in OrderedMultisetPartitionsIntoSets()
1201
+ True
1202
+ sage: [[2], [3,3]] in OrderedMultisetPartitionsIntoSets()
1203
+ False
1204
+
1205
+ The following examples show how to test whether or not an object
1206
+ is an ordered multiset partition into sets::
1207
+
1208
+ sage: [[3,2],[2]] in OrderedMultisetPartitionsIntoSets()
1209
+ True
1210
+ sage: [[3,2],[2]] in OrderedMultisetPartitionsIntoSets(7)
1211
+ True
1212
+ sage: [[3,2],[2]] in OrderedMultisetPartitionsIntoSets([2,2,3])
1213
+ True
1214
+ sage: [[3,2],[2]] in OrderedMultisetPartitionsIntoSets(5)
1215
+ False
1216
+
1217
+ .. RUBRIC:: Optional keyword arguments
1218
+
1219
+ Passing keyword arguments that are incompatible with required requirements
1220
+ results in an error; otherwise, the collection of ordered multiset partitions
1221
+ into sets is restricted accordingly:
1222
+
1223
+ *The* ``weight`` *keyword:*
1224
+
1225
+ This is used to specify which multiset `X` is to be considered,
1226
+ if this multiset was not passed as one of the required arguments for
1227
+ :class:`OrderedMultisetPartitionsIntoSets`. In principle, it is a dictionary,
1228
+ but weak compositions are also allowed. For example, the ordered multiset
1229
+ partitions into sets of integer 4 are listed by weight below::
1230
+
1231
+ sage: OrderedMultisetPartitionsIntoSets(4, weight=[0,0,0,1])
1232
+ Ordered Multiset Partitions into Sets of integer 4 with constraint: weight={4: 1}
1233
+ sage: OrderedMultisetPartitionsIntoSets(4, weight=[0,0,0,1]).list()
1234
+ [[{4}]]
1235
+ sage: OrderedMultisetPartitionsIntoSets(4, weight=[1,0,1]).list()
1236
+ [[{1}, {3}], [{1,3}], [{3}, {1}]]
1237
+ sage: OrderedMultisetPartitionsIntoSets(4, weight=[0,2]).list()
1238
+ [[{2}, {2}]]
1239
+ sage: OrderedMultisetPartitionsIntoSets(4, weight=[0,1,1]).list()
1240
+ []
1241
+ sage: OrderedMultisetPartitionsIntoSets(4, weight=[2,1]).list()
1242
+ [[{1}, {1}, {2}], [{1}, {1,2}], [{1}, {2}, {1}], [{1,2}, {1}], [{2}, {1}, {1}]]
1243
+ sage: O1 = OrderedMultisetPartitionsIntoSets(weight=[2,0,1])
1244
+ sage: O2 = OrderedMultisetPartitionsIntoSets(weight={1:2, 3:1})
1245
+ sage: O1 == O2
1246
+ True
1247
+ sage: OrderedMultisetPartitionsIntoSets(4, weight=[4]).list()
1248
+ [[{1}, {1}, {1}, {1}]]
1249
+
1250
+ *The* ``size`` *keyword:*
1251
+
1252
+ This is used to constrain the sum of entries across all blocks of the ordered
1253
+ multiset partition into sets. (This size is not pre-determined when alphabet
1254
+ `A` and order `d` are passed as required arguments.) For example, the ordered
1255
+ multiset partitions into sets of order 3 over the alphabet `[1,2,4]` that have
1256
+ size equal to 5 are as follows::
1257
+
1258
+ sage: OMPs = OrderedMultisetPartitionsIntoSets
1259
+ sage: OMPs([1,2,4], 3, size=5).list()
1260
+ [[{1,2}, {2}], [{2}, {1,2}], [{2}, {2}, {1}],
1261
+ [{2}, {1}, {2}], [{1}, {2}, {2}]]
1262
+
1263
+ *The* ``alphabet`` *option:*
1264
+
1265
+ This is used to constrain which integers appear across all blocks of the
1266
+ ordered multiset partition into sets. For example, the ordered multiset
1267
+ partitions into sets of integer 4 are listed for different choices of alphabet
1268
+ below. Note that ``alphabet`` is allowed to be an integer or an iterable::
1269
+
1270
+ sage: OMPs = OrderedMultisetPartitionsIntoSets
1271
+ sage: OMPs(4, alphabet=3).list()
1272
+ [[{1,3}], [{3}, {1}],
1273
+ [{1,2}, {1}], [{2}, {2}],
1274
+ [{2}, {1}, {1}], [{1}, {3}],
1275
+ [{1}, {1,2}], [{1}, {2}, {1}],
1276
+ [{1}, {1}, {2}], [{1}, {1}, {1}, {1}]]
1277
+ sage: OMPs(4, alphabet=3) == OMPs(4, alphabet=[1,2,3])
1278
+ True
1279
+ sage: OMPs(4, alphabet=[3]).list()
1280
+ []
1281
+ sage: OMPs(4, alphabet=[1,3]).list()
1282
+ [[{1,3}], [{3}, {1}], [{1}, {3}], [{1}, {1}, {1}, {1}]]
1283
+ sage: OMPs(4, alphabet=[2]).list()
1284
+ [[{2}, {2}]]
1285
+ sage: OMPs(4, alphabet=[1,2]).list()
1286
+ [[{1,2}, {1}], [{2}, {2}], [{2}, {1}, {1}], [{1}, {1,2}],
1287
+ [{1}, {2}, {1}], [{1}, {1}, {2}], [{1}, {1}, {1}, {1}]]
1288
+ sage: OMPs(4, alphabet=4).list() == OMPs(4).list()
1289
+ True
1290
+
1291
+ *The* ``length``, ``min_length``, *and* ``max_length`` *options:*
1292
+
1293
+ These are used to constrain the number of blocks within the ordered multiset
1294
+ partitions into sets. For example, the ordered multiset partitions into sets
1295
+ of integer 4 of length exactly 2, at least 2, and at most 2 are given by::
1296
+
1297
+ sage: OrderedMultisetPartitionsIntoSets(4, length=2).list()
1298
+ [[{3}, {1}], [{1,2}, {1}], [{2}, {2}], [{1}, {3}], [{1}, {1,2}]]
1299
+ sage: OrderedMultisetPartitionsIntoSets(4, min_length=3).list()
1300
+ [[{2}, {1}, {1}], [{1}, {2}, {1}], [{1}, {1}, {2}], [{1}, {1}, {1}, {1}]]
1301
+ sage: OrderedMultisetPartitionsIntoSets(4, max_length=2).list()
1302
+ [[{4}], [{1,3}], [{3}, {1}], [{1,2}, {1}], [{2}, {2}], [{1}, {3}],
1303
+ [{1}, {1,2}]]
1304
+
1305
+ *The* ``order``, ``min_order``, *and* ``max_order`` *options:*
1306
+
1307
+ These are used to constrain the number of elements across all blocks of the
1308
+ ordered multiset partitions into sets. For example, the ordered multiset
1309
+ partitions into sets of integer 4 are listed by order below::
1310
+
1311
+ sage: OrderedMultisetPartitionsIntoSets(4, order=1).list()
1312
+ [[{4}]]
1313
+ sage: OrderedMultisetPartitionsIntoSets(4, order=2).list()
1314
+ [[{1,3}], [{3}, {1}], [{2}, {2}], [{1}, {3}]]
1315
+ sage: OrderedMultisetPartitionsIntoSets(4, order=3).list()
1316
+ [[{1,2}, {1}], [{2}, {1}, {1}], [{1}, {1,2}], [{1}, {2}, {1}], [{1}, {1}, {2}]]
1317
+ sage: OrderedMultisetPartitionsIntoSets(4, order=4).list()
1318
+ [[{1}, {1}, {1}, {1}]]
1319
+
1320
+ Also, here is a use of ``max_order``, giving the ordered multiset
1321
+ partitions into sets of integer 4 with order 1 or 2::
1322
+
1323
+ sage: OrderedMultisetPartitionsIntoSets(4, max_order=2).list()
1324
+ [[{4}], [{1,3}], [{3}, {1}], [{2}, {2}], [{1}, {3}]]
1325
+
1326
+ TESTS::
1327
+
1328
+ sage: C = OrderedMultisetPartitionsIntoSets(8, length=3); C.cardinality()
1329
+ 72
1330
+ sage: TestSuite(C).run()
1331
+ """
1332
+ @staticmethod
1333
+ def __classcall_private__(self, *args, **constraints):
1334
+ """
1335
+ Return the correct parent based upon the input:
1336
+
1337
+ EXAMPLES::
1338
+
1339
+ sage: OrderedMultisetPartitionsIntoSets()
1340
+ Ordered Multiset Partitions into Sets
1341
+ sage: OrderedMultisetPartitionsIntoSets(4)
1342
+ Ordered Multiset Partitions into Sets of integer 4
1343
+ sage: OrderedMultisetPartitionsIntoSets(4, max_order=2)
1344
+ Ordered Multiset Partitions into Sets of integer 4 with constraint: max_order=2
1345
+
1346
+ sage: OrderedMultisetPartitionsIntoSets({1:2, 3:1})
1347
+ Ordered Multiset Partitions into Sets of multiset {{1, 1, 3}}
1348
+ sage: OrderedMultisetPartitionsIntoSets({1:2, 3:1}) == OrderedMultisetPartitionsIntoSets([1,1,3])
1349
+ True
1350
+ sage: OrderedMultisetPartitionsIntoSets({'a':2, 'c':1}, length=2)
1351
+ Ordered Multiset Partitions into Sets of multiset {{a, a, c}} with constraint: length=2
1352
+ sage: OrderedMultisetPartitionsIntoSets({'a':2, 'c':1}, length=4).list()
1353
+ []
1354
+
1355
+ sage: OrderedMultisetPartitionsIntoSets(4, 3)
1356
+ Ordered Multiset Partitions into Sets of order 3 over alphabet {1, 2, 3, 4}
1357
+ sage: OrderedMultisetPartitionsIntoSets(['a', 'd'], 3)
1358
+ Ordered Multiset Partitions into Sets of order 3 over alphabet {a, d}
1359
+ sage: OrderedMultisetPartitionsIntoSets([2,4], 3, min_length=2)
1360
+ Ordered Multiset Partitions into Sets of order 3 over alphabet {2, 4}
1361
+ with constraint: min_length=2
1362
+
1363
+ TESTS:
1364
+
1365
+ The alphabet and order keywords cannot be used if they are also passed
1366
+ as required arguments, even if the values are compatible::
1367
+
1368
+ sage: OrderedMultisetPartitionsIntoSets([1,2,4], 4, alphabet=[2,4], order=3)
1369
+ Traceback (most recent call last):
1370
+ ...
1371
+ ValueError: cannot pass alphabet as first argument and keyword argument
1372
+ sage: OrderedMultisetPartitionsIntoSets([1,2,4], 4, order=4)
1373
+ Traceback (most recent call last):
1374
+ ...
1375
+ ValueError: cannot pass order as second argument and keyword argument
1376
+
1377
+ The weight, size, and order keywords cannot be used if a multiset is
1378
+ passed as a required argument, even if the values are compatible::
1379
+
1380
+ sage: OrderedMultisetPartitionsIntoSets([1,1,4], weight={1:3, 2:1}).list()
1381
+ Traceback (most recent call last):
1382
+ ...
1383
+ ValueError: cannot pass multiset as first argument and weight as keyword argument
1384
+ sage: OrderedMultisetPartitionsIntoSets([1,1,4], size=6).list()
1385
+ Traceback (most recent call last):
1386
+ ...
1387
+ ValueError: cannot pass multiset as first argument and size as keyword argument
1388
+ sage: OrderedMultisetPartitionsIntoSets([1,1,4], weight={1:3, 2:1}, order=2).list()
1389
+ Traceback (most recent call last):
1390
+ ...
1391
+ ValueError: cannot pass multiset as first argument and ['order', 'weight'] as keyword arguments
1392
+
1393
+ The size keyword cannot be used if it is also passed as a required argument,
1394
+ even if the value is compatible::
1395
+
1396
+ sage: OrderedMultisetPartitionsIntoSets(5, size=5)
1397
+ Traceback (most recent call last):
1398
+ ...
1399
+ ValueError: cannot pass size as first argument and keyword argument
1400
+ """
1401
+ constraints = dict(constraints)
1402
+ if "weight" in constraints:
1403
+ # Should be a 'dictionary' of letter-frequencies, but accept a weak composition
1404
+ w = constraints["weight"]
1405
+ if not isinstance(w, dict):
1406
+ # make sure we didn't receive ``some_dict.items()``
1407
+ if len(w) > 0 and isinstance(w[0], (list, tuple)):
1408
+ w = dict(w)
1409
+ else:
1410
+ w = {i+1: w[i] for i in range(len(w)) if w[i] > 0}
1411
+ if not all((a in ZZ and a > 0) for a in w.values()):
1412
+ raise ValueError("%s must be a dictionary of letter-frequencies or a weak composition" % w)
1413
+ else:
1414
+ constraints["weight"] = tuple(w.items())
1415
+
1416
+ if "alphabet" in constraints:
1417
+ A = constraints["alphabet"]
1418
+ if A in ZZ:
1419
+ A = range(1, A + 1)
1420
+ constraints["alphabet"] = frozenset(A)
1421
+
1422
+ if len(args) == 2: # treat as `alphabet` & `order`
1423
+ alph = args[0]
1424
+ order = args[1]
1425
+ if alph in ZZ:
1426
+ alph = range(1, alph + 1)
1427
+ if (alph and len(set(alph)) == len(alph)) and (order in ZZ and order >= 0):
1428
+ if "alphabet" in constraints:
1429
+ raise ValueError("cannot pass alphabet as first argument and keyword argument")
1430
+ elif "order" in constraints:
1431
+ raise ValueError("cannot pass order as second argument and keyword argument")
1432
+ if constraints == {}:
1433
+ return OrderedMultisetPartitionsIntoSets_alph_d(frozenset(alph), order)
1434
+ else:
1435
+ return OrderedMultisetPartitionsIntoSets_alph_d_constraints(frozenset(alph), order, **constraints)
1436
+ elif frozenset(alph) == frozenset() and order == 0:
1437
+ return OrderedMultisetPartitionsIntoSets_alph_d_constraints(frozenset(alph), order, **constraints)
1438
+ else:
1439
+ raise ValueError("alphabet=%s must be a nonempty set and order=%s must be a nonnegative integer" % (alph, order))
1440
+
1441
+ elif len(args) == 1: # treat as `size` or `multiset`
1442
+ X = args[0]
1443
+ if isinstance(X, (list, tuple)):
1444
+ tmp = {}
1445
+ for i in X:
1446
+ tmp[i] = tmp.get(i, 0) + 1
1447
+ X = tmp
1448
+ if isinstance(X, dict):
1449
+ over_determined = set(["size", "weight", "alphabet", "order", "min_order", "max_order"]).intersection(set(constraints))
1450
+ if over_determined:
1451
+ if len(over_determined) > 1:
1452
+ suff = "s"
1453
+ offenses = str(sorted(over_determined))
1454
+ else:
1455
+ suff = ""
1456
+ offenses = str(over_determined.pop())
1457
+ raise ValueError("cannot pass multiset as first argument and %s as keyword argument%s" % (offenses, suff))
1458
+ X_items = tuple(X.items())
1459
+ if constraints == {}:
1460
+ return OrderedMultisetPartitionsIntoSets_X(X_items)
1461
+ else:
1462
+ return OrderedMultisetPartitionsIntoSets_X_constraints(X_items, **constraints)
1463
+
1464
+ elif X in ZZ and X >= 0:
1465
+ if "size" in constraints:
1466
+ raise ValueError("cannot pass size as first argument and keyword argument")
1467
+ if constraints == {}:
1468
+ return OrderedMultisetPartitionsIntoSets_n(X)
1469
+ else:
1470
+ return OrderedMultisetPartitionsIntoSets_n_constraints(X, **constraints)
1471
+
1472
+ else:
1473
+ # zero arguments are passed?
1474
+ raise ValueError("%s must be a nonnegative integer or a list or dictionary representing a multiset" % X)
1475
+
1476
+ elif len(args) > 2:
1477
+ raise ValueError("OrderedMultisetPartitonsIntoSets takes 1, 2, or 3 arguments")
1478
+ else:
1479
+ # try to do better than a generic parent
1480
+ if "weight" in constraints:
1481
+ X = constraints.pop("weight")
1482
+ return OrderedMultisetPartitionsIntoSets(dict(X), **constraints)
1483
+ elif "size" in constraints:
1484
+ n = constraints.pop("size")
1485
+ return OrderedMultisetPartitionsIntoSets(n, **constraints)
1486
+ elif "alphabet" in constraints and "order" in constraints:
1487
+ A = constraints.pop("alphabet")
1488
+ d = constraints.pop("order")
1489
+ return OrderedMultisetPartitionsIntoSets(A, d, **constraints)
1490
+
1491
+ # generic parent
1492
+ return OrderedMultisetPartitionsIntoSets_all_constraints(**constraints)
1493
+
1494
+ def __init__(self, is_finite=None, **constraints):
1495
+ """
1496
+ Initialize ``self``.
1497
+
1498
+ TESTS::
1499
+
1500
+ sage: c = {"length":4, "max_order":6, "alphabet":[2,4,5,6]}
1501
+ sage: OrderedMultisetPartitionsIntoSets(**c).constraints
1502
+ {'alphabet': frozenset({2, 4, 5, 6}), 'length': 4, 'max_order': 6}
1503
+ sage: OrderedMultisetPartitionsIntoSets(17, **c).constraints
1504
+ {'alphabet': frozenset({2, 4, 5, 6}), 'length': 4, 'max_order': 6}
1505
+ sage: OrderedMultisetPartitionsIntoSets(17, **c).full_constraints
1506
+ {'alphabet': frozenset({2, 4, 5, 6}), 'length': 4, 'max_order': 6, 'size': 17}
1507
+
1508
+ sage: c = {"length":4, "min_length":5, "max_order":6, "order":5, "alphabet":4}
1509
+ sage: OrderedMultisetPartitionsIntoSets(**c).full_constraints
1510
+ {'alphabet': frozenset({1, 2, 3, 4}), 'length': 4, 'order': 5}
1511
+ sage: OrderedMultisetPartitionsIntoSets(**c).constraints
1512
+ {'length': 4}
1513
+ sage: OrderedMultisetPartitionsIntoSets(4, 5, **c).constraints
1514
+ Traceback (most recent call last):
1515
+ ...
1516
+ ValueError: cannot pass alphabet as first argument and keyword argument
1517
+
1518
+ sage: c = {"weight":[2,2,0,3], "min_length":5, "max_order":6, "order":5, "alphabet":4}
1519
+ sage: OrderedMultisetPartitionsIntoSets(**c).constraints
1520
+ Traceback (most recent call last):
1521
+ ...
1522
+ ValueError: cannot pass multiset as first argument and ['alphabet', 'max_order', 'order'] as keyword arguments
1523
+ """
1524
+ constraints = dict(constraints)
1525
+
1526
+ # standardize values for certain keywords
1527
+ if "alphabet" in constraints:
1528
+ if constraints["alphabet"] in ZZ:
1529
+ constraints["alphabet"] = frozenset(range(1, constraints["alphabet"]+1))
1530
+ else:
1531
+ constraints["alphabet"] = frozenset(constraints["alphabet"])
1532
+
1533
+ if "weight" in constraints:
1534
+ X = dict(constraints["weight"])
1535
+ constraints["weight"] = X
1536
+ constraints.pop("alphabet", None)
1537
+ constraints.pop("min_order", None)
1538
+ constraints.pop("order", None)
1539
+ constraints.pop("max_order", None)
1540
+ constraints.pop("size", None)
1541
+
1542
+ if "length" in constraints:
1543
+ constraints.pop("min_length", None)
1544
+ constraints.pop("max_length", None)
1545
+ min_k = constraints.get("min_length", 0)
1546
+ max_k = constraints.get("max_length", infinity)
1547
+ assert min_k <= max_k, "min_length=%s <= max_length=%s" % (min_k, max_k)
1548
+ if min_k == max_k:
1549
+ constraints["length"] = constraints.pop("min_length",
1550
+ constraints.pop("max_length"))
1551
+
1552
+ if "order" in constraints:
1553
+ constraints.pop("min_order", None)
1554
+ constraints.pop("max_order", None)
1555
+ min_ord = constraints.get("min_order", 0)
1556
+ max_ord = constraints.get("max_order", infinity)
1557
+ assert min_ord <= max_ord, "min_order=%s <= max_order=%s" % (min_ord, max_ord)
1558
+ if min_ord == max_ord:
1559
+ constraints["order"] = constraints.pop("min_order",
1560
+ constraints.pop("max_order"))
1561
+
1562
+ # pop keys with empty values, with the exception of 'size' or 'order'
1563
+ self.constraints = {}
1564
+ for (key,val) in constraints.items():
1565
+ if val:
1566
+ self.constraints[key] = val
1567
+ elif key in ("size", "order", "length") and val is not None:
1568
+ self.constraints[key] = val
1569
+
1570
+ self.full_constraints = dict(self.constraints)
1571
+ if hasattr(self, "_X"):
1572
+ self.full_constraints["weight"] = dict(self._X)
1573
+ self.constraints.pop("weight", None)
1574
+ if hasattr(self, "_n"):
1575
+ self.full_constraints["size"] = self._n
1576
+ self.constraints.pop("size", None)
1577
+ if hasattr(self, "_alphabet"):
1578
+ self.full_constraints["alphabet"] = self._alphabet
1579
+ self.constraints.pop("alphabet", None)
1580
+ self.full_constraints["order"] = self._order
1581
+ self.constraints.pop("order", None)
1582
+
1583
+ if is_finite or _is_finite(constraints):
1584
+ Parent.__init__(self, category=FiniteEnumeratedSets())
1585
+ else:
1586
+ Parent.__init__(self, category=InfiniteEnumeratedSets())
1587
+
1588
+ def _repr_(self):
1589
+ """
1590
+ Return a string representation of ``self``.
1591
+
1592
+ TESTS::
1593
+
1594
+ sage: OrderedMultisetPartitionsIntoSets()
1595
+ Ordered Multiset Partitions into Sets
1596
+ """
1597
+ return "Ordered Multiset Partitions into Sets"
1598
+
1599
+ def _constraint_repr_(self, cdict=None):
1600
+ """
1601
+ Return a string representation of all constraints
1602
+ appearing within ``self.constraints``.
1603
+
1604
+ A helper method for ``self._repr_()``.
1605
+
1606
+ EXAMPLES::
1607
+
1608
+ sage: OMPs = OrderedMultisetPartitionsIntoSets()
1609
+ sage: c = {"length":4, "max_order":6, "alphabet":frozenset([2,4,5,6])}
1610
+ sage: OMPs._constraint_repr_(c)
1611
+ ' with constraints: alphabet={2, 4, 5, 6}, length=4, max_order=6'
1612
+ sage: c = {"size":14}
1613
+ sage: OMPs._constraint_repr_(c)
1614
+ ' with constraint: size=14'
1615
+ """
1616
+ if not cdict:
1617
+ cdict = dict(self.constraints)
1618
+ if "alphabet" in cdict:
1619
+ # make, e.g., `set([2,3,4])` print as `{2, 3, 4}`
1620
+ if not all(l in ZZ for l in cdict["alphabet"]):
1621
+ A = sorted(cdict["alphabet"], key=str)
1622
+ else:
1623
+ A = sorted(cdict["alphabet"])
1624
+ cdict["alphabet"] = "{" + repr(A)[1:-1] + "}"
1625
+ constr = ""
1626
+ ss = ['%s=%s' % item for item in cdict.items()]
1627
+ ss = sorted(ss)
1628
+ if len(ss) > 1:
1629
+ constr = " with constraints: " + ", ".join(ss)
1630
+ elif len(ss) == 1:
1631
+ constr = " with constraint: " + ", ".join(ss)
1632
+ return constr
1633
+
1634
+ def _element_constructor_(self, lst):
1635
+ """
1636
+ Construct an element of ``self`` from ``lst``.
1637
+
1638
+ EXAMPLES::
1639
+
1640
+ sage: P = OrderedMultisetPartitionsIntoSets()
1641
+ sage: A = P([[3],[3,1]]) ; A # indirect doctest
1642
+ [{3}, {1,3}]
1643
+ sage: P1 = OrderedMultisetPartitionsIntoSets(7, alphabet=3)
1644
+ sage: A1 = P1([[3],[3,1]]); A1
1645
+ [{3}, {1,3}]
1646
+ sage: P2 = OrderedMultisetPartitionsIntoSets(alphabet=3)
1647
+ sage: A2 = P2([[3],[3,1]]); A2
1648
+ [{3}, {1,3}]
1649
+ sage: A == A1 == A2
1650
+ True
1651
+ sage: P = OrderedMultisetPartitionsIntoSets(3)
1652
+ sage: P([[3],[3,1]])
1653
+ Traceback (most recent call last):
1654
+ ...
1655
+ ValueError: cannot convert [[3], [3, 1]] into an element of
1656
+ Ordered Multiset Partitions into Sets of integer 3
1657
+ """
1658
+ if not lst:
1659
+ omp = []
1660
+ else:
1661
+ omp = [list(z) for z in lst]
1662
+
1663
+ if omp in self:
1664
+ return self.element_class(self, list(map(frozenset, omp)))
1665
+ else:
1666
+ raise ValueError("cannot convert %s into an element of %s" % (lst, self))
1667
+
1668
+ Element = OrderedMultisetPartitionIntoSets
1669
+
1670
+ def __contains__(self, x):
1671
+ """
1672
+ Return if ``x`` is contained in ``self``.
1673
+
1674
+ TESTS::
1675
+
1676
+ sage: [[2,1], [1,3]] in OrderedMultisetPartitionsIntoSets()
1677
+ True
1678
+ sage: [[2,1], [1,3]] in OrderedMultisetPartitionsIntoSets(7)
1679
+ True
1680
+ sage: [[2,2], [1,3]] in OrderedMultisetPartitionsIntoSets()
1681
+ False
1682
+ sage: [] in OrderedMultisetPartitionsIntoSets()
1683
+ True
1684
+ sage: [] in OrderedMultisetPartitionsIntoSets(0)
1685
+ True
1686
+ sage: [] in OrderedMultisetPartitionsIntoSets(2)
1687
+ False
1688
+ sage: [[2, 1]] in OrderedMultisetPartitionsIntoSets(3, length=2)
1689
+ False
1690
+ sage: [[2, -1]] in OrderedMultisetPartitionsIntoSets()
1691
+ True
1692
+ """
1693
+ if not isinstance(x, (OrderedMultisetPartitionIntoSets, list, tuple)):
1694
+ return False
1695
+ return _has_nonempty_sets(x) and self._satisfies_constraints(x)
1696
+
1697
+ def _satisfies_constraints(self, x):
1698
+ """
1699
+ Check whether or not ``x`` satisfies all of the constraints
1700
+ appearing within ``self.full_constraints`` (Boolean output).
1701
+
1702
+ .. NOTE::
1703
+
1704
+ This test will cause an infinite recursion with
1705
+ ``self._element_constructor_()`` if the ``__contains__``
1706
+ method in ``OrderedMultisetPartitionsIntoSets_X`` is removed.
1707
+
1708
+ TESTS::
1709
+
1710
+ sage: c = {"length":3, "max_order":5, "alphabet":[1,2,4], "size":12}
1711
+ sage: OMPs = OrderedMultisetPartitionsIntoSets(**c)
1712
+ sage: OMPs._satisfies_constraints([{2,4}, {1}, {1,4}])
1713
+ True
1714
+ sage: failures = {((2,4), (2,4)), ((1,2,4), (1,), (1,4)),
1715
+ ....: ((2,4), (3,), (3,)), ((2,4), (1,), (2,4))}
1716
+ sage: any(OMPs._satisfies_constraints(x) for x in failures)
1717
+ False
1718
+ sage: c = {"max_length":4, "weight":{1:2, 2:1, 4:2}}
1719
+ sage: OMPs = OrderedMultisetPartitionsIntoSets(**c)
1720
+ sage: OMPs._satisfies_constraints([{2,4}, {1}, {1,4}])
1721
+ True
1722
+ sage: failures = {((2,), (4,), (1,), (1,), (4,)), ((1,), (1,), (2,4), (2,4))}
1723
+ sage: any(OMPs._satisfies_constraints(x) for x in failures)
1724
+ False
1725
+ """
1726
+ X = _concatenate(x)
1727
+ P = OrderedMultisetPartitionsIntoSets_X(tuple(_get_weight(X).items()))
1728
+ x = P.element_class(P, [frozenset(block) for block in x])
1729
+ constr = self.full_constraints
1730
+ tsts = []
1731
+ if 'size' in constr:
1732
+ tsts.append( x.size() == constr['size'] )
1733
+ if 'weight' in constr:
1734
+ tsts.append( x.weight() == constr['weight'] )
1735
+ if 'alphabet' in constr:
1736
+ tsts.append( frozenset(x.letters()).issubset(constr['alphabet']) )
1737
+ if 'length' in constr:
1738
+ tsts.append( x.length() == constr['length'] )
1739
+ if 'min_length' in constr:
1740
+ tsts.append( x.length() >= constr['min_length'] )
1741
+ if 'max_length' in constr:
1742
+ tsts.append( x.length() <= constr['max_length'] )
1743
+ if 'order' in constr:
1744
+ tsts.append( x.order() == constr['order'] )
1745
+ if 'min_order' in constr:
1746
+ tsts.append( x.order() >= constr['min_order'] )
1747
+ if 'max_order' in constr:
1748
+ tsts.append( x.order() <= constr['max_order'] )
1749
+
1750
+ return all(tsts)
1751
+
1752
+ def _from_list(self, lst):
1753
+ """
1754
+ Return an ordered multiset partition into sets of singleton blocks, whose
1755
+ singletons are the elements ``lst``.
1756
+
1757
+ If any of the elements of ``lst`` are zero (or '0'), then use
1758
+ these as breaks points for the blocks.
1759
+
1760
+ .. SEEALSO::
1761
+
1762
+ :meth:`OrderedMultisetPartitionsIntoSets._from_list_with_zeros()`.
1763
+
1764
+ INPUT:
1765
+
1766
+ - ``lst`` -- an iterable
1767
+
1768
+ EXAMPLES::
1769
+
1770
+ sage: OMPs = OrderedMultisetPartitionsIntoSets()
1771
+ sage: OMPs._from_list([1,4,0,8])
1772
+ [{1,4}, {8}]
1773
+ sage: OMPs._from_list([1,4,8])
1774
+ [{1}, {4}, {8}]
1775
+ sage: OMPs._from_list([1,4,8,0]) == OrderedMultisetPartitionIntoSets([[1,4,8]])
1776
+ True
1777
+ sage: OMPs._from_list('abaa')
1778
+ [{'a'}, {'b'}, {'a'}, {'a'}]
1779
+ sage: OMPs._from_list('ab0a0a')
1780
+ [{'a','b'}, {'a'}, {'a'}]
1781
+
1782
+ TESTS::
1783
+
1784
+ sage: OMPs._from_list([1,0,2,3,1]) == OrderedMultisetPartitionIntoSets([[1], [2,3,1]])
1785
+ True
1786
+ sage: OMPs._from_list([1,2,'3',0,1]) == OrderedMultisetPartitionIntoSets([{1,2,'3'}, [1]])
1787
+ True
1788
+ """
1789
+ if all(a in ZZ for a in lst) and any(a < 0 for a in lst):
1790
+ raise ValueError("`_from_list` does not expect to see negative integers; received {}".format(str(lst)))
1791
+ if 0 in list(lst) or '0' in list(lst):
1792
+ return self._from_list_with_zeros(lst)
1793
+
1794
+ d = [frozenset([x]) for x in lst]
1795
+ c = self.element_class(self, d)
1796
+ # give a better parent, if self is generic
1797
+ if isinstance(self, OrderedMultisetPartitionsIntoSets_all_constraints):
1798
+ P = OrderedMultisetPartitionsIntoSets(_get_weight(lst))
1799
+ return P.element_class(P, c)
1800
+ else:
1801
+ return self.element_class(self, c)
1802
+
1803
+ def _from_list_with_zeros(self, lst_with_zeros):
1804
+ r"""
1805
+ Return an ordered multiset partition into sets from a list of nonnegative
1806
+ integers (or their string equivalents).
1807
+
1808
+ Blocks are separated by zeros. Consecutive zeros are ignored.
1809
+
1810
+ EXAMPLES::
1811
+
1812
+ sage: OrderedMultisetPartitionsIntoSets()._from_list([1,2,4])
1813
+ [{1}, {2}, {4}]
1814
+ sage: OrderedMultisetPartitionsIntoSets()._from_list_with_zeros([1,2,4])
1815
+ [{1,2,4}]
1816
+ sage: OrderedMultisetPartitionsIntoSets()._from_list_with_zeros([1,0,2,0,0,4])
1817
+ [{1}, {2}, {4}]
1818
+ sage: OrderedMultisetPartitionsIntoSets()._from_list_with_zeros('abc00a0b')
1819
+ [{'a','b','c'}, {'a'}, {'b'}]
1820
+ """
1821
+ from_zero_lst = list(lst_with_zeros)
1822
+ if from_zero_lst[-1] not in {0, '0'}:
1823
+ from_zero_lst += [0]
1824
+ co = []
1825
+ block = []
1826
+ for a in from_zero_lst:
1827
+ if a in {0, '0'}:
1828
+ if block:
1829
+ co.append(block)
1830
+ block = []
1831
+ else:
1832
+ block.append(a)
1833
+ if co in self:
1834
+ c = self.element_class(self, map(frozenset, co))
1835
+ # give a better parent, if `self` is generic
1836
+ if isinstance(self, OrderedMultisetPartitionsIntoSets_all_constraints):
1837
+ P = OrderedMultisetPartitionsIntoSets(c.weight())
1838
+ return P.element_class(P, c)
1839
+ else:
1840
+ return c
1841
+ else:
1842
+ raise ValueError("ordered multiset partitions into sets do not have repeated entries within blocks (%s received)" % str(co))
1843
+
1844
+ def __iter__(self):
1845
+ """
1846
+ Iterate over ordered multiset partitions into sets.
1847
+
1848
+ EXAMPLES::
1849
+
1850
+ sage: OrderedMultisetPartitionsIntoSets(3).list()
1851
+ [[{3}], [{1,2}], [{2}, {1}], [{1}, {2}], [{1}, {1}, {1}]]
1852
+ sage: OrderedMultisetPartitionsIntoSets(0).list()
1853
+ [[]]
1854
+ sage: C = OrderedMultisetPartitionsIntoSets()
1855
+ sage: it = C.__iter__()
1856
+ sage: [next(it) for i in range(16)]
1857
+ [[], [{1}], [{2}], [{1}, {1}], [{3}], [{1,2}], [{2}, {1}],
1858
+ [{1}, {2}], [{1}, {1}, {1}], [{4}], [{1,3}], [{3}, {1}],
1859
+ [{1,2}, {1}], [{2}, {2}], [{2}, {1}, {1}], [{1}, {3}]]
1860
+
1861
+ TESTS::
1862
+
1863
+ sage: OrderedMultisetPartitionsIntoSets(alphabet=[1,3], max_length=2).list()
1864
+ [[], [{1}], [{3}], [{1,3}], [{1}, {1}], [{1}, {3}],
1865
+ [{3}, {1}], [{3}, {3}], [{1,3}, {1}], [{1,3}, {3}],
1866
+ [{1}, {1,3}], [{3}, {1,3}], [{1,3}, {1,3}]]
1867
+ sage: C = OrderedMultisetPartitionsIntoSets(min_length=2, max_order=2)
1868
+ sage: it = C.__iter__()
1869
+ sage: [next(it) for i in range(15)]
1870
+ [[{1}, {1}], [{2}, {1}], [{1}, {2}], [{3}, {1}], [{2}, {2}],
1871
+ [{1}, {3}], [{4}, {1}], [{3}, {2}], [{2}, {3}], [{1}, {4}],
1872
+ [{5}, {1}], [{4}, {2}], [{3}, {3}], [{2}, {4}], [{1}, {5}]]
1873
+ sage: OrderedMultisetPartitionsIntoSets(alphabet=[1,3], min_length=2).list()
1874
+ Traceback (most recent call last):
1875
+ ...
1876
+ NotImplementedError: cannot list an infinite set
1877
+ """
1878
+ # Look for evidence of ``FiniteEnumeratedSets()`` among constraints.
1879
+ # ``_base_iterator`` ignores most constraints in ``self.full_constraints``.
1880
+ iterator = _base_iterator(self.full_constraints)
1881
+ if iterator:
1882
+ for co in iterator:
1883
+ if self._satisfies_constraints(co):
1884
+ yield self.element_class(self, co)
1885
+ else:
1886
+ # iterate over blocks of letters over an alphabet
1887
+ if "alphabet" in self.constraints:
1888
+ A = self.constraints["alphabet"]
1889
+ # establish a cutoff order ``max_ell``
1890
+ max = self.constraints.get("max_length", infinity)
1891
+ max = self.constraints.get("length", max)
1892
+ max = max * len(A)
1893
+ max = self.constraints.get("max_order", max)
1894
+ max_ell = self.constraints.get("order", max)
1895
+ ell = 0
1896
+ while True and ell <= max_ell:
1897
+ for co in _iterator_order(A, ell):
1898
+ if self._satisfies_constraints(co):
1899
+ yield self.element_class(self, co)
1900
+ ell += 1
1901
+ # or iterate over partitions of multisets of positive integers
1902
+ else:
1903
+ n = 0
1904
+ while True:
1905
+ for co in _iterator_size(n):
1906
+ if self._satisfies_constraints(co):
1907
+ yield self.element_class(self, co)
1908
+ n += 1
1909
+
1910
+ def subset(self, size):
1911
+ """
1912
+ Return a subset of all ordered multiset partitions into sets.
1913
+
1914
+ INPUT:
1915
+
1916
+ - ``size`` -- integer representing a slice of all ordered
1917
+ multiset partitions into sets
1918
+
1919
+ The slice alluded to above is taken with respect to length, or
1920
+ to order, or to size, depending on the constraints of ``self``.
1921
+
1922
+ EXAMPLES::
1923
+
1924
+ sage: C = OrderedMultisetPartitionsIntoSets(weight={2:2, 3:1, 5:1})
1925
+ sage: C.subset(3)
1926
+ Ordered Multiset Partitions into Sets of multiset {{2, 2, 3, 5}} with constraint: length=3
1927
+ sage: C = OrderedMultisetPartitionsIntoSets(weight={2:2, 3:1, 5:1}, min_length=2)
1928
+ sage: C.subset(3)
1929
+ Ordered Multiset Partitions into Sets of multiset {{2, 2, 3, 5}} with constraint: length=3
1930
+ sage: C = OrderedMultisetPartitionsIntoSets(alphabet=[2,3,5])
1931
+ sage: C.subset(3)
1932
+ Ordered Multiset Partitions into Sets of order 3 over alphabet {2, 3, 5}
1933
+ sage: C = OrderedMultisetPartitionsIntoSets(order=5)
1934
+ sage: C.subset(3)
1935
+ Ordered Multiset Partitions into Sets of integer 3 with constraint: order=5
1936
+ sage: C = OrderedMultisetPartitionsIntoSets(alphabet=[2,3,5], order=5, length=3)
1937
+ sage: C.subset(3)
1938
+ Ordered Multiset Partitions into Sets of order 3 over alphabet {2, 3, 5} with constraint: length=3
1939
+ sage: C = OrderedMultisetPartitionsIntoSets()
1940
+ sage: C.subset(3)
1941
+ Ordered Multiset Partitions into Sets of integer 3
1942
+ sage: C.subset(3) == OrderedMultisetPartitionsIntoSets(3)
1943
+ True
1944
+ """
1945
+ fc = self.full_constraints
1946
+
1947
+ # slice by 'length'
1948
+ if "weight" in fc:
1949
+ return OrderedMultisetPartitionsIntoSets(fc["weight"], length=size, **self.constraints)
1950
+ elif "alphabet" in fc and "size" in fc:
1951
+ add_length = dict(self.constraints)
1952
+ add_length["length"] = size
1953
+ return OrderedMultisetPartitionsIntoSets(fc["alphabet"], fc["order"], **add_length)
1954
+
1955
+ # slice by 'order'
1956
+ if "alphabet" in fc:
1957
+ no_alpha = {k: v for (k, v) in self.constraints.items() if k != "alphabet"}
1958
+ return OrderedMultisetPartitionsIntoSets(fc["alphabet"], size, **no_alpha)
1959
+
1960
+ # slice by 'size'
1961
+ return OrderedMultisetPartitionsIntoSets(size, **self.constraints)
1962
+
1963
+ ###############
1964
+
1965
+
1966
+ class OrderedMultisetPartitionsIntoSets_all_constraints(OrderedMultisetPartitionsIntoSets):
1967
+ r"""
1968
+ All ordered multiset partitions into sets (with or without constraints).
1969
+
1970
+ EXAMPLES::
1971
+
1972
+ sage: C = OrderedMultisetPartitionsIntoSets(); C
1973
+ Ordered Multiset Partitions into Sets
1974
+ sage: [[1],[1,'a']] in C
1975
+ True
1976
+
1977
+ sage: OrderedMultisetPartitionsIntoSets(weight=[2,0,1], length=2)
1978
+ Ordered Multiset Partitions into Sets of multiset {{1, 1, 3}} with constraint: length=2
1979
+
1980
+ TESTS::
1981
+
1982
+ sage: OMP = OrderedMultisetPartitionsIntoSets()
1983
+ sage: TestSuite(OMP).run() # long time
1984
+
1985
+ sage: C = OrderedMultisetPartitionsIntoSets(weight=[2,0,1], length=2)
1986
+ sage: TestSuite(C).run()
1987
+
1988
+ sage: D1 = OrderedMultisetPartitionsIntoSets(weight={1:2, 3:1}, min_length=2, max_length=2)
1989
+ sage: D2 = OrderedMultisetPartitionsIntoSets({1:2, 3:1}, min_length=2, max_length=2)
1990
+ sage: D3 = OrderedMultisetPartitionsIntoSets(5, weight={1:2, 3:1}, length=2)
1991
+ sage: D4 = OrderedMultisetPartitionsIntoSets([1,3], 3, weight={1:2, 3:1}, length=2)
1992
+ sage: D5 = OrderedMultisetPartitionsIntoSets([1,3], 3, size=5, length=2)
1993
+ sage: all(C != D for D in [D1, D2, D3, D4, D5])
1994
+ True
1995
+ sage: all(Set(C) == Set(D) for D in [D1, D2, D3, D4, D5])
1996
+ True
1997
+ sage: E = OrderedMultisetPartitionsIntoSets({1:2, 3:1}, min_length=2)
1998
+ sage: Set(C) == Set(E)
1999
+ False
2000
+ """
2001
+
2002
+ def _repr_(self):
2003
+ """
2004
+ Return a string representation of ``self``.
2005
+
2006
+ TESTS::
2007
+
2008
+ sage: OrderedMultisetPartitionsIntoSets(min_length=3, max_order=5)
2009
+ Ordered Multiset Partitions into Sets with constraints: max_order=5, min_length=3
2010
+ sage: OrderedMultisetPartitionsIntoSets(min_length=3, max_order=5, alphabet=[1,'a'])
2011
+ Ordered Multiset Partitions into Sets with constraints:
2012
+ alphabet={1, 'a'}, max_order=5, min_length=3
2013
+ """
2014
+ return "Ordered Multiset Partitions into Sets" + self._constraint_repr_()
2015
+
2016
+ ###############
2017
+
2018
+
2019
+ class OrderedMultisetPartitionsIntoSets_n(OrderedMultisetPartitionsIntoSets):
2020
+ """
2021
+ Ordered multiset partitions into sets of a fixed integer `n`.
2022
+ """
2023
+
2024
+ def __init__(self, n):
2025
+ """
2026
+ Initialize ``self``.
2027
+
2028
+ TESTS::
2029
+
2030
+ sage: C = OrderedMultisetPartitionsIntoSets(Integer(4))
2031
+ sage: TestSuite(C).run()
2032
+ sage: C2 = OrderedMultisetPartitionsIntoSets(int(4))
2033
+ sage: C is C2
2034
+ True
2035
+ sage: C3 = OrderedMultisetPartitionsIntoSets(7/2)
2036
+ Traceback (most recent call last):
2037
+ ...
2038
+ ValueError: 7/2 must be a nonnegative integer or a list or
2039
+ dictionary representing a multiset
2040
+ """
2041
+ self._n = n
2042
+ OrderedMultisetPartitionsIntoSets.__init__(self, True)
2043
+
2044
+ def _repr_(self):
2045
+ """
2046
+ Return a string representation of ``self``.
2047
+
2048
+ TESTS::
2049
+
2050
+ sage: OrderedMultisetPartitionsIntoSets(3)
2051
+ Ordered Multiset Partitions into Sets of integer 3
2052
+ """
2053
+ return "Ordered Multiset Partitions into Sets of integer %s" % self._n
2054
+
2055
+ def cardinality(self):
2056
+ """
2057
+ Return the number of elements in ``self``.
2058
+
2059
+ TESTS::
2060
+
2061
+ sage: len(OrderedMultisetPartitionsIntoSets(10).list())
2062
+ 1500
2063
+ sage: OrderedMultisetPartitionsIntoSets(10).cardinality()
2064
+ 1500
2065
+ """
2066
+ # Dispense with the complex computation for small orders.
2067
+ if self._n <= 5:
2068
+ orders = {0: 1, 1: 1, 2: 2, 3: 5, 4: 11, 5: 25}
2069
+ return ZZ(orders[self._n])
2070
+
2071
+ # We view an ordered multiset partition into sets as a list of 2-regular integer partitions.
2072
+ #
2073
+ # The 2-regular partitions have a nice generating function (see OEIS:A000009).
2074
+ # Below, we take (products of) coefficients of polynomials to compute cardinality.
2075
+ t = PowerSeriesRing(ZZ, 't').gen().O(self._n + 1)
2076
+ partspoly = prod(1 + t**k for k in range(1, self._n + 1)).dict()
2077
+ deg = 0
2078
+ for alpha in composition_iterator_fast(self._n):
2079
+ deg += prod(partspoly[d] for d in alpha)
2080
+ return ZZ(deg)
2081
+
2082
+ def _an_element_(self):
2083
+ """
2084
+ Return a typical element of ``self``.
2085
+
2086
+ EXAMPLES::
2087
+
2088
+ sage: OrderedMultisetPartitionsIntoSets(13).an_element()
2089
+ [{2,3}, {2,3}, {1,2}]
2090
+ sage: OrderedMultisetPartitionsIntoSets(14).an_element()
2091
+ [{2,3}, {2,3}, {4}]
2092
+ """
2093
+ #output will have at most three blocks, each of size 1, 2, or 3.
2094
+ alpha = Compositions(self._n, max_part=self._n//3+1).an_element()
2095
+ out = []
2096
+ for a in alpha:
2097
+ if a in {1, 2, 4}:
2098
+ out.append([a])
2099
+ else:
2100
+ if a % 2:
2101
+ out.append([a//2+1, a//2])
2102
+ else:
2103
+ out.append([a//2, a//2-1, 1])
2104
+ return self.element_class(self, map(frozenset, out))
2105
+
2106
+ def random_element(self):
2107
+ """
2108
+ Return a random element of ``self``.
2109
+
2110
+ This method does not return elements of ``self`` with uniform probability,
2111
+ but it does cover all elements. The scheme is as follows:
2112
+
2113
+ - produce a random composition `C`;
2114
+ - choose a random partition of `c` into distinct parts for each `c` in `C`.
2115
+
2116
+ EXAMPLES::
2117
+
2118
+ sage: OrderedMultisetPartitionsIntoSets(5).random_element() # random
2119
+ [{1,2}, {1}, {1}]
2120
+ sage: OrderedMultisetPartitionsIntoSets(5).random_element() # random
2121
+ [{2}, {1,2}]
2122
+
2123
+ sage: OMP = OrderedMultisetPartitionsIntoSets(5)
2124
+ sage: d = {}
2125
+ sage: for _ in range(1100):
2126
+ ....: x = OMP.random_element()
2127
+ ....: d[x] = d.get(x, 0) + 1
2128
+ sage: d.values() # random
2129
+ [72, 73, 162, 78, 135, 75, 109, 65, 135, 134, 62]
2130
+ """
2131
+ C = Compositions(self._n).random_element()
2132
+ co = [IntegerListsLex(c, min_part=1, max_part=c,
2133
+ min_slope=1).random_element() for c in C]
2134
+ return self.element_class(self, map(frozenset, co))
2135
+
2136
+ def __iter__(self):
2137
+ """
2138
+ Iterate over ``self``.
2139
+
2140
+ TESTS::
2141
+
2142
+ sage: O = OrderedMultisetPartitionsIntoSets(6)
2143
+ sage: it = O.__iter__()
2144
+ sage: [next(it) for _ in range(10)]
2145
+ [[{6}], [{2,4}], [{1,5}], [{1,2,3}],
2146
+ [{5}, {1}], [{2,3}, {1}], [{1,4}, {1}],
2147
+ [{4}, {2}], [{1,3}, {2}], [{4}, {1}, {1}]]
2148
+ """
2149
+ for co in _iterator_size(self._n):
2150
+ yield self.element_class(self, co)
2151
+
2152
+
2153
+ class OrderedMultisetPartitionsIntoSets_n_constraints(OrderedMultisetPartitionsIntoSets):
2154
+ """
2155
+ Class of ordered multiset partitions into sets of a fixed integer `n`
2156
+ satisfying constraints.
2157
+ """
2158
+
2159
+ def __init__(self, n, **constraints):
2160
+ """
2161
+ Mimic class ``OrderedMultisetPartitionsIntoSets_n`` to initialize.
2162
+
2163
+ TESTS::
2164
+
2165
+ sage: C = OrderedMultisetPartitionsIntoSets(6, length=3)
2166
+ sage: TestSuite(C).run()
2167
+
2168
+ sage: C = OrderedMultisetPartitionsIntoSets(6, weight=[3,0,1], length=3)
2169
+ sage: TestSuite(C).run()
2170
+ """
2171
+ self._n = n
2172
+ OrderedMultisetPartitionsIntoSets.__init__(self, True, size=n, **constraints)
2173
+
2174
+ def _repr_(self):
2175
+ """
2176
+ Return a string representation of ``self``.
2177
+
2178
+ EXAMPLES::
2179
+
2180
+ sage: O = OrderedMultisetPartitionsIntoSets(14, length=4, max_order=6, alphabet={2,4,5,6})
2181
+ sage: O
2182
+ Ordered Multiset Partitions into Sets of integer 14 with constraints:
2183
+ alphabet={2, 4, 5, 6}, length=4, max_order=6
2184
+ """
2185
+ cdict = dict(self.constraints)
2186
+ cdict.pop("size", None)
2187
+ base_repr = "Ordered Multiset Partitions into Sets of integer %s" % self._n
2188
+ return base_repr + self._constraint_repr_(cdict)
2189
+
2190
+ ###############
2191
+
2192
+
2193
+ class OrderedMultisetPartitionsIntoSets_X(OrderedMultisetPartitionsIntoSets):
2194
+ """
2195
+ Class of ordered multiset partitions into sets of a fixed multiset `X`.
2196
+ """
2197
+
2198
+ def __init__(self, X):
2199
+ """
2200
+ Initialize ``self``.
2201
+
2202
+ TESTS::
2203
+
2204
+ sage: C = OrderedMultisetPartitionsIntoSets([1,1,4])
2205
+ sage: TestSuite(C).run()
2206
+
2207
+ sage: C2 = OrderedMultisetPartitionsIntoSets({1:2, 4:1})
2208
+ sage: C is C2
2209
+ True
2210
+ """
2211
+ self._X = X
2212
+ # sort the multiset
2213
+ if all((k in ZZ and k > 0) for (k,v) in X):
2214
+ self._Xtup = tuple([k for (k,v) in sorted(X) for _ in range(v)])
2215
+ else:
2216
+ self._Xtup = tuple([k for (k,v) in sorted(X, key=str) for _ in range(v)])
2217
+ OrderedMultisetPartitionsIntoSets.__init__(self, True)
2218
+
2219
+ def _repr_(self):
2220
+ """
2221
+ Return a string representation of ``self``.
2222
+
2223
+ TESTS::
2224
+
2225
+ sage: repr(OrderedMultisetPartitionsIntoSets([1,1,4]))
2226
+ 'Ordered Multiset Partitions into Sets of multiset {{1, 1, 4}}'
2227
+ """
2228
+ ms_rep = "{{" + ", ".join(map(str, self._Xtup)) + "}}"
2229
+ return "Ordered Multiset Partitions into Sets" + " of multiset %s" % ms_rep
2230
+
2231
+ def __contains__(self, x):
2232
+ """
2233
+ Return if ``x`` is contained in ``self``.
2234
+
2235
+ TESTS::
2236
+
2237
+ sage: from sage.combinat.multiset_partition_into_sets_ordered import OrderedMultisetPartitionsIntoSets_X as OMPX
2238
+ sage: [[2,1], [1,3]] in OMPX(((1,2), (2,1), (3,1)))
2239
+ True
2240
+ sage: co = OrderedMultisetPartitionIntoSets([[2,1], [1,3]])
2241
+ sage: co in OMPX(((1,2), (2,1), (3,1)))
2242
+ True
2243
+ sage: [[2,1], [2,3]] in OMPX(((1,2), (2,1), (3,1)))
2244
+ False
2245
+ sage: [] in OMPX(())
2246
+ True
2247
+ sage: [[2, -1], [2,'a']] in OMPX(((2,2), (-1,1), ('a',1)))
2248
+ True
2249
+ """
2250
+ if not isinstance(x, (OrderedMultisetPartitionIntoSets, list, tuple)):
2251
+ return False
2252
+
2253
+ x_Xtup = sorted(_concatenate(x), key=str)
2254
+ self_Xtup = sorted(self._Xtup, key=str)
2255
+ return _has_nonempty_sets(x) and x_Xtup == self_Xtup
2256
+
2257
+ def cardinality(self):
2258
+ """
2259
+ Return the number of ordered partitions of multiset ``X``.
2260
+
2261
+ TESTS::
2262
+
2263
+ sage: len(OrderedMultisetPartitionsIntoSets([2,2,2,3,4,5]).list())
2264
+ 535
2265
+ sage: OrderedMultisetPartitionsIntoSets([2,2,2,3,4,5]).cardinality()
2266
+ 535
2267
+ """
2268
+ if self._Xtup == ():
2269
+ return ZZ(0)
2270
+
2271
+ # We build ordered multiset partitions into sets of `X` by permutation + deconcatenation
2272
+ deg = 0
2273
+ for alpha in Permutations_mset(self._Xtup):
2274
+ fattest = _break_at_descents(alpha)
2275
+ deg += prod(2**(len(k)-1) for k in fattest)
2276
+ return ZZ(deg)
2277
+
2278
+ def _an_element_(self):
2279
+ """
2280
+ Return a typical element of ``self``.
2281
+
2282
+ EXAMPLES::
2283
+
2284
+ sage: OrderedMultisetPartitionsIntoSets([2,2,2,3,4,5]).an_element()
2285
+ [{2}, {2}, {2,3,4}, {5}]
2286
+ sage: OrderedMultisetPartitionsIntoSets([2,2,2,3,4,4,5]).an_element()
2287
+ [{2}, {2}, {2,3}, {4}, {4,5}]
2288
+ """
2289
+ if not self._Xtup:
2290
+ return self.element_class(self, [])
2291
+ alpha = Permutations_mset(self._Xtup).an_element()
2292
+ co = _break_at_descents(alpha)
2293
+
2294
+ # construct "an element" by breaking the first fat block of `co` in two
2295
+ elt = []
2296
+ for i in range(len(co)):
2297
+ if len(co[i]) == 1:
2298
+ elt.append(co[i])
2299
+ else:
2300
+ break
2301
+ elt.append(co[i][:len(co[i])//2 + 1])
2302
+ elt.append(co[i][len(co[i])//2 + 1:])
2303
+ elt.extend(co[i+1:])
2304
+ return self.element_class(self, map(frozenset, elt))
2305
+
2306
+ def random_element(self):
2307
+ """
2308
+ Return a random element of ``self``.
2309
+
2310
+ This method does not return elements of ``self`` with uniform probability,
2311
+ but it does cover all elements. The scheme is as follows:
2312
+
2313
+ - produce a random permutation ``p`` of the multiset;
2314
+ - create blocks of an OMP ``fat`` by breaking ``p`` after non-ascents;
2315
+ - take a random element of ``fat.finer()``.
2316
+
2317
+ EXAMPLES::
2318
+
2319
+ sage: OrderedMultisetPartitionsIntoSets([1,1,3]).random_element() # random
2320
+ [{1}, {1,3}]
2321
+ sage: OrderedMultisetPartitionsIntoSets([1,1,3]).random_element() # random
2322
+ [{3}, {1}, {1}]
2323
+
2324
+ sage: OMP = OrderedMultisetPartitionsIntoSets([1,1,3,3])
2325
+ sage: d = {}
2326
+ sage: for _ in range(1000):
2327
+ ....: x = OMP.random_element()
2328
+ ....: d[x] = d.get(x, 0) + 1
2329
+ sage: d.values() # random
2330
+ [102, 25, 76, 24, 66, 88, 327, 27, 83, 83, 239, 72, 88]
2331
+ """
2332
+ if not self._Xtup:
2333
+ return self.element_class(self, [])
2334
+
2335
+ alpha = Permutations_mset(self._Xtup).random_element()
2336
+ co = _break_at_descents(alpha)
2337
+ finer = self.element_class(self, map(frozenset,co)).finer()
2338
+ return FiniteEnumeratedSets()(finer).random_element()
2339
+
2340
+ def __iter__(self):
2341
+ """
2342
+ Iterate over ``self``.
2343
+
2344
+ TESTS::
2345
+
2346
+ sage: O = OrderedMultisetPartitionsIntoSets(['a', 'b', 'a'])
2347
+ sage: sorted(O, key=str)
2348
+ [[{'a','b'}, {'a'}],
2349
+ [{'a'}, {'a','b'}],
2350
+ [{'a'}, {'a'}, {'b'}],
2351
+ [{'a'}, {'b'}, {'a'}],
2352
+ [{'b'}, {'a'}, {'a'}]]
2353
+
2354
+ sage: O = OrderedMultisetPartitionsIntoSets([1, 1, 2])
2355
+ sage: list(O)
2356
+ [[{1}, {1}, {2}], [{1}, {1,2}], [{1}, {2}, {1}],
2357
+ [{1,2}, {1}], [{2}, {1}, {1}]]
2358
+ """
2359
+ for co in _iterator_weight(weight=dict(self._X)):
2360
+ yield self.element_class(self, co)
2361
+
2362
+
2363
+ class OrderedMultisetPartitionsIntoSets_X_constraints(OrderedMultisetPartitionsIntoSets):
2364
+ """
2365
+ Class of ordered multiset partitions into sets of a fixed multiset `X`
2366
+ satisfying constraints.
2367
+ """
2368
+
2369
+ def __init__(self, X, **constraints):
2370
+ """
2371
+ Mimic class ``OrderedMultisetPartitionsIntoSets_X`` to initialize.
2372
+
2373
+ TESTS::
2374
+
2375
+ sage: C = OrderedMultisetPartitionsIntoSets([1,1,2,4], length=3)
2376
+ sage: TestSuite(C).run()
2377
+
2378
+ sage: C = OrderedMultisetPartitionsIntoSets([1,1,2,4], max_length=3)
2379
+ sage: TestSuite(C).run()
2380
+ """
2381
+ self._X = X
2382
+ self._Xtup = tuple(k for (k,v) in sorted(X) for _ in range(v))
2383
+ OrderedMultisetPartitionsIntoSets.__init__(self, True, weight=X, **constraints)
2384
+
2385
+ def _repr_(self):
2386
+ """
2387
+ Return a string representation of ``self``.
2388
+
2389
+ EXAMPLES::
2390
+
2391
+ sage: O = OrderedMultisetPartitionsIntoSets([2,2,2,3,4,4,5], min_length=4, max_length=5)
2392
+ sage: O
2393
+ Ordered Multiset Partitions into Sets of multiset {{2, 2, 2, 3, 4, 4, 5}}
2394
+ with constraints: max_length=5, min_length=4
2395
+ """
2396
+ cdict = dict(self.constraints)
2397
+ cdict.pop("weight", None)
2398
+ ms_rep = "{{" + ", ".join(map(str, self._Xtup)) + "}}"
2399
+ base_repr = "Ordered Multiset Partitions into Sets" + " of multiset %s" % ms_rep
2400
+ return base_repr + self._constraint_repr_(cdict)
2401
+
2402
+ ###############
2403
+
2404
+
2405
+ class OrderedMultisetPartitionsIntoSets_alph_d(OrderedMultisetPartitionsIntoSets):
2406
+ """
2407
+ Class of ordered multiset partitions into sets of specified order `d`
2408
+ over a fixed alphabet `A`.
2409
+ """
2410
+
2411
+ def __init__(self, A, d):
2412
+ """
2413
+ Initialize ``self``.
2414
+
2415
+ TESTS::
2416
+
2417
+ sage: C = OrderedMultisetPartitionsIntoSets(3, 2)
2418
+ sage: TestSuite(C).run()
2419
+
2420
+ sage: C2 = OrderedMultisetPartitionsIntoSets([1,2,3], 2)
2421
+ sage: C is C2
2422
+ True
2423
+
2424
+ sage: list(OrderedMultisetPartitionsIntoSets([1,2,3], 2))
2425
+ [[{1,2}], [{1,3}], [{2,3}], [{1}, {1}], [{1}, {2}], [{1}, {3}], [{2}, {1}],
2426
+ [{2}, {2}], [{2}, {3}], [{3}, {1}], [{3}, {2}], [{3}, {3}]]
2427
+ """
2428
+ self._alphabet = A
2429
+ self._order = d
2430
+ OrderedMultisetPartitionsIntoSets.__init__(self, True)
2431
+
2432
+ def _repr_(self):
2433
+ """
2434
+ Return a string representation of ``self``.
2435
+
2436
+ TESTS::
2437
+
2438
+ sage: repr(OrderedMultisetPartitionsIntoSets(3, 2))
2439
+ 'Ordered Multiset Partitions into Sets of order 2 over alphabet {1, 2, 3}'
2440
+ sage: repr(OrderedMultisetPartitionsIntoSets([1,3], 2))
2441
+ 'Ordered Multiset Partitions into Sets of order 2 over alphabet {1, 3}'
2442
+ """
2443
+ A_rep = "Ordered Multiset Partitions into Sets of order " + str(self._order)
2444
+ A_rep += " over alphabet {%s}" % (", ".join(map(str, sorted(self._alphabet))))
2445
+ return A_rep
2446
+
2447
+ def _an_element_(self):
2448
+ """
2449
+ Return a typical element of ``OrderedMultisetPartitionIntoSets_alph_d``.
2450
+
2451
+ EXAMPLES::
2452
+
2453
+ sage: OrderedMultisetPartitionsIntoSets([2,3,4,5], 3).an_element()
2454
+ [{2,4,5}]
2455
+ """
2456
+ alpha = Compositions(self._order, max_part=len(self._alphabet)).an_element()
2457
+ co = [Subsets_sk(self._alphabet, a).an_element() for a in alpha]
2458
+ return self.element_class(self, map(frozenset, co))
2459
+
2460
+ def random_element(self):
2461
+ r"""
2462
+ Return a random element of ``self``.
2463
+
2464
+ This method does not return elements of ``self`` with uniform probability,
2465
+ but it does cover all elements. The scheme is as follows:
2466
+
2467
+ - produce a random composition `C`;
2468
+ - choose random subsets of ``self._alphabet`` of size `c` for each `c` in `C`.
2469
+
2470
+ EXAMPLES::
2471
+
2472
+ sage: OrderedMultisetPartitionsIntoSets([1,4], 3).random_element() # random
2473
+ [{4}, {1,4}]
2474
+ sage: OrderedMultisetPartitionsIntoSets([1,3], 4).random_element() # random
2475
+ [{1,3}, {1}, {3}]
2476
+
2477
+ sage: OMP = OrderedMultisetPartitionsIntoSets([2,3,4], 2)
2478
+ sage: d = {}
2479
+ sage: for _ in range(1200):
2480
+ ....: x = OMP.random_element()
2481
+ ....: d[x] = d.get(x, 0) + 1
2482
+ sage: d.values() # random
2483
+ [192, 68, 73, 61, 69, 60, 77, 204, 210, 66, 53, 67]
2484
+ """
2485
+ if not self._alphabet:
2486
+ return self.element_class(self, [])
2487
+
2488
+ alpha = Compositions(self._order, max_part=len(self._alphabet)).random_element()
2489
+ co = [Subsets_sk(self._alphabet, a).random_element() for a in alpha]
2490
+ return self.element_class(self, map(frozenset, co))
2491
+
2492
+ def __iter__(self):
2493
+ """
2494
+ Iterate over ``self``.
2495
+
2496
+ TESTS::
2497
+
2498
+ sage: O = OrderedMultisetPartitionsIntoSets(['a', 'b'], 3)
2499
+ sage: it = O.__iter__()
2500
+ sage: sorted([next(it) for _ in range(O.cardinality())], key=str)
2501
+ [[{'a','b'}, {'a'}], [{'a','b'}, {'b'}], [{'a'}, {'a','b'}],
2502
+ [{'a'}, {'a'}, {'a'}], [{'a'}, {'a'}, {'b'}], [{'a'}, {'b'}, {'a'}],
2503
+ [{'a'}, {'b'}, {'b'}], [{'b'}, {'a','b'}], [{'b'}, {'a'}, {'a'}],
2504
+ [{'b'}, {'a'}, {'b'}], [{'b'}, {'b'}, {'a'}], [{'b'}, {'b'}, {'b'}]]
2505
+ """
2506
+ for co in _iterator_order(self._alphabet, self._order):
2507
+ yield self.element_class(self, co)
2508
+
2509
+ def cardinality(self):
2510
+ """
2511
+ Return the number of ordered partitions of order ``self._order`` on
2512
+ alphabet ``self._alphabet``.
2513
+
2514
+ TESTS::
2515
+
2516
+ sage: len(OrderedMultisetPartitionsIntoSets([1, 'a'], 3).list())
2517
+ 12
2518
+ sage: OrderedMultisetPartitionsIntoSets([1, 'a'], 3).cardinality()
2519
+ 12
2520
+ """
2521
+ if self._order == 0:
2522
+ return ZZ(0)
2523
+
2524
+ # iteration scheme:
2525
+ # - start from an integer composition ``alpha`` of ``self._order``.
2526
+ # - for each ``a`` in ``alpha``, pick ``a`` letters from ``alphabet``
2527
+ min_length = self._order // len(self._alphabet)
2528
+ max_length = self._order
2529
+
2530
+ deg = 0
2531
+ for k in range(min_length, max_length+1):
2532
+ for alpha in IntegerListsLex(self._order, length=k, min_part=1, max_part=len(self._alphabet)):
2533
+ deg += prod(binomial(len(self._alphabet), a) for a in alpha)
2534
+ return ZZ(deg)
2535
+
2536
+
2537
+ class OrderedMultisetPartitionsIntoSets_alph_d_constraints(OrderedMultisetPartitionsIntoSets):
2538
+ """
2539
+ Class of ordered multiset partitions into sets of specified order `d`
2540
+ over a fixed alphabet `A` satisfying constraints.
2541
+ """
2542
+
2543
+ def __init__(self, A, d, **constraints):
2544
+ """
2545
+ Mimic class ``OrderedMultisetPartitionsIntoSets_alph_d`` to initialize.
2546
+
2547
+ EXAMPLES::
2548
+
2549
+ sage: list(OrderedMultisetPartitionsIntoSets(3, 2, length=3))
2550
+ []
2551
+ sage: list(OrderedMultisetPartitionsIntoSets([1,2,4], 2, length=1))
2552
+ [[{1,2}], [{1,4}], [{2,4}]]
2553
+
2554
+ TESTS::
2555
+
2556
+ sage: C = OrderedMultisetPartitionsIntoSets(3, 2, length=3)
2557
+ sage: TestSuite(C).run()
2558
+
2559
+ sage: C = OrderedMultisetPartitionsIntoSets([1,2,4], 4, min_length=3)
2560
+ sage: TestSuite(C).run()
2561
+ """
2562
+ self._alphabet = A
2563
+ self._order = d
2564
+ OrderedMultisetPartitionsIntoSets.__init__(self, True, alphabet=A,
2565
+ order=d, **constraints)
2566
+
2567
+ def _repr_(self):
2568
+ """
2569
+ Return a string representation of ``self``.
2570
+
2571
+ EXAMPLES::
2572
+
2573
+ sage: O = OrderedMultisetPartitionsIntoSets([2,3,4,5], 4, length=3)
2574
+ sage: O
2575
+ Ordered Multiset Partitions into Sets of order 4 over alphabet {2, 3, 4, 5}
2576
+ with constraint: length=3
2577
+ sage: O = OrderedMultisetPartitionsIntoSets([2,3,4,5], 4, max_length=4, size=10)
2578
+ sage: O
2579
+ Ordered Multiset Partitions into Sets of order 4 over alphabet {2, 3, 4, 5}
2580
+ with constraints: max_length=4, size=10
2581
+ """
2582
+ cdict = dict(self.constraints)
2583
+ cdict.pop("alphabet", None)
2584
+ cdict.pop("order", None)
2585
+ base_repr = "Ordered Multiset Partitions into Sets of order " + str(self._order)
2586
+ base_repr += " over alphabet {%s}" % (", ".join(map(str, sorted(self._alphabet))))
2587
+ return base_repr + self._constraint_repr_(cdict)
2588
+
2589
+ ###############
2590
+
2591
+
2592
+ def _get_multiset(co):
2593
+ """
2594
+ Construct the multiset (as a sorted tuple) suggested by the lists
2595
+ of lists ``co``.
2596
+
2597
+ EXAMPLES::
2598
+
2599
+ sage: from sage.combinat.multiset_partition_into_sets_ordered import _get_multiset
2600
+ sage: L = ((1,), (1, 6), (6, 7), (1,), (1, 3))
2601
+ sage: _get_multiset(L)
2602
+ (1, 1, 1, 1, 3, 6, 6, 7)
2603
+ """
2604
+ return tuple(sorted(_concatenate(co), key=str))
2605
+
2606
+
2607
+ def _get_weight(lst):
2608
+ """
2609
+ Construct the multiset (as a dictionary) suggested by the
2610
+ multiset-as-list ``lst``.
2611
+
2612
+ EXAMPLES::
2613
+
2614
+ sage: from sage.combinat.multiset_partition_into_sets_ordered import _get_weight
2615
+ sage: L = (1, 1, 1, 3, 1, 6, 6, 7)
2616
+ sage: _get_weight(L)
2617
+ {1: 4, 3: 1, 6: 2, 7: 1}
2618
+ """
2619
+ out = {}
2620
+ for k in lst:
2621
+ out[k] = out.get(k,0) + 1
2622
+ return out
2623
+
2624
+
2625
+ def _has_nonempty_sets(x):
2626
+ """
2627
+ Blocks should be nonempty sets/lists/tuples of distinct elements.
2628
+
2629
+ TESTS::
2630
+
2631
+ sage: from sage.combinat.multiset_partition_into_sets_ordered import _has_nonempty_sets
2632
+ sage: _has_nonempty_sets([[2,4], {1}, (1,4)])
2633
+ True
2634
+ sage: _has_nonempty_sets([[2,4], {}, (1,4)])
2635
+ False
2636
+ sage: _has_nonempty_sets([(2,4), (1,1), (1,4)])
2637
+ False
2638
+ """
2639
+ return all((isinstance(block, (list, tuple, set, frozenset, Set_object))
2640
+ and block and len(set(block)) == len(block))
2641
+ for block in x)
2642
+
2643
+
2644
+ def _union_of_sets(list_of_sets):
2645
+ """
2646
+ Return the union of a list of iterables as a frozenset.
2647
+
2648
+ EXAMPLES::
2649
+
2650
+ sage: from sage.combinat.multiset_partition_into_sets_ordered import _union_of_sets
2651
+ sage: L = ([1,2,3], Set([1,5,6]), [], range(5,8))
2652
+ sage: _union_of_sets(L)
2653
+ frozenset({1, 2, 3, 5, 6, 7})
2654
+ """
2655
+ return reduce(lambda a, b: frozenset(a) | frozenset(b),
2656
+ list_of_sets, frozenset())
2657
+
2658
+
2659
+ def _concatenate(list_of_iters):
2660
+ """
2661
+ Return the concatenation of a list of iterables as a tuple.
2662
+
2663
+ EXAMPLES::
2664
+
2665
+ sage: from sage.combinat.multiset_partition_into_sets_ordered import _concatenate
2666
+ sage: L = ([1,2,3], Set([4,5,6]), [], range(7,11))
2667
+ sage: _concatenate(L)
2668
+ (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
2669
+ """
2670
+ return tuple([val for block in list_of_iters for val in block])
2671
+
2672
+
2673
+ def _is_finite(constraints):
2674
+ """
2675
+ Return ``True`` if the dictionary ``constraints`` corresponds to
2676
+ a finite collection of ordered multiset partitions into sets.
2677
+
2678
+ If either ``weight`` or ``size`` is among the constraints, then
2679
+ the constraints represent a finite collection of ordered multiset
2680
+ partitions into sets. If both are absent, one needs ``alphabet`` to be
2681
+ present (plus a bound on length or order) in order to have a
2682
+ finite collection of ordered multiset partitions into sets.
2683
+
2684
+ EXAMPLES::
2685
+
2686
+ sage: from sage.combinat.multiset_partition_into_sets_ordered import _is_finite
2687
+ sage: W = {"weight": {1:3, 2:3, 4:1}, "length": 5}
2688
+ sage: S = {"size": 44, "min_length": 5}
2689
+ sage: AO = {"alphabet": range(44), "max_order": 5}
2690
+ sage: all(_is_finite(constr) for constr in (W, S, AO))
2691
+ True
2692
+ sage: AL = {"alphabet": range(44), "min_order": 5}
2693
+ sage: _is_finite(AL)
2694
+ False
2695
+ """
2696
+ if "weight" in constraints or "size" in constraints:
2697
+ return True
2698
+ elif "alphabet" in constraints:
2699
+ # Assume the alphabet is finite
2700
+ Bounds = set(["length", "max_length", "order", "max_order"])
2701
+ return Bounds.intersection(set(constraints)) != set()
2702
+
2703
+
2704
+ def _base_iterator(constraints):
2705
+ """
2706
+ Return a base iterator for ordered multiset partitions into sets or ``None``.
2707
+
2708
+ If the keys within ``constraints`` dictionary correspond to a finite set
2709
+ of ordered multiset partitions into sets, return an iterator. Else,
2710
+ return ``None``.
2711
+
2712
+ OUTPUT:
2713
+
2714
+ Tuples of ``frozenset`` objects representing ordered multiset partitions
2715
+ into sets.
2716
+
2717
+ EXAMPLES:
2718
+
2719
+ If key ``weight`` is present, ignore all other constraints
2720
+ (passes to ``_iterator_weight``)::
2721
+
2722
+ sage: from sage.combinat.multiset_partition_into_sets_ordered import _base_iterator
2723
+ sage: OMP = OrderedMultisetPartitionIntoSets
2724
+ sage: constraints = {"weight": {1:3, 2:3, 4:1}, "length": 5}
2725
+ sage: it = _base_iterator(constraints)
2726
+ sage: sorted(OMP(next(it)) for _ in range(4)) # note the partitions of length 6 and 7
2727
+ [[{1}, {1}, {1}, {2}, {2}, {2}, {4}],
2728
+ [{1}, {1}, {1}, {2}, {2}, {2,4}],
2729
+ [{1}, {1}, {1,2}, {2}, {2}, {4}],
2730
+ [{1}, {1}, {1,2}, {2}, {2,4}]]
2731
+
2732
+ If key ``size`` is present, pass to ``_iterator_size``, which then
2733
+ takes into account whether or not keys ``length`` and ``alphabet``
2734
+ are among the constraints::
2735
+
2736
+ sage: constraints = {"size": 5}
2737
+ sage: it = _base_iterator(constraints)
2738
+ sage: [OMP(next(it)) for _ in range(8)]
2739
+ [[{5}], [{2,3}], [{1,4}], [{4}, {1}], [{1,3}, {1}],
2740
+ [{3}, {2}], [{1,2}, {2}], [{3}, {1}, {1}]]
2741
+
2742
+ sage: constraintsL = {"size": 6, "length":2}
2743
+ sage: it = _base_iterator(constraintsL)
2744
+ sage: [OMP(next(it)) for _ in range(8)]
2745
+ [[{5}, {1}], [{2,3}, {1}], [{1,4}, {1}], [{4}, {2}],
2746
+ [{1,3}, {2}], [{3}, {3}], [{3}, {1,2}], [{1,2}, {3}]]
2747
+
2748
+ sage: constraintsA = {"size": 6, "alphabet":frozenset([2, 3])}
2749
+ sage: it = _base_iterator(constraintsA)
2750
+ sage: list(it)
2751
+ [(frozenset({3}), frozenset({3})),
2752
+ (frozenset({2}), frozenset({2}), frozenset({2}))]
2753
+
2754
+ If key ``alphabet`` is present, the slice may still be infinite, in
2755
+ which case ``None`` is returned. Else, use to ``_iterator_order``::
2756
+
2757
+ sage: constraints = {"alphabet": frozenset([3, 4]), "min_length":2}
2758
+ sage: _base_iterator(constraints) is None
2759
+ True
2760
+ sage: constraints = {"alphabet": frozenset([3, 4]), "max_length":2}
2761
+ sage: it = _base_iterator(constraints)
2762
+ sage: list(map(OMP, it))
2763
+ [[], [{3}], [{4}], [{3,4}], [{3}, {3}], [{3}, {4}],
2764
+ [{4}, {3}], [{4}, {4}], [{3,4}, {3}], [{3,4}, {4}],
2765
+ [{3}, {3,4}], [{4}, {3,4}], [{3,4}, {3,4}]]
2766
+ """
2767
+ if "weight" in constraints:
2768
+ return _iterator_weight(constraints["weight"])
2769
+ elif "size" in constraints:
2770
+ return _iterator_size(constraints["size"],
2771
+ constraints.get("length",None), constraints.get("alphabet",None))
2772
+ elif "alphabet" in constraints:
2773
+ A = constraints["alphabet"]
2774
+ # assumes `alphabet` is finite
2775
+ min_k = constraints.get("min_length", 0)
2776
+ max_k = constraints.get("max_length", infinity)
2777
+ min_ord = constraints.get("min_order", 0)
2778
+ max_ord = constraints.get("max_order", max_k * len(A))
2779
+ max_k = min(max_k, max_ord)
2780
+ if "length" in constraints:
2781
+ min_k = max_k = constraints["length"]
2782
+ min_ord = max(min_ord, min_k)
2783
+ max_ord = min(max_ord, len(A) * max_k)
2784
+ if "order" in constraints:
2785
+ min_ord = max_ord = constraints["order"]
2786
+ max_k = min(max_k, max_ord)
2787
+ if min_ord:
2788
+ min_k = max(1, min_k, min_ord // len(A))
2789
+ if infinity not in (max_k, max_ord):
2790
+ return chain(*(_iterator_order(A, ord, range(min_k, max_k + 1))
2791
+ for ord in range(min_ord, max_ord + 1)))
2792
+ # else
2793
+ return None
2794
+
2795
+
2796
+ def _iterator_weight(weight):
2797
+ """
2798
+ An iterator for the ordered multiset partitions into sets with weight given by
2799
+ the dictionary (or weak composition) ``weight``.
2800
+
2801
+ The dictionary ``weight`` may contain values equal to `0`;
2802
+ the corresponding keys are ignored.
2803
+
2804
+ OUTPUT:
2805
+
2806
+ Tuples of ``frozenset`` objects representing ordered multiset partitions
2807
+ into sets.
2808
+
2809
+ EXAMPLES::
2810
+
2811
+ sage: from sage.combinat.multiset_partition_into_sets_ordered import _iterator_weight
2812
+ sage: weight = {1:2, 'b':1}
2813
+ sage: OMP = OrderedMultisetPartitionsIntoSets(weight)
2814
+ sage: l = list(_iterator_weight(weight))
2815
+
2816
+ sage: sorted(map(OMP, l), key=str) == sorted(map(OMP,
2817
+ ....: [[{1}, {1}, {'b'}], [{1}, {1,'b'}], [{1}, {'b'}, {1}],
2818
+ ....: [{1,'b'}, {1}], [{'b'}, {1}, {1}]]), key=str)
2819
+ True
2820
+ sage: OMP = OrderedMultisetPartitionsIntoSets({1:3, 3:1})
2821
+ sage: list(map(OMP, _iterator_weight([3,0,1])))
2822
+ [[{1}, {1}, {1}, {3}], [{1}, {1}, {1,3}], [{1}, {1}, {3}, {1}],
2823
+ [{1}, {1,3}, {1}], [{1}, {3}, {1}, {1}],
2824
+ [{1,3}, {1}, {1}], [{3}, {1}, {1}, {1}]]
2825
+
2826
+ TESTS::
2827
+
2828
+ sage: list(_iterator_weight([2,0,1]))
2829
+ [(frozenset({1}), frozenset({1}), frozenset({3})),
2830
+ (frozenset({1}), frozenset({1, 3})),
2831
+ (frozenset({1}), frozenset({3}), frozenset({1})),
2832
+ (frozenset({1, 3}), frozenset({1})),
2833
+ (frozenset({3}), frozenset({1}), frozenset({1}))]
2834
+ sage: list(_iterator_weight([]))
2835
+ [()]
2836
+ """
2837
+ # "weight" should be a dict mapping keys to weights
2838
+ if isinstance(weight, (list, tuple)):
2839
+ weight = {k+1: val for k, val in enumerate(weight) if val}
2840
+
2841
+ # We first map the arbitrary keys to integers to combat unreliable
2842
+ # sorting behavior.
2843
+ keys = tuple(set(weight))
2844
+ multiset = []
2845
+ for i, key in enumerate(keys):
2846
+ multiset += [i] * weight[key]
2847
+
2848
+ # We build ordered multiset partitions into sets of `X` by
2849
+ # permutation + deconcatenation
2850
+ for alpha in Permutations_mset(multiset):
2851
+ co = _break_at_descents(alpha, weak=True)
2852
+ for A in OrderedMultisetPartitionIntoSets(co).finer(strong=True):
2853
+ B = tuple([frozenset([keys[i] for i in block]) for block in A])
2854
+ yield B
2855
+
2856
+
2857
+ def _iterator_size(size, length=None, alphabet=None):
2858
+ r"""
2859
+ An iterator for the ordered multiset partitions into sets of integer `n`.
2860
+
2861
+ The degree `n` part of ordered multiset partitions into sets contains all
2862
+ sequences of subsets of `\NN_+` whose total sum adds up to `n`.
2863
+
2864
+ If optional argument ``alphabet`` is given, it should be a ``Set`` object.
2865
+ Then only yield those `c` with all letters taken from ``alphabet``.
2866
+
2867
+ OUTPUT:
2868
+
2869
+ Tuples of ``frozenset`` objects representing ordered multiset partitions
2870
+ into sets.
2871
+
2872
+ TESTS::
2873
+
2874
+ sage: from sage.combinat.multiset_partition_into_sets_ordered import _iterator_size
2875
+ sage: OMP = OrderedMultisetPartitionsIntoSets(3)
2876
+ sage: list(map(OMP, _iterator_size(3)))
2877
+ [[{3}], [{1,2}], [{2}, {1}], [{1}, {2}], [{1}, {1}, {1}]]
2878
+
2879
+ sage: OMP = OrderedMultisetPartitionsIntoSets(5, alphabet=(1,3))
2880
+ sage: list(map(OMP, _iterator_size(5, alphabet={1,3})))
2881
+ [[{1,3}, {1}], [{3}, {1}, {1}], [{1}, {1,3}], [{1}, {3}, {1}],
2882
+ [{1}, {1}, {3}], [{1}, {1}, {1}, {1}, {1}]]
2883
+
2884
+ sage: list(_iterator_size(2))
2885
+ [(frozenset({2}),), (frozenset({1}), frozenset({1}))]
2886
+ """
2887
+ # iteration scheme:
2888
+ # - start from an integer composition ``alpha``.
2889
+ # - for each ``a`` in ``alpha``, pick distinct integers that sum to ``a``
2890
+ if alphabet:
2891
+ min_p = min(alphabet)
2892
+ max_p = max(alphabet)
2893
+ for alpha in IntegerListsLex(size, length=length, min_part=1,
2894
+ max_part=min(size, sum(alphabet))):
2895
+ for p in product(*[IntegerListsLex(a, min_slope=1,
2896
+ min_part=min_p,
2897
+ max_part=min(a, max_p))
2898
+ for a in alpha]):
2899
+ if frozenset(_concatenate(p)).issubset(frozenset(alphabet)):
2900
+ yield tuple(frozenset(k) for k in p)
2901
+ else:
2902
+ for alpha in IntegerListsLex(size, length=length, min_part=1, max_part=size):
2903
+ for p in product(*[IntegerListsLex(a, min_slope=1,
2904
+ min_part=1) for a in alpha]):
2905
+ yield tuple(frozenset(k) for k in p)
2906
+
2907
+
2908
+ def _iterator_order(A, d, lengths=None):
2909
+ """
2910
+ An iterator for the ordered multiset partitions into sets of order `d`
2911
+ over alphabet `A`.
2912
+
2913
+ If optional argument ``lengths`` is given, it should be a list of integers.
2914
+ Then only yield those ordered multiset partitions into sets with length
2915
+ in ``lengths``.
2916
+
2917
+ OUTPUT:
2918
+
2919
+ Tuples of ``frozenset`` objects representing ordered multiset partitions
2920
+ into sets.
2921
+
2922
+ TESTS::
2923
+
2924
+ sage: from sage.combinat.multiset_partition_into_sets_ordered import _iterator_order
2925
+ sage: OMP = OrderedMultisetPartitionsIntoSets([1,4], 3)
2926
+ sage: list(map(OMP, _iterator_order({1,4}, 3)))
2927
+ [[{1,4}, {1}], [{1,4}, {4}], [{1}, {1,4}], [{4}, {1,4}], [{1}, {1}, {1}],
2928
+ [{1}, {1}, {4}], [{1}, {4}, {1}], [{1}, {4}, {4}], [{4}, {1}, {1}],
2929
+ [{4}, {1}, {4}], [{4}, {4}, {1}], [{4}, {4}, {4}]]
2930
+ sage: list(map(OMP, _iterator_order([1,4], 3, [3])))
2931
+ [[{1}, {1}, {1}], [{1}, {1}, {4}], [{1}, {4}, {1}], [{1}, {4}, {4}],
2932
+ [{4}, {1}, {1}], [{4}, {1}, {4}], [{4}, {4}, {1}], [{4}, {4}, {4}]]
2933
+
2934
+ sage: OMP = OrderedMultisetPartitionsIntoSets([1,2,4], 3)
2935
+ sage: list(map(OMP, _iterator_order([1,2,4], 3, [1,2])))[:10]
2936
+ [[{1,2,4}], [{1,2}, {1}], [{1,2}, {2}], [{1,2}, {4}], [{1,4}, {1}],
2937
+ [{1,4}, {2}], [{1,4}, {4}], [{2,4}, {1}], [{2,4}, {2}], [{2,4}, {4}]]
2938
+
2939
+ sage: list(_iterator_order([1,4], 3, [1]))
2940
+ []
2941
+ sage: list(_iterator_order([1,4], 3, [2]))
2942
+ [(frozenset({1, 4}), frozenset({1})), (frozenset({1, 4}), frozenset({4})),
2943
+ (frozenset({1}), frozenset({1, 4})), (frozenset({4}), frozenset({1, 4}))]
2944
+ sage: list(_iterator_order([1,4], 3, [4]))
2945
+ []
2946
+ sage: list(_iterator_order([1,4], 0, [3]))
2947
+ []
2948
+ sage: list(_iterator_order([1,4], 0, [0,3]))
2949
+ [()]
2950
+ sage: list(_iterator_order([1,4], 0))
2951
+ [()]
2952
+ """
2953
+ A = frozenset(A)
2954
+
2955
+ # iteration scheme:
2956
+ # start from an integer composition ``alpha`` of ``d``.
2957
+ # for each ``a`` in ``alpha``, pick ``a`` letters from ``A``
2958
+ n = len(A)
2959
+ if not lengths:
2960
+ if d:
2961
+ lengths = range(max(1, d // n), d+1)
2962
+ else:
2963
+ lengths = (0,)
2964
+
2965
+ for k in lengths:
2966
+ if not k and not d:
2967
+ yield ()
2968
+ else:
2969
+ for alpha in IntegerListsLex(d, length=k, min_part=1, max_part=n):
2970
+ for co in product(*[Subsets_sk(A, a) for a in alpha]):
2971
+ yield tuple(frozenset(X) for X in co)
2972
+
2973
+
2974
+ def _descents(w) -> list:
2975
+ r"""
2976
+ Return descent positions in the word ``w``.
2977
+
2978
+ EXAMPLES::
2979
+
2980
+ sage: from sage.combinat.multiset_partition_into_sets_ordered import _descents
2981
+ sage: _descents([1, 2, 3, 2, 2, 1, 4, 3]) == [2, 4, 6]
2982
+ True
2983
+ sage: _descents([])
2984
+ []
2985
+ """
2986
+ return [j for j in range(len(w) - 1) if w[j] > w[j + 1]]
2987
+
2988
+
2989
+ def _break_at_descents(alpha, weak=True):
2990
+ r"""
2991
+ Return the deconcatenation of the composition ``alpha`` at its
2992
+ set of descents.
2993
+
2994
+ OUTPUT:
2995
+
2996
+ A list `[a_1, \ldots, a_r]` of nonempty lists whose concatenation
2997
+ is ``list(alpha)`` with the property that ``alpha[i] >= alpha[i+1]``
2998
+ if and only if positions `i` and `i+1` correspond to different
2999
+ lists. (Specifically, ``alpha[i]`` is the last letter of some
3000
+ `a_j` and ``alpha[i+1]`` is the first letter of `a_{j+1}`.)
3001
+
3002
+ If the optional argument ``weak`` is ``False``, then only make
3003
+ breaks when ``alpha[i] > alpha[i+1]``.
3004
+
3005
+ EXAMPLES::
3006
+
3007
+ sage: from sage.combinat.multiset_partition_into_sets_ordered import _break_at_descents
3008
+ sage: _break_at_descents([1, 2, 3, 2, 2, 1, 4, 3])
3009
+ [[1, 2, 3], [2], [2], [1, 4], [3]]
3010
+ sage: _break_at_descents([1, 2, 3, 2, 2, 1, 4, 3], weak=False)
3011
+ [[1, 2, 3], [2, 2], [1, 4], [3]]
3012
+ sage: _break_at_descents([])
3013
+ []
3014
+ """
3015
+ if not alpha:
3016
+ return []
3017
+
3018
+ Blocks = []
3019
+ block = [alpha[0]]
3020
+ for i in range(1,len(alpha)):
3021
+ if (alpha[i-1] > alpha[i]) or (alpha[i-1] == alpha[i] and weak):
3022
+ Blocks.append(block)
3023
+ block = [alpha[i]]
3024
+ else:
3025
+ block.append(alpha[i])
3026
+ if block:
3027
+ Blocks.append(block)
3028
+ return Blocks
3029
+
3030
+
3031
+ def _refine_block(S, strong=False):
3032
+ r"""
3033
+ Return the list of all possible refinements of a set `S`.
3034
+
3035
+ A refinement of `S` is a tuple of nonempty subsets whose union is `S`.
3036
+
3037
+ If optional argument ``strong`` is set to ``True``, then only those
3038
+ refinements that are deconcatenations of the list ``sorted(S)`` are returned.
3039
+
3040
+ (The subsets involved are stored as ``frozenset`` objects.)
3041
+
3042
+ EXAMPLES::
3043
+
3044
+ sage: from sage.combinat.multiset_partition_into_sets_ordered import _refine_block
3045
+ sage: _refine_block([1, 2], strong=True)
3046
+ [(frozenset({1}), frozenset({2})), (frozenset({1, 2}),)]
3047
+
3048
+ sage: [tuple(Set(x) for x in tupl) for tupl in _refine_block([1, 2, 3], strong=True)]
3049
+ [({1}, {2}, {3}), ({1}, {2, 3}), ({1, 2}, {3}), ({1, 2, 3},)]
3050
+ sage: [tuple(Set(x) for x in tupl) for tupl in _refine_block([1, 2, 3])]
3051
+ [({3}, {2}, {1}), ({2}, {3}, {1}), ({3}, {1}, {2}),
3052
+ ({3}, {1, 2}), ({2}, {1}, {3}), ({2}, {1, 3}),
3053
+ ({2, 3}, {1}), ({1}, {3}, {2}), ({1}, {2}, {3}),
3054
+ ({1}, {2, 3}), ({1, 3}, {2}), ({1, 2}, {3}),
3055
+ ({1, 2, 3},)]
3056
+
3057
+ TESTS::
3058
+
3059
+ sage: len(_refine_block([1, 2, 3, 4])) == 1 + binomial(4,1)*2 + binomial(4,2) + binomial(4,2)*factorial(3) + factorial(4)
3060
+ True
3061
+ sage: len(_refine_block([1, 2, 3, 4], strong=True)) == 2**3
3062
+ True
3063
+ sage: _refine_block([])
3064
+ Traceback (most recent call last):
3065
+ ...
3066
+ ValueError: S (=[]) must be nonempty
3067
+ """
3068
+ if not S:
3069
+ raise ValueError("S (=%s) must be nonempty" % S)
3070
+
3071
+ if all(s in ZZ for s in S):
3072
+ X = sorted(S)
3073
+ else:
3074
+ X = sorted(S, key=str)
3075
+ n = len(X)
3076
+ out = []
3077
+ if not strong:
3078
+ WordSet = IntegerListsLex(min_part=0, max_part=n-1, length=n)
3079
+ else:
3080
+ WordSet = IntegerListsLex(min_part=0, max_part=n-1, length=n, min_slope=0)
3081
+
3082
+ for w in WordSet:
3083
+ if _is_initial_segment(sorted(set(w))):
3084
+ a = [frozenset() for _ in range(max(w)+1)]
3085
+ for pos in range(n):
3086
+ a[w[pos]] = a[w[pos]].union({X[pos]})
3087
+ out.append(tuple(a))
3088
+ return out
3089
+
3090
+
3091
+ def _is_initial_segment(lst):
3092
+ r"""
3093
+ Return ``True`` if ``lst`` is an interval in `\ZZ` of the form `[0, 1, \ldots, n]`.
3094
+
3095
+ EXAMPLES::
3096
+
3097
+ sage: from sage.combinat.multiset_partition_into_sets_ordered import _is_initial_segment
3098
+ sage: _is_initial_segment([1, 2, 3])
3099
+ False
3100
+ sage: _is_initial_segment([0, 1, 2, 3])
3101
+ True
3102
+ sage: _is_initial_segment([0])
3103
+ True
3104
+ """
3105
+ return list(range(max(lst)+1)) == lst
3106
+
3107
+
3108
+ def _split_block(S, k=2):
3109
+ """
3110
+ Return the list of all possible splittings of a set `S` into `k` parts.
3111
+
3112
+ A splitting of `S` is a tuple of (possibly empty) subsets whose union is `S`.
3113
+
3114
+ (The subsets involved are stored as ``frozenset`` objects.)
3115
+
3116
+ EXAMPLES::
3117
+
3118
+ sage: from sage.combinat.multiset_partition_into_sets_ordered import _split_block
3119
+ sage: S = [1, 2, 3]
3120
+ sage: _split_block(S, 1)
3121
+ [(frozenset({1, 2, 3}),)]
3122
+
3123
+ sage: [tuple(Set(x) for x in tupl) for tupl in _split_block(S, 2)]
3124
+ [({}, {1, 2, 3}), ({3}, {1, 2}), ({2}, {1, 3}), ({2, 3}, {1}),
3125
+ ({1}, {2, 3}), ({1, 3}, {2}), ({1, 2}, {3}), ({1, 2, 3}, {})]
3126
+ sage: [tuple(Set(x) for x in tupl) for tupl in _split_block({2, 4}, 3)]
3127
+ [({}, {}, {2, 4}), ({}, {4}, {2}), ({4}, {}, {2}),
3128
+ ({}, {2}, {4}), ({}, {2, 4}, {}), ({4}, {2}, {}),
3129
+ ({2}, {}, {4}), ({2}, {4}, {}), ({2, 4}, {}, {})]
3130
+ """
3131
+ if all(s in ZZ for s in S):
3132
+ X = sorted(S)
3133
+ else:
3134
+ X = sorted(S, key=str)
3135
+ n = len(X)
3136
+ out = []
3137
+ for w in IntegerListsLex(min_part=0, max_part=k-1, length=n):
3138
+ a = [frozenset() for _ in range(k)]
3139
+ for pos in range(n):
3140
+ a[w[pos]] = a[w[pos]].union({X[pos]})
3141
+ out.append(tuple(a))
3142
+ return out
3143
+
3144
+
3145
+ def _to_minimaj_blocks(T):
3146
+ r"""
3147
+ Return a tuple of tuples, representing an ordered multiset partition into sets
3148
+ in the minimaj ordering on blocks
3149
+
3150
+ INPUT:
3151
+
3152
+ - ``T`` -- a sequence of row words corresponding to (skew-)tableaux
3153
+
3154
+ OUTPUT:
3155
+
3156
+ The minimaj bijection `\phi^{-1}` of [BCHOPSY2017]_ applied to ``T``.
3157
+
3158
+ EXAMPLES::
3159
+
3160
+ sage: from sage.combinat.multiset_partition_into_sets_ordered import _to_minimaj_blocks
3161
+ sage: co = OrderedMultisetPartitionsIntoSets(14).an_element(); co
3162
+ [{2,3}, {2,3}, {4}]
3163
+ sage: co.to_tableaux_words()
3164
+ [[3, 2], [], [3, 2, 4]]
3165
+ sage: _to_minimaj_blocks(co.to_tableaux_words()) == co.minimaj_blocks()
3166
+ True
3167
+ """
3168
+ mu = [(i,) for i in T[-1]]
3169
+ breaks = [0] + _descents(T[-1]) + [len(mu)-1]
3170
+ T = [T[i][::-1] for i in range(len(T)-1)][::-1]
3171
+ for f in range(len(breaks)-1):
3172
+ for j in range(breaks[f],breaks[f+1]+1):
3173
+ mu[j] += tuple(i for i in T[f] if (mu[j][0] < i or j == breaks[f])
3174
+ and (j == breaks[f+1] or i <= mu[j+1][0]))
3175
+ return tuple(mu)
3176
+
3177
+
3178
+ ###############
3179
+
3180
+ class MinimajCrystal(UniqueRepresentation, Parent):
3181
+ r"""
3182
+ Crystal of ordered multiset partitions into sets with `ell` letters from
3183
+ alphabet `\{1, 2, \ldots, n\}` divided into `k` blocks.
3184
+
3185
+ Elements are represented in the minimaj ordering of blocks as in
3186
+ Benkart et al. [BCHOPSY2017]_.
3187
+
3188
+ .. NOTE::
3189
+
3190
+ Elements are not stored internally as ordered multiset partitions
3191
+ into sets, but as certain (pairs of) words stemming from the minimaj
3192
+ bijection `\phi` of [BCHOPSY2017]_. See
3193
+ :class:`sage.combinat.multiset_partition_into_sets_ordered.MinimajCrystal.Element`
3194
+ for further details.
3195
+
3196
+ AUTHORS:
3197
+
3198
+ - Anne Schilling (2018): initial draft
3199
+ - Aaron Lauve (2018): changed to use ``Letters`` crystal for elements
3200
+
3201
+ EXAMPLES::
3202
+
3203
+ sage: list(crystals.Minimaj(2,3,2)) # needs sage.modules
3204
+ [((2, 1), (1,)), ((2,), (1, 2)), ((1,), (1, 2)), ((1, 2), (2,))]
3205
+
3206
+ sage: b = crystals.Minimaj(3, 5, 2).an_element(); b # needs sage.modules
3207
+ ((2, 3, 1), (1, 2))
3208
+ sage: b.f(2) # needs sage.modules
3209
+ ((2, 3, 1), (1, 3))
3210
+ sage: b.e(2) # needs sage.modules
3211
+ """
3212
+
3213
+ def __init__(self, n, ell, k):
3214
+ """
3215
+ Initialize ``self``.
3216
+
3217
+ TESTS::
3218
+
3219
+ sage: # needs sage.modules
3220
+ sage: B = crystals.Minimaj(2,3,2)
3221
+ sage: TestSuite(B).run()
3222
+ sage: B = crystals.Minimaj(3, 5, 2)
3223
+ sage: TestSuite(B).run()
3224
+ sage: list(crystals.Minimaj(2,6,3))
3225
+ [((1, 2), (2, 1), (1, 2))]
3226
+ sage: list(crystals.Minimaj(2,5,2)) # blocks too fat for alphabet
3227
+ []
3228
+ sage: list(crystals.Minimaj(4,2,3)) # more blocks than letters
3229
+ Traceback (most recent call last):
3230
+ ...
3231
+ ValueError: n (=4), ell (=2), and k (=3) must all be positive integers
3232
+ """
3233
+ Parent.__init__(self, category=ClassicalCrystals())
3234
+ self.n = n
3235
+ self.ell = ell
3236
+ self.k = k
3237
+ if not all([n in ZZ, ell in ZZ, k in ZZ]):
3238
+ raise TypeError("n (=%s), ell (=%s), and k (=%s) must all be positive integers" % (n, ell, k))
3239
+ if not all([n > 0, ell >= k, k > 0]):
3240
+ raise ValueError("n (=%s), ell (=%s), and k (=%s) must all be positive integers" % (n, ell, k))
3241
+ self._cartan_type = CartanType(['A',n-1])
3242
+ B = Letters(['A', n-1])
3243
+ T = tensor([B]*ell)
3244
+ self._BT = (B, T)
3245
+ self._OMPs = OrderedMultisetPartitionsIntoSets(n, ell, length=k)
3246
+ self.module_generators = []
3247
+ for co in self._OMPs:
3248
+ t = co.to_tableaux_words()
3249
+ word = T(*[B(a) for a in _concatenate(t)])
3250
+ blocks = [len(h) for h in t]
3251
+ breaks = tuple([0]+running_total(blocks))
3252
+ mu = self.element_class(self, (word, breaks))
3253
+ self.module_generators.append(mu)
3254
+
3255
+ def _repr_(self):
3256
+ """
3257
+ Return the string representation of ``self``.
3258
+
3259
+ EXAMPLES::
3260
+
3261
+ sage: B = crystals.Minimaj(3,4,2); B # needs sage.modules
3262
+ Minimaj Crystal of type A_2 of words of length 4 into 2 blocks
3263
+ """
3264
+ return ("Minimaj Crystal of type A_%s of words of length %s into %s blocks"
3265
+ % (self.n-1, self.ell, self.k))
3266
+
3267
+ def _an_element_(self):
3268
+ """
3269
+ Return a typical element of ``self``.
3270
+
3271
+ EXAMPLES::
3272
+
3273
+ sage: # needs sage.modules
3274
+ sage: B = crystals.Minimaj(4,5,3)
3275
+ sage: B.an_element()
3276
+ ((2, 3, 1), (1,), (1,))
3277
+ sage: B = crystals.Minimaj(2,2,1)
3278
+ sage: B.an_element()
3279
+ ((1, 2),)
3280
+ sage: B = crystals.Minimaj(1,2,1)
3281
+ sage: B.an_element()
3282
+ Traceback (most recent call last):
3283
+ ...
3284
+ EmptySetError
3285
+ """
3286
+ t = self._OMPs.an_element().to_tableaux_words()
3287
+ breaks = tuple([0]+running_total([len(h) for h in t]))
3288
+ B,T = self._BT
3289
+ return self.element_class(self, (T(*[B(a) for a in _concatenate(t)]), breaks))
3290
+
3291
+ def _element_constructor_(self, x):
3292
+ """
3293
+ Build an element of Minimaj from an ordered multiset partition into sets.
3294
+
3295
+ EXAMPLES::
3296
+
3297
+ sage: # needs sage.modules
3298
+ sage: B1 = crystals.Minimaj(4,5,3); b = B1.an_element(); b
3299
+ ((2, 3, 1), (1,), (1,))
3300
+ sage: B1._element_constructor_(list(b))
3301
+ ((2, 3, 1), (1,), (1,))
3302
+ sage: B1._element_constructor_([[1,2,3], [2], [2]])
3303
+ ((3, 1, 2), (2,), (2,))
3304
+ sage: B2 = crystals.Minimaj(5,5,3)
3305
+ sage: B2._element_constructor_(b)
3306
+ ((2, 3, 1), (1,), (1,))
3307
+ """
3308
+ # Allow ``x`` to be either of:
3309
+ # - an ordered multiset partition into sets in ``self._OMPs``;
3310
+ # - an element of another Minimaj crystal with
3311
+ # + same `ell` and `k`, and
3312
+ # + all letters smaller or equal to ``self._n``.
3313
+ x = list(x)
3314
+ if x in self:
3315
+ t = self._OMPs(x).to_tableaux_words()
3316
+ breaks = tuple([0]+running_total([len(h) for h in t]))
3317
+ B,T = self._BT
3318
+ return self.element_class(self, (T(*[B(a) for a in _concatenate(t)]), breaks))
3319
+ else:
3320
+ raise ValueError("cannot convert %s into an element of %s" % (x, self))
3321
+
3322
+ def __contains__(self, x):
3323
+ """
3324
+ Return ``True`` if ``x`` is an element of ``self`` or an ordered
3325
+ multiset partition into sets.
3326
+
3327
+ EXAMPLES::
3328
+
3329
+ sage: # needs sage.modules
3330
+ sage: B1 = crystals.Minimaj(2,5,3); b1 = B1.an_element(); b1
3331
+ ((1, 2), (2, 1), (1,))
3332
+ sage: B2 = crystals.Minimaj(5,5,3); b2 = B2.an_element(); b2
3333
+ ((2, 3, 1), (1,), (1,))
3334
+ sage: b2a = B2(((1,2), (1,), (1,2))); b2a
3335
+ ((2, 1), (1,), (1, 2))
3336
+ sage: b1 in B2
3337
+ True
3338
+ sage: b2 in B1
3339
+ False
3340
+ sage: b2a in B1
3341
+ True
3342
+ """
3343
+ if isinstance(x, MinimajCrystal.Element):
3344
+ if x.parent() == self:
3345
+ return True
3346
+ else:
3347
+ return list(x) in self._OMPs
3348
+ else:
3349
+ return x in self._OMPs
3350
+
3351
+ def from_tableau(self, t):
3352
+ r"""
3353
+ Return the bijection `\phi^{-1}` of [BCHOPSY2017]_ applied to ``t``.
3354
+
3355
+ INPUT:
3356
+
3357
+ - ``t`` -- a sequence of column tableaux and a ribbon tableau
3358
+
3359
+ EXAMPLES::
3360
+
3361
+ sage: # needs sage.modules
3362
+ sage: B = crystals.Minimaj(3,6,3)
3363
+ sage: b = B.an_element(); b
3364
+ ((3, 1, 2), (2, 1), (1,))
3365
+ sage: t = b.to_tableaux_words(); t
3366
+ [[1], [2, 1], [], [3, 2, 1]]
3367
+ sage: B.from_tableau(t)
3368
+ ((3, 1, 2), (2, 1), (1,))
3369
+ sage: B.from_tableau(t) == b
3370
+ True
3371
+
3372
+ TESTS::
3373
+
3374
+ sage: # needs sage.modules
3375
+ sage: B = crystals.Minimaj(3,6,3)
3376
+ sage: all(mu == B.from_tableau(mu.to_tableaux_words()) for mu in B)
3377
+ True
3378
+ sage: t = B.an_element().to_tableaux_words()
3379
+ sage: B1 = crystals.Minimaj(3,6,2)
3380
+ sage: B1.from_tableau(t)
3381
+ Traceback (most recent call last):
3382
+ ...
3383
+ ValueError: ((3, 1, 2), (2, 1), (1,)) is not an element of
3384
+ Minimaj Crystal of type A_2 of words of length 6 into 2 blocks
3385
+ """
3386
+ mu = _to_minimaj_blocks(t)
3387
+ if mu in self:
3388
+ return self(mu)
3389
+ else:
3390
+ raise ValueError("%s is not an element of %s" % (mu, self))
3391
+
3392
+ def val(self, q='q'):
3393
+ r"""
3394
+ Return the `Val` polynomial corresponding to ``self``.
3395
+
3396
+ EXAMPLES:
3397
+
3398
+ Verifying Example 4.5 from [BCHOPSY2017]_::
3399
+
3400
+ sage: B = crystals.Minimaj(3, 4, 2) # for `Val_{4,1}^{(3)}` # needs sage.modules
3401
+ sage: B.val() # needs sage.modules
3402
+ (q^2+q+1)*s[2, 1, 1] + q*s[2, 2]
3403
+ """
3404
+ H = [self._OMPs(list(b)) for b in self.highest_weight_vectors()]
3405
+ Sym = SymmetricFunctions(ZZ[q])
3406
+ q = Sym.base_ring().gens()[0]
3407
+ s = Sym.schur()
3408
+ return sum((q**(t.minimaj()) * s[sorted(t.weight().values(), reverse=True)]
3409
+ for t in H), Sym.zero())
3410
+
3411
+ class Element(ElementWrapper):
3412
+ r"""
3413
+ An element of a Minimaj crystal.
3414
+
3415
+ .. NOTE::
3416
+
3417
+ Minimaj elements `b` are stored internally as pairs
3418
+ ``(w, breaks)``, where:
3419
+
3420
+ - ``w`` -- a word of length ``self.parent().ell`` over the
3421
+ letters `1` up to ``self.parent().n``;
3422
+ - ``breaks`` is a list of de-concatenation points to turn ``w``
3423
+ into a list of row words of (skew-)tableaux that represent
3424
+ `b` under the minimaj bijection `\phi` of [BCHOPSY2017]_.
3425
+
3426
+ The pair ``(w, breaks)`` may be recovered via ``b.value``.
3427
+ """
3428
+
3429
+ def _repr_(self):
3430
+ """
3431
+ Return the string representation of ``self``.
3432
+
3433
+ EXAMPLES::
3434
+
3435
+ sage: crystals.Minimaj(4,5,3).an_element() # needs sage.modules
3436
+ ((2, 3, 1), (1,), (1,))
3437
+ """
3438
+ return repr(self._minimaj_blocks_from_word_pair())
3439
+
3440
+ def __iter__(self):
3441
+ """
3442
+ Iterate over ``self._minimaj_blocks_from_word_pair()``.
3443
+
3444
+ EXAMPLES::
3445
+
3446
+ sage: b = crystals.Minimaj(4,5,3).an_element(); b # needs sage.modules
3447
+ ((2, 3, 1), (1,), (1,))
3448
+ sage: b.value # needs sage.modules
3449
+ ([1, 3, 2, 1, 1], (0, 1, 2, 5))
3450
+ sage: list(b) # needs sage.modules
3451
+ [(2, 3, 1), (1,), (1,)]
3452
+ """
3453
+ return self._minimaj_blocks_from_word_pair().__iter__()
3454
+
3455
+ def _latex_(self):
3456
+ r"""
3457
+ Return the latex representation of ``self``.
3458
+
3459
+ EXAMPLES::
3460
+
3461
+ sage: b = crystals.Minimaj(4,5,3).an_element(); b # needs sage.modules
3462
+ ((2, 3, 1), (1,), (1,))
3463
+ sage: latex(b) # needs sage.modules
3464
+ \left(\left(2, 3, 1\right), \left(1\right), \left(1\right)\right)
3465
+ """
3466
+ return latex(self._minimaj_blocks_from_word_pair())
3467
+
3468
+ def _minimaj_blocks_from_word_pair(self):
3469
+ """
3470
+ Return the tuple of tuples (in the minimaj ordering on blocks of
3471
+ ordered multiset partitions into sets) corresponding to ``self``.
3472
+
3473
+ EXAMPLES::
3474
+
3475
+ sage: B = crystals.Minimaj(4,5,3) # needs sage.modules
3476
+ sage: b = B.an_element(); b.value # needs sage.modules
3477
+ ([1, 3, 2, 1, 1], (0, 1, 2, 5))
3478
+ sage: b._minimaj_blocks_from_word_pair() # needs sage.modules
3479
+ ((2, 3, 1), (1,), (1,))
3480
+ """
3481
+ return _to_minimaj_blocks(self.to_tableaux_words())
3482
+
3483
+ def to_tableaux_words(self):
3484
+ r"""
3485
+ Return the image of the ordered multiset partition into sets ``self``
3486
+ under the minimaj bijection `\phi` of [BCHOPSY2017]_.
3487
+
3488
+ EXAMPLES::
3489
+
3490
+ sage: # needs sage.modules
3491
+ sage: B = crystals.Minimaj(4,5,3)
3492
+ sage: b = B.an_element(); b
3493
+ ((2, 3, 1), (1,), (1,))
3494
+ sage: b.to_tableaux_words()
3495
+ [[1], [3], [2, 1, 1]]
3496
+ sage: b = B([[1,3,4], [3], [3]]); b
3497
+ ((4, 1, 3), (3,), (3,))
3498
+ sage: b.to_tableaux_words()
3499
+ [[3, 1], [], [4, 3, 3]]
3500
+ """
3501
+ w, breaks = self.value
3502
+ return [[ZZ(w[a].value) for a in range(breaks[j], breaks[j+1])]
3503
+ for j in range(len(breaks)-1)]
3504
+
3505
+ def e(self, i):
3506
+ r"""
3507
+ Return `e_i` on ``self``.
3508
+
3509
+ EXAMPLES::
3510
+
3511
+ sage: B = crystals.Minimaj(4,3,2) # needs sage.modules
3512
+ sage: b = B([[2,3], [3]]); b # needs sage.modules
3513
+ ((2, 3), (3,))
3514
+ sage: [b.e(i) for i in range(1,4)] # needs sage.modules
3515
+ [((1, 3), (3,)), ((2,), (2, 3)), None]
3516
+ """
3517
+ P = self.parent()
3518
+ w, breaks = self.value
3519
+ if w.e(i) is None:
3520
+ return None
3521
+ w = w.e(i)
3522
+ return P.element_class(P, (w, breaks))
3523
+
3524
+ def f(self, i):
3525
+ r"""
3526
+ Return `f_i` on ``self``.
3527
+
3528
+ EXAMPLES::
3529
+
3530
+ sage: B = crystals.Minimaj(4,3,2) # needs sage.modules
3531
+ sage: b = B([[2,3], [3]]); b # needs sage.modules
3532
+ ((2, 3), (3,))
3533
+ sage: [b.f(i) for i in range(1,4)] # needs sage.modules
3534
+ [None, None, ((2, 3), (4,))]
3535
+ """
3536
+ P = self.parent()
3537
+ w, breaks = self.value
3538
+ if w.f(i) is None:
3539
+ return None
3540
+ w = w.f(i)
3541
+ return P.element_class(P, (w, breaks))