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
sage/combinat/rsk.py ADDED
@@ -0,0 +1,3438 @@
1
+ # sage_setup: distribution = sagemath-combinat
2
+ # sage.doctest: needs sage.combinat sage.modules
3
+ r"""
4
+ Robinson-Schensted-Knuth correspondence
5
+
6
+ AUTHORS:
7
+
8
+ - Travis Scrimshaw (2012-12-07): Initial version
9
+ - Chaman Agrawal (2019-06-24): Refactoring on the Rule class
10
+ - Matthew Lancellotti (2018): initial version of super RSK
11
+ - Jianping Pan, Wencin Poh, Anne Schilling (2020-08-31): initial version of RuleStar
12
+
13
+ Introduction
14
+ ============
15
+
16
+ The Robinson-Schensted-Knuth (RSK) correspondence is most naturally
17
+ stated as a bijection between generalized permutations (also known
18
+ as two-line arrays, biwords, ...) and pairs of semi-standard Young
19
+ tableaux `(P, Q)` of identical shape.
20
+
21
+ The basic operation in the RSK correspondence is a row insertion
22
+ `P \leftarrow k` (where `P` is a given semi-standard Young tableau,
23
+ and `k` is an integer). Different insertion algorithms have been
24
+ implemented for the RSK correspondence and can be specified as
25
+ an argument in the function call.
26
+
27
+ EXAMPLES:
28
+
29
+ We can perform RSK and its inverse map on a variety of objects::
30
+
31
+ sage: p = Tableau([[1,2,2],[2]]); q = Tableau([[1,3,3],[2]])
32
+ sage: gp = RSK_inverse(p, q); gp
33
+ [[1, 2, 3, 3], [2, 1, 2, 2]]
34
+ sage: RSK(*gp) # RSK of a biword
35
+ [[[1, 2, 2], [2]], [[1, 3, 3], [2]]]
36
+ sage: RSK([2,3,2,1,2,3]) # Robinson-Schensted of a word
37
+ [[[1, 2, 2, 3], [2], [3]], [[1, 2, 5, 6], [3], [4]]]
38
+ sage: RSK([2,3,2,1,2,3], insertion=RSK.rules.EG) # Edelman-Greene
39
+ [[[1, 2, 3], [2, 3], [3]], [[1, 2, 6], [3, 5], [4]]]
40
+ sage: m = RSK_inverse(p, q, 'matrix'); m # output as matrix
41
+ [0 1]
42
+ [1 0]
43
+ [0 2]
44
+ sage: RSK(m) # RSK of a matrix
45
+ [[[1, 2, 2], [2]], [[1, 3, 3], [2]]]
46
+
47
+ Insertions currently available
48
+ ------------------------------
49
+
50
+ The following insertion algorithms for RSK correspondence are currently
51
+ available:
52
+
53
+ - RSK insertion (:class:`~sage.combinat.rsk.RuleRSK`).
54
+ - Edelman-Greene insertion (:class:`~sage.combinat.rsk.RuleEG`), an algorithm
55
+ defined in [EG1987]_ Definition 6.20 (where it is referred to as
56
+ Coxeter-Knuth insertion).
57
+ - Hecke RSK algorithm (:class:`~sage.combinat.rsk.RuleHecke`) , defined
58
+ using the Hecke insertion studied in [BKSTY06]_ (but using rows instead
59
+ of columns).
60
+ - Dual RSK insertion (:class:`~sage.combinat.rsk.RuleDualRSK`).
61
+ - CoRSK insertion (:class:`~sage.combinat.rsk.RuleCoRSK`), defined in
62
+ [GR2018v5sol]_.
63
+ - Super RSK insertion (:class:`~sage.combinat.rsk.RuleSuperRSK`), a
64
+ combination of row and column insertions defined in [Muth2019]_.
65
+ - Star insertion (:class:`~sage.combinat.rsk.RuleStar`), defined in [MPPS2020]_.
66
+
67
+ Implementing your own insertion rule
68
+ ------------------------------------
69
+
70
+ The functions :func:`RSK()` and :func:`RSK_inverse()` are written so that it
71
+ is easy to implement insertion algorithms you come across in your research.
72
+
73
+ To implement your own insertion algorithm, you first need to import the
74
+ base class for a rule::
75
+
76
+ sage: from sage.combinat.rsk import Rule
77
+
78
+ Using the ``Rule`` class as parent class for your insertion rule,
79
+ first implement the insertion and the reverse insertion algorithm
80
+ for :func:`RSK()` and :func:`RSK_inverse()` respectively (as methods
81
+ ``forward_rule`` and ``backward_rule``). If your insertion algorithm
82
+ uses the same forward and backward rules as ``RuleRSK``, differing
83
+ only in how an entry is inserted into a row, then this is not
84
+ necessary, and it suffices to merely implement the
85
+ ``insertion`` and ``reverse_insertion`` methods.
86
+
87
+ For more information, see :class:`~sage.combinat.rsk.Rule`.
88
+
89
+ TESTS:
90
+
91
+ Check that it is a correspondence between all types of input and
92
+ the input is preserved::
93
+
94
+ sage: t1 = Tableau([[1, 2, 5], [3], [4]])
95
+ sage: t2 = Tableau([[1, 2, 3], [4], [5]])
96
+ sage: gp = RSK_inverse(t1, t2); gp
97
+ [[1, 2, 3, 4, 5], [1, 4, 5, 3, 2]]
98
+ sage: w = RSK_inverse(t1, t2, 'word'); w
99
+ word: 14532
100
+ sage: m = RSK_inverse(t1, t2, 'matrix'); m
101
+ [1 0 0 0 0]
102
+ [0 0 0 1 0]
103
+ [0 0 0 0 1]
104
+ [0 0 1 0 0]
105
+ [0 1 0 0 0]
106
+ sage: p = RSK_inverse(t1, t2, 'permutation'); p
107
+ [1, 4, 5, 3, 2]
108
+ sage: t1
109
+ [[1, 2, 5], [3], [4]]
110
+ sage: t2
111
+ [[1, 2, 3], [4], [5]]
112
+ sage: RSK(*gp) == [t1, t2]
113
+ True
114
+ sage: RSK(w) == [t1, t2]
115
+ True
116
+ sage: RSK(m) == [t1, t2]
117
+ True
118
+ sage: RSK(p) == [t1, t2]
119
+ True
120
+ sage: gp
121
+ [[1, 2, 3, 4, 5], [1, 4, 5, 3, 2]]
122
+ sage: w
123
+ word: 14532
124
+ sage: m
125
+ [1 0 0 0 0]
126
+ [0 0 0 1 0]
127
+ [0 0 0 0 1]
128
+ [0 0 1 0 0]
129
+ [0 1 0 0 0]
130
+ sage: p
131
+ [1, 4, 5, 3, 2]
132
+
133
+ REFERENCES:
134
+
135
+ .. [Knu1970] Donald E. Knuth.
136
+ *Permutations, matrices, and generalized Young tableaux*.
137
+ Pacific J. Math. Volume 34, Number 3 (1970), pp. 709-727.
138
+ http://projecteuclid.org/euclid.pjm/1102971948
139
+
140
+ .. [EG1987] Paul Edelman, Curtis Greene.
141
+ *Balanced Tableaux*.
142
+ Advances in Mathematics 63 (1987), pp. 42-99.
143
+ :doi:`10.1016/0001-8708(87)90063-6`
144
+
145
+ .. [BKSTY06] \A. Buch, A. Kresch, M. Shimozono, H. Tamvakis, and A. Yong.
146
+ *Stable Grothendieck polynomials and* `K`-*theoretic factor sequences*.
147
+ Math. Ann. **340** Issue 2, (2008), pp. 359--382.
148
+ :arxiv:`math/0601514v1`.
149
+
150
+ .. [GR2018v5sol] Darij Grinberg, Victor Reiner.
151
+ *Hopf Algebras In Combinatorics*,
152
+ :arxiv:`1409.8356v5`, available with solutions at
153
+ https://arxiv.org/src/1409.8356v5/anc/HopfComb-v73-with-solutions.pdf
154
+ """
155
+
156
+ # *****************************************************************************
157
+ # Copyright (C) 2012,2019 Travis Scrimshaw <tcscrims at gmail.com>
158
+ # 2019 Chaman Agrawal <chaman.ag at gmail.com>
159
+ # 2019 Matthew Lancellotti <mareoraft at gmail.com>
160
+ # 2020 Jianping Pan <jppan at math dot ucdavis dot edu>
161
+ # Wencin Poh <wpoh at ucdavis dot edu>
162
+ # Anne Schilling <anne at math dot ucdavis dot edu>
163
+ #
164
+ # This program is free software: you can redistribute it and/or modify
165
+ # it under the terms of the GNU General Public License as published by
166
+ # the Free Software Foundation, either version 2 of the License, or
167
+ # (at your option) any later version.
168
+ # https://www.gnu.org/licenses/
169
+ # ****************************************************************************
170
+
171
+ from bisect import bisect_left, bisect_right
172
+
173
+ from sage.misc.lazy_import import lazy_import
174
+ from sage.rings.integer_ring import ZZ
175
+ from sage.structure.element import Matrix
176
+ from sage.structure.unique_representation import UniqueRepresentation
177
+
178
+ lazy_import('sage.matrix.constructor', 'matrix')
179
+
180
+
181
+ class Rule(UniqueRepresentation):
182
+ r"""
183
+ Generic base class for an insertion rule for an RSK-type correspondence.
184
+
185
+ An instance of this class should implement a method
186
+ :meth:`insertion` (which can be applied to a letter ``j``
187
+ and a list ``r``, and modifies ``r`` in place by "bumping"
188
+ ``j`` into it appropriately; it then returns the bumped-out
189
+ entry or ``None`` if no such entry exists) and a method
190
+ :meth:`reverse_insertion` (which does the same but for reverse
191
+ bumping).
192
+ It may also implement :meth:`_backward_format_output` and
193
+ :meth:`_forward_format_output` if the RSK correspondence should
194
+ return something other than (semi)standard tableaux (in the
195
+ forward direction) and matrices or biwords (in the backward
196
+ direction).
197
+ The :meth:`to_pairs` method should also be overridden if
198
+ the input for the (forward) RSK correspondence is not the
199
+ usual kind of biwords (i.e., pairs of two `n`-tuples
200
+ `[a_1, a_2, \ldots, a_n]` and `[b_1, b_2, \ldots, b_n]`
201
+ satisfying `(a_1, b_1) \leq (a_2, b_2) \leq \cdots
202
+ \leq (a_n, b_n)` in lexicographic order).
203
+ Finally, it :meth:`forward_rule` and :meth:`backward_rule`
204
+ have to be overridden if the overall structure of the
205
+ RSK correspondence differs from that of classical RSK (see,
206
+ e.g., the case of Hecke insertion, in which a letter bumped
207
+ into a row may change a different row).
208
+ """
209
+
210
+ def to_pairs(self, obj1=None, obj2=None, check=True):
211
+ r"""
212
+ Given a valid input for the RSK algorithm, such as
213
+ two `n`-tuples ``obj1`` `= [a_1, a_2, \ldots, a_n]`
214
+ and ``obj2`` `= [b_1, b_2, \ldots, b_n]` forming a biword
215
+ (i.e., satisfying
216
+ `a_1 \leq a_2 \leq \cdots \leq a_n`, and if
217
+ `a_i = a_{i+1}`, then `b_i \leq b_{i+1}`),
218
+ or a matrix ("generalized permutation"), or a single word,
219
+ return the array
220
+ `[(a_1, b_1), (a_2, b_2), \ldots, (a_n, b_n)]`.
221
+
222
+ INPUT:
223
+
224
+ - ``obj1``, ``obj2`` -- anything representing a biword
225
+ (see the doc of :meth:`forward_rule` for the
226
+ encodings accepted)
227
+
228
+ - ``check`` -- boolean (default: ``True``); whether to check that
229
+ ``obj1`` and ``obj2`` actually define a valid biword
230
+
231
+ EXAMPLES::
232
+
233
+ sage: from sage.combinat.rsk import Rule
234
+ sage: list(Rule().to_pairs([1, 2, 2, 2], [2, 1, 1, 2]))
235
+ [(1, 2), (2, 1), (2, 1), (2, 2)]
236
+ sage: m = Matrix(ZZ, 3, 2, [0,1,1,0,0,2]) ; m
237
+ [0 1]
238
+ [1 0]
239
+ [0 2]
240
+ sage: list(Rule().to_pairs(m))
241
+ [(1, 2), (2, 1), (3, 2), (3, 2)]
242
+ """
243
+ if obj2 is None:
244
+ try:
245
+ itr = obj1._rsk_iter()
246
+ except AttributeError:
247
+ # If this is (something which looks like) a matrix
248
+ # then build the generalized permutation
249
+ try:
250
+ t = []
251
+ b = []
252
+ for i, row in enumerate(obj1):
253
+ for j, mult in enumerate(row):
254
+ if mult > 0:
255
+ t.extend([i+1]*mult)
256
+ b.extend([j+1]*mult)
257
+ itr = zip(t, b)
258
+ except TypeError:
259
+ itr = zip(range(1, len(obj1)+1), obj1)
260
+ else:
261
+ if check:
262
+ if len(obj1) != len(obj2):
263
+ raise ValueError("the two arrays must be the same length")
264
+ # Check it is a generalized permutation (i.e., biword):
265
+ # that is, the pairs of corresponding entries of obj1
266
+ # and obj2 are weakly increasing in lexicographic order.
267
+ lt = 0
268
+ lb = 0
269
+ for t, b in zip(obj1, obj2):
270
+ if t < lt or (t == lt and b < lb):
271
+ raise ValueError("invalid generalized permutation")
272
+ lt = t
273
+ lb = b
274
+ itr = zip(obj1, obj2)
275
+ return itr
276
+
277
+ def forward_rule(self, obj1, obj2, check_standard=False, check=True):
278
+ r"""
279
+ Return a pair of tableaux obtained by applying forward
280
+ insertion to the generalized permutation ``[obj1, obj2]``.
281
+
282
+ INPUT:
283
+
284
+ - ``obj1``, ``obj2`` -- can be one of the following ways to
285
+ represent a generalized permutation (or, equivalently,
286
+ biword):
287
+
288
+ - two lists ``obj1`` and ``obj2`` of equal length,
289
+ to be interpreted as the top row and the bottom row of
290
+ the biword
291
+
292
+ - a matrix ``obj1`` of nonnegative integers, to be
293
+ interpreted as the generalized permutation in matrix
294
+ form (in this case, ``obj2`` is ``None``)
295
+
296
+ - a word ``obj1`` in an ordered alphabet, to be
297
+ interpreted as the bottom row of the biword (in this
298
+ case, ``obj2`` is ``None``; the top row of the biword
299
+ is understood to be `(1, 2, \ldots, n)` by default)
300
+
301
+ - any object ``obj1`` which has a method ``_rsk_iter()``,
302
+ as long as this method returns an iterator yielding
303
+ pairs of numbers, which then are interpreted as top
304
+ entries and bottom entries in the biword (in this case,
305
+ ``obj2`` is ``None``)
306
+
307
+ - ``check_standard`` -- boolean (default: ``False``); check if either
308
+ of the resulting tableaux is a standard tableau, and if so, typecast
309
+ it as such
310
+
311
+ - ``check`` -- boolean (default: ``True``); whether to check
312
+ that ``obj1`` and ``obj2`` actually define a valid
313
+ biword
314
+
315
+ EXAMPLES::
316
+
317
+ sage: from sage.combinat.rsk import RuleRSK
318
+ sage: RuleRSK().forward_rule([3,3,2,4,1], None)
319
+ [[[1, 3, 4], [2], [3]], [[1, 2, 4], [3], [5]]]
320
+ sage: RuleRSK().forward_rule([1, 1, 1, 3, 7], None)
321
+ [[[1, 1, 1, 3, 7]], [[1, 2, 3, 4, 5]]]
322
+ sage: RuleRSK().forward_rule([7, 6, 3, 3, 1], None)
323
+ [[[1, 3], [3], [6], [7]], [[1, 4], [2], [3], [5]]]
324
+ """
325
+ itr = self.to_pairs(obj1, obj2, check=check)
326
+ p = [] # the "insertion" tableau
327
+ q = [] # the "recording" tableau
328
+ for i, j in itr:
329
+ for r, qr in zip(p, q):
330
+ j1 = self.insertion(j, r)
331
+ if j1 is None:
332
+ r.append(j)
333
+ qr.append(i) # Values are always inserted to the right
334
+ break
335
+ else:
336
+ j = j1
337
+ else:
338
+ # We made through all of the rows of p without breaking
339
+ # so we need to add a new row to p and q.
340
+ p.append([j])
341
+ q.append([i])
342
+ return self._forward_format_output(p, q, check_standard=check_standard)
343
+
344
+ def backward_rule(self, p, q, output):
345
+ r"""
346
+ Return the generalized permutation obtained by applying reverse
347
+ insertion to a pair of tableaux ``(p, q)``.
348
+
349
+ INPUT:
350
+
351
+ - ``p``, ``q`` -- two tableaux of the same shape
352
+
353
+ - ``output`` -- (default: ``'array'``) if ``q`` is semi-standard:
354
+
355
+ - ``'array'`` -- as a two-line array (i.e. generalized permutation
356
+ or biword)
357
+ - ``'matrix'`` -- as an integer matrix
358
+
359
+ and if ``q`` is standard, we can also have the output:
360
+
361
+ - ``'word'`` -- as a word
362
+
363
+ and additionally if ``p`` is standard, we can also have the output:
364
+
365
+ - ``'permutation'`` -- as a permutation
366
+
367
+ EXAMPLES::
368
+
369
+ sage: from sage.combinat.rsk import RuleRSK
370
+ sage: t1 = Tableau([[1, 3, 4], [2], [3]])
371
+ sage: t2 = Tableau([[1, 2, 4], [3], [5]])
372
+ sage: RuleRSK().backward_rule(t1, t2, 'array')
373
+ [[1, 2, 3, 4, 5], [3, 3, 2, 4, 1]]
374
+ sage: t1 = Tableau([[1, 1, 1, 3, 7]])
375
+ sage: t2 = Tableau([[1, 2, 3, 4, 5]])
376
+ sage: RuleRSK().backward_rule(t1, t2, 'array')
377
+ [[1, 2, 3, 4, 5], [1, 1, 1, 3, 7]]
378
+ sage: t1 = Tableau([[1, 3], [3], [6], [7]])
379
+ sage: t2 = Tableau([[1, 4], [2], [3], [5]])
380
+ sage: RuleRSK().backward_rule(t1, t2, 'array')
381
+ [[1, 2, 3, 4, 5], [7, 6, 3, 3, 1]]
382
+ """
383
+ from sage.combinat.tableau import SemistandardTableaux
384
+ # Make a copy of p since this is destructive to it
385
+ p_copy = [list(row) for row in p]
386
+
387
+ if q.is_standard():
388
+ rev_word = [] # This will be our word in reverse
389
+ d = {qij: i for i, Li in enumerate(q) for qij in Li}
390
+ # d is now a dictionary which assigns to each integer k the
391
+ # number of the row of q containing k.
392
+
393
+ for key in sorted(d, reverse=True):
394
+ # Delete last entry from i-th row of p_copy
395
+ i = d[key]
396
+ x = p_copy[i].pop() # Always the right-most entry
397
+ for row in reversed(p_copy[:i]):
398
+ x = self.reverse_insertion(x, row)
399
+ rev_word.append(x)
400
+ return self._backward_format_output(rev_word, None, output, p.is_standard(), True)
401
+
402
+ if q not in SemistandardTableaux():
403
+ raise ValueError("q(=%s) must be a semistandard tableau" % q)
404
+
405
+ # Thus, q is semistandard but not standard.
406
+
407
+ upper_row = []
408
+ lower_row = []
409
+ # upper_row and lower_row will be the upper and lower rows of the
410
+ # generalized permutation we get as a result, but both reversed.
411
+ d = {}
412
+ for row, Li in enumerate(q):
413
+ for col, val in enumerate(Li):
414
+ if val in d:
415
+ d[val][col] = row
416
+ else:
417
+ d[val] = {col: row}
418
+ # d is now a double family such that for every integers k and j,
419
+ # the value d[k][j] is the row i such that the (i, j)-th cell of
420
+ # q is filled with k.
421
+ for value, row_dict in sorted(d.items(), reverse=True, key=lambda x: x[0]):
422
+ for key in sorted(row_dict, reverse=True):
423
+ i = row_dict[key]
424
+ x = p_copy[i].pop() # Always the right-most entry
425
+ for row in reversed(p_copy[:i]):
426
+ x = self.reverse_insertion(x, row)
427
+ lower_row.append(x)
428
+ upper_row.append(value)
429
+ return self._backward_format_output(lower_row, upper_row, output, p.is_standard(), False)
430
+
431
+ def _forward_format_output(self, p, q, check_standard):
432
+ r"""
433
+ Return final output of the ``RSK`` correspondence from the
434
+ output of the corresponding ``forward_rule``.
435
+
436
+ EXAMPLES::
437
+
438
+ sage: from sage.combinat.rsk import RuleRSK
439
+ sage: isinstance(RuleRSK()._forward_format_output([[1, 2, 3, 4, 5]],
440
+ ....: [[1, 2, 3, 4, 5]], True)[0], StandardTableau)
441
+ True
442
+ sage: isinstance(RuleRSK()._forward_format_output([[1, 2, 3, 4, 5]],
443
+ ....: [[1, 2, 3, 4, 5]], False)[0], SemistandardTableau)
444
+ True
445
+ sage: isinstance(RuleRSK()._forward_format_output([[1, 1, 1, 3, 7]],
446
+ ....: [[1, 2, 3, 4, 5]], True)[0], SemistandardTableau)
447
+ True
448
+ """
449
+ from sage.combinat.tableau import SemistandardTableau, StandardTableau
450
+
451
+ if check_standard:
452
+ try:
453
+ P = StandardTableau(p)
454
+ except ValueError:
455
+ P = SemistandardTableau(p)
456
+ try:
457
+ Q = StandardTableau(q)
458
+ except ValueError:
459
+ Q = SemistandardTableau(q)
460
+ return [P, Q]
461
+ return [SemistandardTableau(p), SemistandardTableau(q)]
462
+
463
+ def _backward_format_output(self, lower_row, upper_row, output,
464
+ p_is_standard, q_is_standard):
465
+ r"""
466
+ Return the final output of the ``RSK_inverse`` correspondence
467
+ from the output of the corresponding ``backward_rule``.
468
+
469
+ .. NOTE::
470
+
471
+ The default implementation of ``backward_rule`` lists
472
+ bumped-out entries in the order in which the reverse
473
+ bumping happens, which is *opposite* to the order of the
474
+ final output.
475
+
476
+ EXAMPLES::
477
+
478
+ sage: from sage.combinat.rsk import RuleRSK
479
+ sage: RuleRSK()._backward_format_output([1, 2, 3, 4], None, 'array', False, True)
480
+ [[1, 2, 3, 4], [4, 3, 2, 1]]
481
+ sage: RuleRSK()._backward_format_output([1, 2, 3, 4], None, 'matrix', False, True)
482
+ [0 0 0 1]
483
+ [0 0 1 0]
484
+ [0 1 0 0]
485
+ [1 0 0 0]
486
+ sage: RuleRSK()._backward_format_output([1, 2, 3, 4], None, 'word', False, True)
487
+ word: 4321
488
+ sage: RuleRSK()._backward_format_output([3, 2, 1, 1], [2, 1, 1, 1], 'array', False, False)
489
+ [[1, 1, 1, 2], [1, 1, 2, 3]]
490
+ sage: RuleRSK()._backward_format_output([3, 2, 1, 1], [2, 1, 1, 1], 'matrix', False, False)
491
+ [2 1 0]
492
+ [0 0 1]
493
+ sage: RuleRSK()._backward_format_output([1, 2, 3, 4], None, 'word', False, False)
494
+ Traceback (most recent call last):
495
+ ...
496
+ TypeError: q must be standard to have a word as valid output
497
+ sage: RuleRSK()._backward_format_output([1, 2, 3, 4], None, 'random_type', False, False)
498
+ Traceback (most recent call last):
499
+ ...
500
+ ValueError: invalid output option
501
+ """
502
+ if q_is_standard:
503
+ if output == 'word':
504
+ from sage.combinat.words.word import Word
505
+ return Word(reversed(lower_row))
506
+ if output == 'matrix':
507
+ return to_matrix(list(range(1, len(lower_row)+1)), list(reversed(lower_row)))
508
+ if output == 'array':
509
+ return [list(range(1, len(lower_row)+1)), list(reversed(lower_row))]
510
+ raise ValueError("invalid output option")
511
+
512
+ else:
513
+ if output == 'matrix':
514
+ return to_matrix(list(reversed(upper_row)), list(reversed(lower_row)))
515
+ if output == 'array':
516
+ return [list(reversed(upper_row)), list(reversed(lower_row))]
517
+ if output in ['permutation', 'word']:
518
+ raise TypeError(
519
+ "q must be standard to have a %s as valid output" % output)
520
+ raise ValueError("invalid output option")
521
+
522
+
523
+ class RuleRSK(Rule):
524
+ r"""
525
+ Rule for the classical Robinson-Schensted-Knuth insertion.
526
+
527
+ See :func:`RSK` for the definition of this operation.
528
+
529
+ EXAMPLES::
530
+
531
+ sage: RSK([1, 2, 2, 2], [2, 1, 1, 2], insertion=RSK.rules.RSK)
532
+ [[[1, 1, 2], [2]], [[1, 2, 2], [2]]]
533
+ sage: p = Tableau([[1,2,2],[2]]); q = Tableau([[1,3,3],[2]])
534
+ sage: RSK_inverse(p, q, insertion=RSK.rules.RSK)
535
+ [[1, 2, 3, 3], [2, 1, 2, 2]]
536
+ """
537
+
538
+ def insertion(self, j, r):
539
+ r"""
540
+ Insert the letter ``j`` from the second row of the biword
541
+ into the row `r` using classical Schensted insertion,
542
+ if there is bumping to be done.
543
+
544
+ The row `r` is modified in place if bumping occurs. The bumped-out
545
+ entry, if it exists, is returned.
546
+
547
+ EXAMPLES::
548
+
549
+ sage: from sage.combinat.rsk import RuleRSK
550
+ sage: qr, r = [1,2,3,4,5], [3,3,2,4,8]
551
+ sage: j = RuleRSK().insertion(9, r)
552
+ sage: j is None
553
+ True
554
+ sage: qr, r = [1,2,3,4,5], [3,3,2,4,8]
555
+ sage: j = RuleRSK().insertion(3, r)
556
+ sage: j
557
+ 4
558
+ """
559
+ if r[-1] <= j:
560
+ return None # j needs to be added at the end of the row.
561
+ # Figure out where to insert j into the row r. The
562
+ # bisect command returns the position of the least
563
+ # element of r greater than j. We will call it y.
564
+ y_pos = bisect_right(r, j)
565
+ # Switch j and y
566
+ j, r[y_pos] = r[y_pos], j
567
+ return j
568
+
569
+ def reverse_insertion(self, x, row):
570
+ r"""
571
+ Reverse bump the row ``row`` of the current insertion tableau
572
+ with the number ``x``.
573
+
574
+ The row ``row`` is modified in place. The bumped-out entry
575
+ is returned.
576
+
577
+ EXAMPLES::
578
+
579
+ sage: from sage.combinat.rsk import RuleRSK
580
+ sage: r = [2,3,3,4,8]
581
+ sage: x = RuleRSK().reverse_insertion(4, r); r
582
+ [2, 3, 4, 4, 8]
583
+ sage: x
584
+ 3
585
+ """
586
+ y_pos = bisect_left(row, x) - 1
587
+ # switch x and y
588
+ x, row[y_pos] = row[y_pos], x
589
+ return x
590
+
591
+ def _backward_format_output(self, lower_row, upper_row, output,
592
+ p_is_standard, q_is_standard):
593
+ r"""
594
+ Return the final output of the ``RSK_inverse`` correspondence
595
+ from the output of the corresponding ``backward_rule``.
596
+
597
+ .. NOTE::
598
+
599
+ The default implementation of ``backward_rule`` lists
600
+ bumped-out entries in the order in which the reverse
601
+ bumping happens, which is *opposite* to the order of the
602
+ final output.
603
+
604
+ EXAMPLES::
605
+
606
+ sage: from sage.combinat.rsk import RuleRSK
607
+ sage: RuleRSK()._backward_format_output([1, 2, 3, 4], None,
608
+ ....: 'permutation', True, True)
609
+ [4, 3, 2, 1]
610
+ sage: RuleRSK()._backward_format_output([1, 2, 3, 4], None,
611
+ ....: 'permutation', False, True)
612
+ Traceback (most recent call last):
613
+ ...
614
+ TypeError: p must be standard to have a valid permutation as output
615
+ """
616
+ if q_is_standard and output == 'permutation':
617
+ if not p_is_standard:
618
+ raise TypeError("p must be standard to have a valid permutation as output")
619
+ from sage.combinat.permutation import Permutation
620
+ return Permutation(reversed(lower_row))
621
+ return super()._backward_format_output(lower_row, upper_row, output,
622
+ p_is_standard, q_is_standard)
623
+
624
+
625
+ class RuleEG(Rule):
626
+ r"""
627
+ Rule for Edelman-Greene insertion.
628
+
629
+ For a reduced word of a permutation (i.e., an element of a type `A`
630
+ Coxeter group), one can use Edelman-Greene insertion, an algorithm
631
+ defined in [EG1987]_ Definition 6.20 (where it is referred to as
632
+ Coxeter-Knuth insertion). The Edelman-Greene insertion is similar to the
633
+ standard row insertion except that (using the notations in
634
+ the documentation of :func:`RSK`) if `k_i` and `k_i + 1` both
635
+ exist in row `i`, we *only* set `k_{i+1} = k_i + 1` and continue.
636
+
637
+ EXAMPLES:
638
+
639
+ Let us reproduce figure 6.4 in [EG1987]_::
640
+
641
+ sage: RSK([2,3,2,1,2,3], insertion=RSK.rules.EG)
642
+ [[[1, 2, 3], [2, 3], [3]], [[1, 2, 6], [3, 5], [4]]]
643
+
644
+ Some more examples::
645
+
646
+ sage: a = [2, 1, 2, 3, 2]
647
+ sage: pq = RSK(a, insertion=RSK.rules.EG); pq
648
+ [[[1, 2, 3], [2, 3]], [[1, 3, 4], [2, 5]]]
649
+ sage: RSK(RSK_inverse(*pq, insertion=RSK.rules.EG, output='matrix'),
650
+ ....: insertion=RSK.rules.EG)
651
+ [[[1, 2, 3], [2, 3]], [[1, 3, 4], [2, 5]]]
652
+ sage: RSK_inverse(*pq, insertion=RSK.rules.EG)
653
+ [[1, 2, 3, 4, 5], [2, 1, 2, 3, 2]]
654
+
655
+ The RSK algorithm (:func:`RSK`) built using the Edelman-Greene
656
+ insertion rule ``RuleEG`` is a bijection from reduced words of
657
+ permutations/elements of a type `A` Coxeter group to pairs
658
+ consisting of an increasing tableau and a standard tableau
659
+ of the same shape (see [EG1987]_ Theorem 6.25).
660
+ The inverse of this bijection is obtained using :func:`RSK_inverse`.
661
+ If the optional parameter ``output = 'permutation'`` is set in
662
+ :func:`RSK_inverse`, then the function returns not the
663
+ reduced word itself but the permutation (of smallest possible
664
+ size) whose reduced word it is (although the order of the
665
+ letters is reverse to the usual Sage convention)::
666
+
667
+ sage: w = RSK_inverse(*pq, insertion=RSK.rules.EG, output='permutation'); w
668
+ [4, 3, 1, 2]
669
+ sage: list(reversed(a)) in w.reduced_words()
670
+ True
671
+
672
+ TESTS:
673
+
674
+ Let us check that :func:`RSK_inverse` is the inverse of :func:`RSK`
675
+ on the different types of inputs/outputs for Edelman-Greene.
676
+ First we can check on the reduced words (specifically, those that can
677
+ be obtained using the ``reduced_word()`` method from permutations)::
678
+
679
+ sage: g = lambda w: RSK_inverse(*RSK(w, insertion=RSK.rules.EG),
680
+ ....: insertion=RSK.rules.EG, output='word')
681
+ sage: all(p.reduced_word() == list(g(p.reduced_word()))
682
+ ....: for n in range(7) for p in Permutations(n))
683
+ True
684
+
685
+ In case of non-standard tableaux `P, Q`::
686
+
687
+ sage: RSK_inverse(*RSK([1, 2, 3, 2, 1], insertion='EG'),
688
+ ....: insertion='EG')
689
+ [[1, 2, 3, 4, 5], [1, 2, 3, 2, 1]]
690
+ sage: RSK_inverse(*RSK([1, 1, 1, 2], [1, 2, 3, 4],
691
+ ....: insertion=RSK.rules.EG), insertion=RSK.rules.EG)
692
+ [[1, 1, 1, 2], [1, 2, 3, 4]]
693
+ sage: RSK_inverse(*RSK([1, 2, 3, 3], [2, 1, 2, 2], insertion='EG'),
694
+ ....: insertion='EG')
695
+ [[1, 2, 3, 3], [2, 1, 2, 2]]
696
+
697
+ Since the column reading of the insertion tableau from
698
+ Edelman-Greene insertion gives one of reduced words for the
699
+ original permutation, we can also check for that::
700
+
701
+ sage: f = lambda p: [x for row in reversed(p) for x in row]
702
+ sage: g = lambda wd: RSK(wd, insertion=RSK.rules.EG)[0]
703
+ sage: all(p == Permutations(n).from_reduced_word(f(g(wd)))
704
+ ....: for n in range(6) for p in Permutations(n)
705
+ ....: for wd in p.reduced_words())
706
+ True
707
+ """
708
+
709
+ def insertion(self, j, r):
710
+ r"""
711
+ Insert the letter ``j`` from the second row of the biword
712
+ into the row `r` using Edelman-Greene insertion,
713
+ if there is bumping to be done.
714
+
715
+ The row `r` is modified in place if bumping occurs. The bumped-out
716
+ entry, if it exists, is returned.
717
+
718
+ EXAMPLES::
719
+
720
+ sage: from sage.combinat.rsk import RuleEG
721
+ sage: qr, r = [1,2,3,4,5], [3,3,2,4,8]
722
+ sage: j = RuleEG().insertion(9, r)
723
+ sage: j is None
724
+ True
725
+ sage: qr, r = [1,2,3,4,5], [2,3,4,5,8]
726
+ sage: j = RuleEG().insertion(3, r); r
727
+ [2, 3, 4, 5, 8]
728
+ sage: j
729
+ 4
730
+ sage: qr, r = [1,2,3,4,5], [2,3,5,5,8]
731
+ sage: j = RuleEG().insertion(3, r); r
732
+ [2, 3, 3, 5, 8]
733
+ sage: j
734
+ 5
735
+ """
736
+ if r[-1] <= j:
737
+ return None # j needs to be added at the end of the row
738
+ # Figure out where to insert j into the row r. The
739
+ # bisect command returns the position of the least
740
+ # element of r greater than j. We will call it y.
741
+ y_pos = bisect_right(r, j)
742
+ if r[y_pos] == j + 1 and y_pos > 0 and j == r[y_pos - 1]:
743
+ # Special bump: Nothing to do except increment j by 1
744
+ j += 1
745
+ else:
746
+ # Switch j and y
747
+ j, r[y_pos] = r[y_pos], j
748
+ return j
749
+
750
+ def reverse_insertion(self, x, row):
751
+ r"""
752
+ Reverse bump the row ``row`` of the current insertion tableau
753
+ with the number ``x``.
754
+
755
+ The row ``row`` is modified in place. The bumped-out entry
756
+ is returned.
757
+
758
+ EXAMPLES::
759
+
760
+ sage: from sage.combinat.rsk import RuleEG
761
+ sage: r = [1,1,1,2,3,3]
762
+ sage: x = RuleEG().reverse_insertion(3, r); r
763
+ [1, 1, 1, 2, 3, 3]
764
+ sage: x
765
+ 2
766
+ """
767
+ y_pos = bisect_left(row, x) - 1
768
+ if row[y_pos] == x - 1 and y_pos < len(row)-1 and row[y_pos+1] == x:
769
+ # Nothing to do except decrement x by 1.
770
+ # (Case 1 on p. 74 of Edelman-Greene [EG1987]_.)
771
+ x -= 1
772
+ else:
773
+ # switch x and y
774
+ x, row[y_pos] = row[y_pos], x
775
+ return x
776
+
777
+ def _backward_format_output(self, lower_row, upper_row, output,
778
+ p_is_standard, q_is_standard):
779
+ r"""
780
+ Return the final output of the ``RSK_inverse`` correspondence
781
+ from the output of the corresponding ``backward_rule``.
782
+
783
+ .. NOTE::
784
+
785
+ The default implementation of ``backward_rule`` lists
786
+ bumped-out entries in the order in which the reverse
787
+ bumping happens, which is *opposite* to the order of the
788
+ final output.
789
+
790
+ EXAMPLES::
791
+
792
+ sage: from sage.combinat.rsk import RuleEG
793
+ sage: RuleEG()._backward_format_output([1, 2, 3, 4], None,
794
+ ....: 'permutation', True, False)
795
+ Traceback (most recent call last):
796
+ ...
797
+ TypeError: q must be standard to have a permutation as valid output
798
+ """
799
+ if q_is_standard and output == 'permutation':
800
+ n = 0
801
+ if list(lower_row):
802
+ n = max(list(lower_row)) + 1
803
+ from sage.combinat.permutation import Permutations
804
+ return Permutations(n).from_reduced_word(list(lower_row))
805
+ else:
806
+ return super()._backward_format_output(lower_row, upper_row, output,
807
+ p_is_standard, q_is_standard)
808
+
809
+
810
+ class RuleHecke(Rule):
811
+ r"""
812
+ Rule for Hecke insertion.
813
+
814
+ The Hecke RSK algorithm is similar to the classical RSK algorithm,
815
+ but is defined using the Hecke insertion introduced in in
816
+ [BKSTY06]_ (but using rows instead of columns).
817
+ It is not clear in what generality it works; thus, following
818
+ [BKSTY06]_, we shall assume that our biword `p` has top row
819
+ `(1, 2, \ldots, n)` (or, at least, has its top row strictly
820
+ increasing).
821
+
822
+ The Hecke RSK algorithm returns a pair of an increasing tableau
823
+ and a set-valued standard tableau. If
824
+ `p = ((j_0, k_0), (j_1, k_1), \ldots, (j_{\ell-1}, k_{\ell-1}))`,
825
+ then the algorithm recursively constructs pairs
826
+ `(P_0, Q_0), (P_1, Q_1), \ldots, (P_\ell, Q_\ell)` of tableaux.
827
+ The construction of `P_{t+1}` and `Q_{t+1}` from `P_t`, `Q_t`,
828
+ `j_t` and `k_t` proceeds as follows: Set `i = j_t`, `x = k_t`,
829
+ `P = P_t` and `Q = Q_t`. We are going to insert `x` into the
830
+ increasing tableau `P` and update the set-valued "recording
831
+ tableau" `Q` accordingly. As in the classical RSK algorithm, we
832
+ first insert `x` into row `1` of `P`, then into row `2` of the
833
+ resulting tableau, and so on, until the construction terminates.
834
+ The details are different: Suppose we are inserting `x` into
835
+ row `R` of `P`. If (Case 1) there exists an entry `y` in row `R`
836
+ such that `x < y`, then let `y` be the minimal such entry. We
837
+ replace this entry `y` with `x` if the result is still an
838
+ increasing tableau; in either subcase, we then continue
839
+ recursively, inserting `y` into the next row of `P`.
840
+ If, on the other hand, (Case 2) no such `y` exists, then we
841
+ append `x` to the end of `R` if the result is an increasing
842
+ tableau (Subcase 2.1), and otherwise (Subcase 2.2) do nothing.
843
+ Furthermore, in Subcase 2.1, we add the box that we have just
844
+ filled with `x` in `P` to the shape of `Q`, and fill it with
845
+ the one-element set `\{i\}`. In Subcase 2.2, we find the
846
+ bottommost box of the column containing the rightmost box of
847
+ row `R`, and add `i` to the entry of `Q` in this box (this
848
+ entry is a set, since `Q` is set-valued). In either
849
+ subcase, we terminate the recursion, and set
850
+ `P_{t+1} = P` and `Q_{t+1} = Q`.
851
+
852
+ Notice that set-valued tableaux are encoded as tableaux whose
853
+ entries are tuples of positive integers; each such tuple is strictly
854
+ increasing and encodes a set (namely, the set of its entries).
855
+
856
+ EXAMPLES:
857
+
858
+ As an example of Hecke insertion, we reproduce
859
+ Example 2.1 in :arxiv:`0801.1319v2`::
860
+
861
+ sage: w = [5, 4, 1, 3, 4, 2, 5, 1, 2, 1, 4, 2, 4]
862
+ sage: P,Q = RSK(w, insertion=RSK.rules.Hecke); [P,Q]
863
+ [[[1, 2, 4, 5], [2, 4, 5], [3, 5], [4], [5]],
864
+ [[(1,), (4,), (5,), (7,)],
865
+ [(2,), (9,), (11, 13)],
866
+ [(3,), (12,)],
867
+ [(6,)],
868
+ [(8, 10)]]]
869
+ sage: wp = RSK_inverse(P, Q, insertion=RSK.rules.Hecke,
870
+ ....: output='list'); wp
871
+ [5, 4, 1, 3, 4, 2, 5, 1, 2, 1, 4, 2, 4]
872
+ sage: wp == w
873
+ True
874
+ """
875
+
876
+ def forward_rule(self, obj1, obj2, check_standard=False):
877
+ r"""
878
+ Return a pair of tableaux obtained by applying Hecke
879
+ insertion to the generalized permutation ``[obj1, obj2]``.
880
+
881
+ INPUT:
882
+
883
+ - ``obj1``, ``obj2`` -- can be one of the following ways to
884
+ represent a generalized permutation (or, equivalently,
885
+ biword):
886
+
887
+ - two lists ``obj1`` and ``obj2`` of equal length,
888
+ to be interpreted as the top row and the bottom row of
889
+ the biword
890
+
891
+ - a word ``obj1`` in an ordered alphabet, to be
892
+ interpreted as the bottom row of the biword (in this
893
+ case, ``obj2`` is ``None``; the top row of the biword
894
+ is understood to be `(1, 2, \ldots, n)` by default)
895
+
896
+ - ``check_standard`` -- boolean (default: ``False``); check if either
897
+ of the resulting tableaux is a standard tableau, and if so, typecast
898
+ it as such
899
+
900
+ EXAMPLES::
901
+
902
+ sage: from sage.combinat.rsk import RuleHecke
903
+ sage: p, q = RuleHecke().forward_rule([3,3,2,4,1], None);p
904
+ [[1, 4], [2], [3]]
905
+ sage: q
906
+ [[(1, 2), (4,)], [(3,)], [(5,)]]
907
+ sage: isinstance(p, SemistandardTableau)
908
+ True
909
+ sage: isinstance(q, Tableau)
910
+ True
911
+ """
912
+ from sage.combinat.tableau import SemistandardTableau, Tableau
913
+
914
+ if obj2 is None:
915
+ obj2 = obj1
916
+ obj1 = list(range(1, len(obj1) + 1))
917
+
918
+ p = [] # the "insertion" tableau
919
+ q = [] # the "recording" tableau
920
+
921
+ for i, j in zip(obj1, obj2):
922
+ for ir, r in enumerate(p):
923
+ j1 = self.insertion(j, ir, r, p)
924
+
925
+ if j1 is None:
926
+ # We must have len(p[ir-1]) > len(r), since j is coming
927
+ # from the previous row.
928
+ if r[-1] < j and (ir == 0 or p[ir-1][len(r)] < j):
929
+ # We can add a box to the row
930
+ r.append(j)
931
+ q[ir].append((i,)) # Values are always inserted to the right
932
+ else:
933
+ # We must append i to the bottom of this column
934
+ l = len(r) - 1
935
+ while ir < len(q) and len(q[ir]) > l:
936
+ ir += 1
937
+ q[ir-1][-1] = q[ir-1][-1] + (i,)
938
+ break
939
+ else:
940
+ j = j1
941
+ else:
942
+ # We made through all of the rows of p without breaking
943
+ # so we need to add a new row to p and q.
944
+ p.append([j])
945
+ q.append([(i,)])
946
+ return [SemistandardTableau(p), Tableau(q)]
947
+
948
+ def backward_rule(self, p, q, output):
949
+ r"""
950
+ Return the generalized permutation obtained by applying reverse
951
+ Hecke insertion to a pair of tableaux ``(p, q)``.
952
+
953
+ INPUT:
954
+
955
+ - ``p``, ``q`` -- two tableaux of the same shape
956
+
957
+ - ``output`` -- (default: ``'array'``) if ``q`` is semi-standard:
958
+
959
+ - ``'array'`` -- as a two-line array (i.e. generalized permutation
960
+ or biword)
961
+
962
+ and if ``q`` is standard set-valued, we can have the output:
963
+
964
+ - ``'word'`` -- as a word
965
+ - ``'list'`` -- as a list
966
+
967
+ EXAMPLES::
968
+
969
+ sage: from sage.combinat.rsk import RuleHecke
970
+ sage: t1 = Tableau([[1, 4], [2], [3]])
971
+ sage: t2 = Tableau([[(1, 2), (4,)], [(3,)], [(5,)]])
972
+ sage: RuleHecke().backward_rule(t1, t2, 'array')
973
+ [[1, 2, 3, 4, 5], [3, 3, 2, 4, 1]]
974
+ sage: t1 = Tableau([[1, 4], [2, 3]])
975
+ sage: t2 = Tableau([[(1, 2), (4,)], [(3,)], [(5,)]])
976
+ sage: RuleHecke().backward_rule(t1, t2, 'array')
977
+ Traceback (most recent call last):
978
+ ...
979
+ ValueError: p(=[[1, 4], [2, 3]]) and
980
+ q(=[[(1, 2), (4,)], [(3,)], [(5,)]]) must have the same shape
981
+ """
982
+ if p.shape() != q.shape():
983
+ raise ValueError("p(=%s) and q(=%s) must have the same shape" % (p, q))
984
+ from sage.combinat.tableau import SemistandardTableaux
985
+ if p not in SemistandardTableaux():
986
+ raise ValueError("p(=%s) must be a semistandard tableau" % p)
987
+
988
+ # Make a copy of p and q since this is destructive to it
989
+ p_copy = [list(row) for row in p]
990
+ q_copy = [[list(v) for v in row] for row in q]
991
+ # We shall work on these copies of p and q. Notice that p might get
992
+ # some empty rows in the process; we do not bother pruning them, as
993
+ # they do not matter.
994
+
995
+ # upper_row and lower_row will be the upper and lower rows of the
996
+ # generalized permutation we get as a result, but both reversed.
997
+ upper_row = []
998
+ lower_row = []
999
+ d = {}
1000
+ for ri, row in enumerate(q):
1001
+ for ci, entry in enumerate(row):
1002
+ for val in entry:
1003
+ if val in d:
1004
+ d[val][ci] = ri
1005
+ else:
1006
+ d[val] = {ci: ri}
1007
+ # d is now a double family such that for every integers k and j,
1008
+ # the value d[k][j] is the row i such that the (i, j)-th cell of
1009
+ # q is filled with k.
1010
+ for value, row_dict in sorted(d.items(), key=lambda x: -x[0]):
1011
+ for i in sorted(row_dict.values(), reverse=True):
1012
+ # These are always the right-most entry
1013
+ should_be_value = q_copy[i][-1].pop()
1014
+ assert value == should_be_value
1015
+ if not q_copy[i][-1]:
1016
+ # That is, if value was alone in cell q_copy[i][-1].
1017
+ q_copy[i].pop()
1018
+ x = p_copy[i].pop()
1019
+ else:
1020
+ x = p_copy[i][-1]
1021
+ while i > 0:
1022
+ i -= 1
1023
+ row = p_copy[i]
1024
+ x = self.reverse_insertion(i, x, row, p_copy)
1025
+ lower_row.append(x)
1026
+ upper_row.append(value)
1027
+ return self._backward_format_output(lower_row, upper_row, output, False, False)
1028
+
1029
+ def insertion(self, j, ir, r, p):
1030
+ r"""
1031
+ Insert the letter ``j`` from the second row of the biword
1032
+ into the row `r` of the increasing tableau `p` using
1033
+ Hecke insertion, provided that `r` is the `ir`-th row
1034
+ of `p`, and provided that there is bumping to be done.
1035
+
1036
+ The row `r` is modified in place if bumping occurs. The bumped-out
1037
+ entry, if it exists, is returned.
1038
+
1039
+ EXAMPLES::
1040
+
1041
+ sage: from sage.combinat.rsk import RuleHecke
1042
+ sage: from bisect import bisect_right
1043
+ sage: p, q, r = [], [], [3,3,8,8,8,9]
1044
+ sage: j, ir = 8, 1
1045
+ sage: j1 = RuleHecke().insertion(j, ir, r, p)
1046
+ sage: j1 == r[bisect_right(r, j)]
1047
+ True
1048
+ """
1049
+ if r[-1] <= j:
1050
+ return None # j needs to be added at the end of the row.
1051
+ # Figure out where to insert j into the row r. The
1052
+ # bisect command returns the position of the least
1053
+ # element of r greater than j. We will call it y.
1054
+ y_pos = bisect_right(r, j)
1055
+ y = r[y_pos]
1056
+ # Check to see if we can swap j for y
1057
+ if (y_pos == 0 or r[y_pos-1] < j) and (ir == 0 or p[ir-1][y_pos] < j):
1058
+ r[y_pos] = j
1059
+ j = y
1060
+ return j
1061
+
1062
+ def reverse_insertion(self, i, x, row, p):
1063
+ r"""
1064
+ Reverse bump the row ``row`` of the current insertion tableau
1065
+ ``p`` with the number ``x``, provided that ``row`` is the
1066
+ `i`-th row of `p`.
1067
+
1068
+ The row ``row`` is modified in place. The bumped-out entry
1069
+ is returned.
1070
+
1071
+ EXAMPLES::
1072
+
1073
+ sage: from sage.combinat.rsk import RuleHecke
1074
+ sage: from bisect import bisect_left
1075
+ sage: r = [2,3,3,4,8,9]
1076
+ sage: x, i, p = 9, 1, [1, 2]
1077
+ sage: x1 = RuleHecke().reverse_insertion(i, x, r, p)
1078
+ sage: x1 == r[bisect_left(r,x) - 1]
1079
+ True
1080
+ """
1081
+ y_pos = bisect_left(row, x) - 1
1082
+ y = row[y_pos]
1083
+ # Check to see if we can swap x for y
1084
+ if ((y_pos == len(row) - 1 or x < row[y_pos+1])
1085
+ and (i == len(p) - 1 or len(p[i+1]) <= y_pos
1086
+ or x < p[i+1][y_pos])):
1087
+ row[y_pos] = x
1088
+ x = y
1089
+ return x
1090
+
1091
+ def _backward_format_output(self, lower_row, upper_row, output,
1092
+ p_is_standard, q_is_standard):
1093
+ r"""
1094
+ Return the final output of the ``RSK_inverse`` correspondence
1095
+ from the output of the corresponding ``backward_rule``.
1096
+
1097
+ .. NOTE::
1098
+
1099
+ The default implementation of ``backward_rule`` lists
1100
+ bumped-out entries in the order in which the reverse
1101
+ bumping happens, which is *opposite* to the order of the
1102
+ final output.
1103
+
1104
+ EXAMPLES::
1105
+
1106
+ sage: from sage.combinat.rsk import RuleHecke
1107
+ sage: RuleHecke()._backward_format_output([1, 1, 3, 9], [1, 2, 3, 4],
1108
+ ....: 'array', False, False)
1109
+ [[4, 3, 2, 1], [9, 3, 1, 1]]
1110
+ sage: RuleHecke()._backward_format_output([1, 1, 3, 9], [1, 2, 3, 4],
1111
+ ....: 'word', False, False)
1112
+ Traceback (most recent call last):
1113
+ ...
1114
+ TypeError: q must be standard to have a word as valid output
1115
+ sage: RuleHecke()._backward_format_output([1, 1, 3, 9], [4, 3, 2, 1],
1116
+ ....: 'word', False, False)
1117
+ word: 9311
1118
+ sage: RuleHecke()._backward_format_output([1, 1, 3, 9], [1, 2, 3, 4],
1119
+ ....: 'list', False, False)
1120
+ Traceback (most recent call last):
1121
+ ...
1122
+ TypeError: q must be standard to have a list as valid output
1123
+ sage: RuleHecke()._backward_format_output([1, 1, 3, 9], [4, 3, 2, 1],
1124
+ ....: 'list', False, False)
1125
+ [9, 3, 1, 1]
1126
+ sage: RuleHecke()._backward_format_output([1, 1, 3, 9], [1, 2, 3, 4],
1127
+ ....: 'random_type', False, False)
1128
+ Traceback (most recent call last):
1129
+ ...
1130
+ ValueError: invalid output option
1131
+ """
1132
+ if output == 'array':
1133
+ return [list(reversed(upper_row)), list(reversed(lower_row))]
1134
+ is_standard = (upper_row == list(range(len(upper_row), 0, -1)))
1135
+ if output == 'word':
1136
+ if not is_standard:
1137
+ raise TypeError(
1138
+ "q must be standard to have a %s as valid output" % output)
1139
+ from sage.combinat.words.word import Word
1140
+ return Word(reversed(lower_row))
1141
+ if output == 'list':
1142
+ if not is_standard:
1143
+ raise TypeError(
1144
+ "q must be standard to have a %s as valid output" % output)
1145
+ return list(reversed(lower_row))
1146
+ raise ValueError("invalid output option")
1147
+
1148
+
1149
+ class RuleDualRSK(Rule):
1150
+ r"""
1151
+ Rule for dual RSK insertion.
1152
+
1153
+ Dual RSK insertion differs from classical RSK insertion in the
1154
+ following ways:
1155
+
1156
+ * The input (in terms of biwords) is no longer an arbitrary biword,
1157
+ but rather a strict biword (i.e., a pair of two lists
1158
+ `[a_1, a_2, \ldots, a_n]` and `[b_1, b_2, \ldots, b_n]` that
1159
+ satisfy the strict inequalities
1160
+ `(a_1, b_1) < (a_2, b_2) < \cdots < (a_n, b_n)` in
1161
+ lexicographic order).
1162
+ In terms of matrices, this means that the input is not an
1163
+ arbitrary matrix with nonnegative integer entries, but rather
1164
+ a `\{0, 1\}`-matrix (i.e., a matrix whose entries are `0`'s
1165
+ and `1`'s).
1166
+
1167
+ * The output still consists of two tableaux `(P, Q)` of equal
1168
+ shapes, but rather than both of them being semistandard, now
1169
+ `P` is row-strict (i.e., its transpose is semistandard) while
1170
+ `Q` is semistandard.
1171
+
1172
+ * The main difference is in the way bumping works. Namely,
1173
+ when a number `k_i` is inserted into the `i`-th row of `P`,
1174
+ it bumps out the first integer greater **or equal to** `k_i`
1175
+ in this row (rather than greater than `k_i`).
1176
+
1177
+ The RSK and dual RSK algorithms agree for permutation matrices.
1178
+
1179
+ For more information, see Chapter 7, Section 14 in [Sta-EC2]_
1180
+ (where dual RSK is called `\mathrm{RSK}^{\ast}`) or the third
1181
+ solution to Exercise 2.7.12(a) in [GR2018v5sol]_.
1182
+
1183
+ EXAMPLES::
1184
+
1185
+ sage: RSK([3,3,2,4,1], insertion=RSK.rules.dualRSK)
1186
+ [[[1, 4], [2], [3], [3]], [[1, 4], [2], [3], [5]]]
1187
+ sage: RSK(Word([3,3,2,4,1]), insertion=RSK.rules.dualRSK)
1188
+ [[[1, 4], [2], [3], [3]], [[1, 4], [2], [3], [5]]]
1189
+ sage: RSK(Word([2,3,3,2,1,3,2,3]), insertion=RSK.rules.dualRSK)
1190
+ [[[1, 2, 3], [2, 3], [2, 3], [3]], [[1, 2, 8], [3, 6], [4, 7], [5]]]
1191
+
1192
+ Using dual RSK insertion with a strict biword::
1193
+
1194
+ sage: RSK([1,1,2,4,4,5],[2,4,1,1,3,2], insertion=RSK.rules.dualRSK)
1195
+ [[[1, 2], [1, 3], [2, 4]], [[1, 1], [2, 4], [4, 5]]]
1196
+ sage: RSK([1,1,2,3,3,4,5],[1,3,2,1,3,3,2], insertion=RSK.rules.dualRSK)
1197
+ [[[1, 2, 3], [1, 2], [3], [3]], [[1, 1, 3], [2, 4], [3], [5]]]
1198
+ sage: RSK([1, 2, 2, 2], [2, 1, 2, 4], insertion=RSK.rules.dualRSK)
1199
+ [[[1, 2, 4], [2]], [[1, 2, 2], [2]]]
1200
+ sage: RSK(Word([1,1,3,4,4]), [1,4,2,1,3], insertion=RSK.rules.dualRSK)
1201
+ [[[1, 2, 3], [1], [4]], [[1, 1, 4], [3], [4]]]
1202
+ sage: RSK([1,3,3,4,4], Word([6,1,2,1,7]), insertion=RSK.rules.dualRSK)
1203
+ [[[1, 2, 7], [1], [6]], [[1, 3, 4], [3], [4]]]
1204
+
1205
+ Using dual RSK insertion with a `\{0, 1\}`-matrix::
1206
+
1207
+ sage: RSK(matrix([[0,1],[1,1]]), insertion=RSK.rules.dualRSK)
1208
+ [[[1, 2], [2]], [[1, 2], [2]]]
1209
+
1210
+ We can also give it something looking like a matrix::
1211
+
1212
+ sage: RSK([[0,1],[1,1]], insertion=RSK.rules.dualRSK)
1213
+ [[[1, 2], [2]], [[1, 2], [2]]]
1214
+
1215
+ Let us now call the inverse correspondence::
1216
+
1217
+ sage: RSK_inverse(*RSK([1, 2, 2, 2], [2, 1, 2, 3],
1218
+ ....: insertion=RSK.rules.dualRSK),insertion=RSK.rules.dualRSK)
1219
+ [[1, 2, 2, 2], [2, 1, 2, 3]]
1220
+ sage: P,Q = RSK([1, 2, 2, 2], [2, 1, 2, 3],insertion=RSK.rules.dualRSK)
1221
+ sage: RSK_inverse(P, Q, insertion=RSK.rules.dualRSK)
1222
+ [[1, 2, 2, 2], [2, 1, 2, 3]]
1223
+
1224
+ When applied to two standard tableaux, reverse dual RSK
1225
+ insertion behaves identically to the usual reverse RSK insertion::
1226
+
1227
+ sage: t1 = Tableau([[1, 2, 5], [3], [4]])
1228
+ sage: t2 = Tableau([[1, 2, 3], [4], [5]])
1229
+ sage: RSK_inverse(t1, t2, insertion=RSK.rules.dualRSK)
1230
+ [[1, 2, 3, 4, 5], [1, 4, 5, 3, 2]]
1231
+ sage: RSK_inverse(t1, t2, 'word', insertion=RSK.rules.dualRSK)
1232
+ word: 14532
1233
+ sage: RSK_inverse(t1, t2, 'matrix', insertion=RSK.rules.dualRSK)
1234
+ [1 0 0 0 0]
1235
+ [0 0 0 1 0]
1236
+ [0 0 0 0 1]
1237
+ [0 0 1 0 0]
1238
+ [0 1 0 0 0]
1239
+ sage: RSK_inverse(t1, t2, 'permutation', insertion=RSK.rules.dualRSK)
1240
+ [1, 4, 5, 3, 2]
1241
+ sage: RSK_inverse(t1, t1, 'permutation', insertion=RSK.rules.dualRSK)
1242
+ [1, 4, 3, 2, 5]
1243
+ sage: RSK_inverse(t2, t2, 'permutation', insertion=RSK.rules.dualRSK)
1244
+ [1, 2, 5, 4, 3]
1245
+ sage: RSK_inverse(t2, t1, 'permutation', insertion=RSK.rules.dualRSK)
1246
+ [1, 5, 4, 2, 3]
1247
+
1248
+ Let us check that forward and backward dual RSK are mutually
1249
+ inverse when the first tableau is merely transpose semistandard::
1250
+
1251
+ sage: p = Tableau([[1,2,2],[1]]); q = Tableau([[1,2,4],[3]])
1252
+ sage: ret = RSK_inverse(p, q, insertion=RSK.rules.dualRSK); ret
1253
+ [[1, 2, 3, 4], [1, 2, 1, 2]]
1254
+ sage: RSK_inverse(p, q, 'word', insertion=RSK.rules.dualRSK)
1255
+ word: 1212
1256
+
1257
+ In general for dual RSK::
1258
+
1259
+ sage: p = Tableau([[1,1,2],[1]]); q = Tableau([[1,3,3],[2]])
1260
+ sage: RSK_inverse(p, q, insertion=RSK.rules.dualRSK)
1261
+ [[1, 2, 3, 3], [1, 1, 1, 2]]
1262
+ sage: RSK_inverse(p, q, 'matrix', insertion=RSK.rules.dualRSK)
1263
+ [1 0]
1264
+ [1 0]
1265
+ [1 1]
1266
+
1267
+ TESTS:
1268
+
1269
+ Empty objects::
1270
+
1271
+ sage: RSK(Permutation([]), insertion=RSK.rules.dualRSK)
1272
+ [[], []]
1273
+ sage: RSK(Word([]), insertion=RSK.rules.dualRSK)
1274
+ [[], []]
1275
+ sage: RSK(matrix([[]]), insertion=RSK.rules.dualRSK)
1276
+ [[], []]
1277
+ sage: RSK([], [], insertion=RSK.rules.dualRSK)
1278
+ [[], []]
1279
+ sage: RSK([[]], insertion=RSK.rules.dualRSK)
1280
+ [[], []]
1281
+
1282
+ Check that :func:`RSK_inverse` is the inverse of :func:`RSK` on the
1283
+ different types of inputs/outputs::
1284
+
1285
+ sage: RSK_inverse(Tableau([]), Tableau([]),
1286
+ ....: insertion=RSK.rules.dualRSK)
1287
+ [[], []]
1288
+ sage: f = lambda p: RSK_inverse(*RSK(p, insertion=RSK.rules.dualRSK),
1289
+ ....: output='permutation', insertion=RSK.rules.dualRSK)
1290
+ sage: all(p == f(p) for n in range(7) for p in Permutations(n))
1291
+ True
1292
+ sage: all(RSK_inverse(*RSK(w, insertion=RSK.rules.dualRSK),
1293
+ ....: output='word', insertion=RSK.rules.dualRSK) == w
1294
+ ....: for n in range(4) for w in Words(5, n))
1295
+ True
1296
+ sage: from sage.combinat.integer_matrices import IntegerMatrices
1297
+ sage: M = IntegerMatrices([1,2,2,1], [3,1,1,1]) #this is probably wrong
1298
+ sage: all(RSK_inverse(*RSK(m, insertion=RSK.rules.dualRSK),
1299
+ ....: output='matrix', insertion=RSK.rules.dualRSK) == m
1300
+ ....: for m in M if all(x in [0, 1] for x in m))
1301
+ True
1302
+
1303
+ sage: n = ZZ.random_element(200)
1304
+ sage: p = Permutations(n).random_element()
1305
+ sage: True if p == f(p) else p
1306
+ True
1307
+
1308
+ Checking that the tableaux should be of same shape::
1309
+
1310
+ sage: RSK_inverse(Tableau([[1,2,3]]), Tableau([[1,2]]),
1311
+ ....: insertion=RSK.rules.dualRSK)
1312
+ Traceback (most recent call last):
1313
+ ...
1314
+ ValueError: p(=[[1, 2, 3]]) and q(=[[1, 2]]) must have the same shape
1315
+ """
1316
+
1317
+ def to_pairs(self, obj1=None, obj2=None, check=True):
1318
+ r"""
1319
+ Given a valid input for the dual RSK algorithm, such as
1320
+ two `n`-tuples ``obj1`` `= [a_1, a_2, \ldots, a_n]`
1321
+ and ``obj2`` `= [b_1, b_2, \ldots, b_n]` forming a strict
1322
+ biword (i.e., satisfying `a_1 \leq a_2 \leq \cdots \leq a_n`,
1323
+ and if `a_i = a_{i+1}`, then `b_i < b_{i+1}`) or a
1324
+ `\{0, 1\}`-matrix ("rook placement"), or a single word, return
1325
+ the array `[(a_1, b_1), (a_2, b_2), \ldots, (a_n, b_n)]`.
1326
+
1327
+ INPUT:
1328
+
1329
+ - ``obj1``, ``obj2`` -- anything representing a strict biword
1330
+ (see the doc of :meth:`forward_rule` for the
1331
+ encodings accepted)
1332
+
1333
+ - ``check`` -- boolean (default: ``True``); whether to check
1334
+ that ``obj1`` and ``obj2`` actually define a valid
1335
+ strict biword
1336
+
1337
+ EXAMPLES::
1338
+
1339
+ sage: from sage.combinat.rsk import RuleDualRSK
1340
+ sage: list(RuleDualRSK().to_pairs([1, 2, 2, 2], [2, 1, 2, 3]))
1341
+ [(1, 2), (2, 1), (2, 2), (2, 3)]
1342
+ sage: RuleDualRSK().to_pairs([1, 2, 2, 2], [1, 2, 3, 3])
1343
+ Traceback (most recent call last):
1344
+ ...
1345
+ ValueError: invalid strict biword
1346
+ sage: m = Matrix(ZZ, 3, 2, [0,1,1,1,0,1]) ; m
1347
+ [0 1]
1348
+ [1 1]
1349
+ [0 1]
1350
+ sage: list(RuleDualRSK().to_pairs(m))
1351
+ [(1, 2), (2, 1), (2, 2), (3, 2)]
1352
+ sage: m = Matrix(ZZ, 3, 2, [0,1,1,0,0,2]) ; m
1353
+ [0 1]
1354
+ [1 0]
1355
+ [0 2]
1356
+ sage: RuleDualRSK().to_pairs(m)
1357
+ Traceback (most recent call last):
1358
+ ...
1359
+ ValueError: dual RSK requires a {0, 1}-matrix
1360
+ """
1361
+ if obj2 is None:
1362
+ try:
1363
+ itr = obj1._rsk_iter()
1364
+ except AttributeError:
1365
+ # If this is (something which looks like) a matrix
1366
+ # then build the generalized permutation
1367
+ try:
1368
+ t = []
1369
+ b = []
1370
+ for i, row in enumerate(obj1):
1371
+ for j, mult in enumerate(row):
1372
+ if mult > 1:
1373
+ raise ValueError("dual RSK requires a {0, 1}-matrix")
1374
+ if mult > 0:
1375
+ t.append(i+1)
1376
+ b.append(j+1)
1377
+ itr = zip(t, b)
1378
+ except TypeError:
1379
+ itr = zip(range(1, len(obj1)+1), obj1)
1380
+ else:
1381
+ if check:
1382
+ if len(obj1) != len(obj2):
1383
+ raise ValueError("the two arrays must be the same length")
1384
+ # Check it is a generalized permutation (i.e., biword):
1385
+ # that is, the pairs of corresponding entries of obj1
1386
+ # and obj2 are weakly increasing in lexicographic order.
1387
+ lt = 0
1388
+ lb = 0
1389
+ for t, b in zip(obj1, obj2):
1390
+ if t < lt or (t == lt and b <= lb):
1391
+ raise ValueError("invalid strict biword")
1392
+ lt = t
1393
+ lb = b
1394
+ itr = zip(obj1, obj2)
1395
+ return itr
1396
+
1397
+ def insertion(self, j, r):
1398
+ r"""
1399
+ Insert the letter ``j`` from the second row of the biword
1400
+ into the row `r` using dual RSK insertion, if there is
1401
+ bumping to be done.
1402
+
1403
+ The row `r` is modified in place if bumping occurs. The bumped-out
1404
+ entry, if it exists, is returned.
1405
+
1406
+ EXAMPLES::
1407
+
1408
+ sage: from sage.combinat.rsk import RuleDualRSK
1409
+ sage: r = [1, 3, 4, 5]
1410
+ sage: j = RuleDualRSK().insertion(4, r); j
1411
+ 4
1412
+ sage: r
1413
+ [1, 3, 4, 5]
1414
+ sage: r = [1, 2, 3, 6, 7]
1415
+ sage: j = RuleDualRSK().insertion(4, r); j
1416
+ 6
1417
+ sage: r
1418
+ [1, 2, 3, 4, 7]
1419
+ sage: r = [1, 3]
1420
+ sage: j = RuleDualRSK().insertion(4, r); j is None
1421
+ True
1422
+ sage: r
1423
+ [1, 3]
1424
+ """
1425
+ if r[-1] < j:
1426
+ return None # j needs to be added at the end of the row.
1427
+ # Figure out where to insert j into the row r. The
1428
+ # bisect command returns the position of the least
1429
+ # element of r greater or equal to j. We will call it y.
1430
+ y_pos = bisect_left(r, j)
1431
+ # Switch j and y
1432
+ j, r[y_pos] = r[y_pos], j
1433
+ return j
1434
+
1435
+ def reverse_insertion(self, x, row):
1436
+ r"""
1437
+ Reverse bump the row ``row`` of the current insertion tableau
1438
+ with the number ``x`` using dual RSK insertion.
1439
+
1440
+ The row ``row`` is modified in place. The bumped-out entry
1441
+ is returned.
1442
+
1443
+ EXAMPLES::
1444
+
1445
+ sage: from sage.combinat.rsk import RuleDualRSK
1446
+ sage: r = [1, 2, 4, 6, 7]
1447
+ sage: x = RuleDualRSK().reverse_insertion(6, r); r
1448
+ [1, 2, 4, 6, 7]
1449
+ sage: x
1450
+ 6
1451
+ sage: r = [1, 2, 4, 5, 7]
1452
+ sage: x = RuleDualRSK().reverse_insertion(6, r); r
1453
+ [1, 2, 4, 6, 7]
1454
+ sage: x
1455
+ 5
1456
+ """
1457
+ y_pos = bisect_right(row, x) - 1
1458
+ # switch x and y
1459
+ x, row[y_pos] = row[y_pos], x
1460
+ return x
1461
+
1462
+ def _backward_format_output(self, lower_row, upper_row, output,
1463
+ p_is_standard, q_is_standard):
1464
+ r"""
1465
+ Return the final output of the ``RSK_inverse`` correspondence
1466
+ from the output of the corresponding ``backward_rule``.
1467
+
1468
+ .. NOTE::
1469
+
1470
+ The default implementation of ``backward_rule`` lists
1471
+ bumped-out entries in the order in which the reverse
1472
+ bumping happens, which is *opposite* to the order of the
1473
+ final output.
1474
+
1475
+ EXAMPLES::
1476
+
1477
+ sage: from sage.combinat.rsk import RuleDualRSK
1478
+ sage: RuleDualRSK()._backward_format_output([1, 2, 3, 4], None,
1479
+ ....: 'permutation', True, True)
1480
+ [4, 3, 2, 1]
1481
+ sage: RuleDualRSK()._backward_format_output([1, 2, 3, 4], None,
1482
+ ....: 'permutation', False, True)
1483
+ Traceback (most recent call last):
1484
+ ...
1485
+ TypeError: p must be standard to have a valid permutation as output
1486
+ """
1487
+ if q_is_standard and output == 'permutation':
1488
+ if not p_is_standard:
1489
+ raise TypeError("p must be standard to have a valid permutation as output")
1490
+ from sage.combinat.permutation import Permutation
1491
+ return Permutation(reversed(lower_row))
1492
+ else:
1493
+ return super()._backward_format_output(lower_row, upper_row, output,
1494
+ p_is_standard, q_is_standard)
1495
+
1496
+ def _forward_format_output(self, p, q, check_standard):
1497
+ r"""
1498
+ Return final output of the ``RSK`` (here, dual RSK)
1499
+ correspondence from the output of the corresponding
1500
+ ``forward_rule``.
1501
+
1502
+ EXAMPLES::
1503
+
1504
+ sage: from sage.combinat.rsk import RuleDualRSK
1505
+ sage: isinstance(RuleDualRSK()._forward_format_output([[1,2,3,4,5]],
1506
+ ....: [[1,2,3,4,5]], True)[0], StandardTableau)
1507
+ True
1508
+ sage: isinstance(RuleDualRSK()._forward_format_output([[1,2,3,4,5]],
1509
+ ....: [[1,2,3,4,5]], False)[0], Tableau)
1510
+ True
1511
+ sage: isinstance(RuleDualRSK()._forward_format_output([[1,1,1,3,7]],
1512
+ ....: [[1,2,3,4,5]], True)[0], Tableau)
1513
+ True
1514
+ """
1515
+ from sage.combinat.tableau import Tableau, StandardTableau, SemistandardTableau
1516
+
1517
+ if len(p) == 0:
1518
+ return [StandardTableau([]), StandardTableau([])]
1519
+
1520
+ if check_standard:
1521
+ try:
1522
+ P = StandardTableau(p)
1523
+ except ValueError:
1524
+ P = Tableau(p)
1525
+ try:
1526
+ Q = StandardTableau(q)
1527
+ except ValueError:
1528
+ Q = SemistandardTableau(q)
1529
+ return [P, Q]
1530
+ return [Tableau(p), SemistandardTableau(q)]
1531
+
1532
+
1533
+ class RuleCoRSK(RuleRSK):
1534
+ r"""
1535
+ Rule for coRSK insertion.
1536
+
1537
+ CoRSK insertion differs from classical RSK insertion in the
1538
+ following ways:
1539
+
1540
+ * The input (in terms of biwords) is no longer a biword,
1541
+ but rather a strict cobiword -- i.e., a pair of two lists
1542
+ `[a_1, a_2, \ldots, a_n]` and `[b_1, b_2, \ldots, b_n]` that
1543
+ satisfy the strict inequalities
1544
+ `(a_1, b_1) \widetilde{<} (a_2, b_2) \widetilde{<} \cdots
1545
+ \widetilde{<} (a_n, b_n)`, where
1546
+ the binary relation `\widetilde{<}` on pairs of integers
1547
+ is defined by having `(u_1, v_1) \widetilde{<} (u_2, v_2)`
1548
+ if and only if either `u_1 < u_2` or (`u_1 = u_2` and
1549
+ `v_1 > v_2`).
1550
+ In terms of matrices, this means that the input is not an
1551
+ arbitrary matrix with nonnegative integer entries, but rather
1552
+ a `\{0, 1\}`-matrix (i.e., a matrix whose entries are `0`'s
1553
+ and `1`'s).
1554
+
1555
+ * The output still consists of two tableaux `(P, Q)` of equal
1556
+ shapes, but rather than both of them being semistandard, now
1557
+ `Q` is row-strict (i.e., its transpose is semistandard) while
1558
+ `P` is semistandard.
1559
+
1560
+ Bumping proceeds in the same way as for RSK insertion.
1561
+
1562
+ The RSK and coRSK algorithms agree for permutation matrices.
1563
+
1564
+ For more information, see Section A.4 in [Ful1997]_ (specifically,
1565
+ construction (1d)) or the second solution to Exercise 2.7.12(a) in
1566
+ [GR2018v5sol]_.
1567
+
1568
+ EXAMPLES::
1569
+
1570
+ sage: RSK([1,2,5,3,1], insertion = RSK.rules.coRSK)
1571
+ [[[1, 1, 3], [2], [5]], [[1, 2, 3], [4], [5]]]
1572
+ sage: RSK(Word([2,3,3,2,1,3,2,3]), insertion = RSK.rules.coRSK)
1573
+ [[[1, 2, 2, 3, 3], [2, 3], [3]], [[1, 2, 3, 6, 8], [4, 7], [5]]]
1574
+ sage: RSK(Word([3,3,2,4,1]), insertion = RSK.rules.coRSK)
1575
+ [[[1, 3, 4], [2], [3]], [[1, 2, 4], [3], [5]]]
1576
+ sage: from sage.combinat.rsk import to_matrix
1577
+ sage: RSK(to_matrix([1, 1, 3, 3, 4], [3, 2, 2, 1, 3]), insertion = RSK.rules.coRSK)
1578
+ [[[1, 2, 3], [2], [3]], [[1, 3, 4], [1], [3]]]
1579
+
1580
+ Using coRSK insertion with a `\{0, 1\}`-matrix::
1581
+
1582
+ sage: RSK(matrix([[0,1],[1,0]]), insertion = RSK.rules.coRSK)
1583
+ [[[1], [2]], [[1], [2]]]
1584
+
1585
+ We can also give it something looking like a matrix::
1586
+
1587
+ sage: RSK([[0,1],[1,0]], insertion = RSK.rules.coRSK)
1588
+ [[[1], [2]], [[1], [2]]]
1589
+
1590
+ We can also use the inverse correspondence::
1591
+
1592
+ sage: RSK_inverse(*RSK([1, 2, 2, 2], [2, 3, 2, 1],
1593
+ ....: insertion=RSK.rules.coRSK),insertion=RSK.rules.coRSK)
1594
+ [[1, 2, 2, 2], [2, 3, 2, 1]]
1595
+ sage: P,Q = RSK([1, 2, 2, 2], [2, 3, 2, 1],insertion=RSK.rules.coRSK)
1596
+ sage: RSK_inverse(P, Q, insertion=RSK.rules.coRSK)
1597
+ [[1, 2, 2, 2], [2, 3, 2, 1]]
1598
+
1599
+ When applied to two standard tableaux, backwards coRSK
1600
+ insertion behaves identically to the usual backwards RSK
1601
+ insertion::
1602
+
1603
+ sage: t1 = Tableau([[1, 2, 5], [3], [4]])
1604
+ sage: t2 = Tableau([[1, 2, 3], [4], [5]])
1605
+ sage: RSK_inverse(t1, t2, insertion=RSK.rules.coRSK)
1606
+ [[1, 2, 3, 4, 5], [1, 4, 5, 3, 2]]
1607
+ sage: RSK_inverse(t1, t2, 'word', insertion=RSK.rules.coRSK)
1608
+ word: 14532
1609
+ sage: RSK_inverse(t1, t2, 'matrix', insertion=RSK.rules.coRSK)
1610
+ [1 0 0 0 0]
1611
+ [0 0 0 1 0]
1612
+ [0 0 0 0 1]
1613
+ [0 0 1 0 0]
1614
+ [0 1 0 0 0]
1615
+ sage: RSK_inverse(t1, t2, 'permutation', insertion=RSK.rules.coRSK)
1616
+ [1, 4, 5, 3, 2]
1617
+ sage: RSK_inverse(t1, t1, 'permutation', insertion=RSK.rules.coRSK)
1618
+ [1, 4, 3, 2, 5]
1619
+ sage: RSK_inverse(t2, t2, 'permutation', insertion=RSK.rules.coRSK)
1620
+ [1, 2, 5, 4, 3]
1621
+ sage: RSK_inverse(t2, t1, 'permutation', insertion=RSK.rules.coRSK)
1622
+ [1, 5, 4, 2, 3]
1623
+
1624
+ For coRSK, the first tableau is semistandard while the second tableau
1625
+ is transpose semistandard::
1626
+
1627
+ sage: p = Tableau([[1,2,2],[5]]); q = Tableau([[1,2,4],[3]])
1628
+ sage: ret = RSK_inverse(p, q, insertion=RSK.rules.coRSK); ret
1629
+ [[1, 2, 3, 4], [1, 5, 2, 2]]
1630
+ sage: RSK_inverse(p, q, 'word', insertion=RSK.rules.coRSK)
1631
+ word: 1522
1632
+
1633
+ TESTS:
1634
+
1635
+ Empty objects::
1636
+
1637
+ sage: RSK(Permutation([]), insertion=RSK.rules.coRSK)
1638
+ [[], []]
1639
+ sage: RSK(Word([]), insertion=RSK.rules.coRSK)
1640
+ [[], []]
1641
+ sage: RSK(matrix([[]]), insertion=RSK.rules.coRSK)
1642
+ [[], []]
1643
+ sage: RSK([], [], insertion=RSK.rules.coRSK)
1644
+ [[], []]
1645
+ sage: RSK([[]], insertion=RSK.rules.coRSK)
1646
+ [[], []]
1647
+
1648
+ Check that :func:`RSK_inverse` is the inverse of :func:`RSK` on the
1649
+ different types of inputs/outputs::
1650
+
1651
+ sage: RSK_inverse(Tableau([]), Tableau([]),
1652
+ ....: insertion=RSK.rules.coRSK)
1653
+ [[], []]
1654
+ sage: f = lambda p: RSK_inverse(*RSK(p, insertion=RSK.rules.coRSK),
1655
+ ....: output='permutation', insertion=RSK.rules.coRSK)
1656
+ sage: all(p == f(p) for n in range(7) for p in Permutations(n))
1657
+ True
1658
+ sage: all(RSK_inverse(*RSK(w, insertion=RSK.rules.coRSK),
1659
+ ....: output='word', insertion=RSK.rules.coRSK) == w
1660
+ ....: for n in range(4) for w in Words(5, n))
1661
+ True
1662
+ sage: from sage.combinat.integer_matrices import IntegerMatrices
1663
+ sage: M = IntegerMatrices([1,2,2,1], [3,1,1,1])
1664
+ sage: all(RSK_inverse(*RSK(m, insertion=RSK.rules.coRSK),
1665
+ ....: output='matrix', insertion=RSK.rules.coRSK) == m
1666
+ ....: for m in M if all(x in [0, 1] for x in m))
1667
+ True
1668
+
1669
+ sage: n = ZZ.random_element(200)
1670
+ sage: p = Permutations(n).random_element()
1671
+ sage: True if p == f(p) else p
1672
+ True
1673
+
1674
+ Checking that the tableaux should be of same shape::
1675
+
1676
+ sage: RSK_inverse(Tableau([[1,2,3]]), Tableau([[1,2]]),
1677
+ ....: insertion=RSK.rules.dualRSK)
1678
+ Traceback (most recent call last):
1679
+ ...
1680
+ ValueError: p(=[[1, 2, 3]]) and q(=[[1, 2]]) must have the same shape
1681
+
1682
+ Checking that the biword is a strict cobiword::
1683
+
1684
+ sage: RSK([1,2,4,3], [1,2,3,4], insertion=RSK.rules.coRSK)
1685
+ Traceback (most recent call last):
1686
+ ...
1687
+ ValueError: invalid strict cobiword
1688
+ sage: RSK([1,2,3,3], [1,2,3,4], insertion=RSK.rules.coRSK)
1689
+ Traceback (most recent call last):
1690
+ ...
1691
+ ValueError: invalid strict cobiword
1692
+ sage: RSK([1,2,3,3], [1,2,3,3], insertion=RSK.rules.coRSK)
1693
+ Traceback (most recent call last):
1694
+ ...
1695
+ ValueError: invalid strict cobiword
1696
+ """
1697
+
1698
+ def to_pairs(self, obj1=None, obj2=None, check=True):
1699
+ r"""
1700
+ Given a valid input for the coRSK algorithm, such as
1701
+ two `n`-tuples ``obj1`` `= [a_1, a_2, \ldots, a_n]`
1702
+ and ``obj2`` `= [b_1, b_2, \ldots, b_n]` forming a
1703
+ strict cobiword (i.e., satisfying
1704
+ `a_1 \leq a_2 \leq \cdots \leq a_n`, and if
1705
+ `a_i = a_{i+1}`, then `b_i > b_{i+1}`),
1706
+ or a `\{0, 1\}`-matrix ("rook placement"), or a
1707
+ single word, return the array
1708
+ `[(a_1, b_1), (a_2, b_2), \ldots, (a_n, b_n)]`.
1709
+
1710
+ INPUT:
1711
+
1712
+ - ``obj1``, ``obj2`` -- anything representing a strict
1713
+ cobiword (see the doc of :meth:`forward_rule` for
1714
+ the encodings accepted)
1715
+
1716
+ - ``check`` -- boolean (default: ``True``); whether to check
1717
+ that ``obj1`` and ``obj2`` actually define a valid
1718
+ strict cobiword
1719
+
1720
+ EXAMPLES::
1721
+
1722
+ sage: from sage.combinat.rsk import RuleCoRSK
1723
+ sage: list(RuleCoRSK().to_pairs([1, 2, 2, 2], [2, 3, 2, 1]))
1724
+ [(1, 2), (2, 3), (2, 2), (2, 1)]
1725
+ sage: RuleCoRSK().to_pairs([1, 2, 2, 2], [1, 2, 3, 3])
1726
+ Traceback (most recent call last):
1727
+ ...
1728
+ ValueError: invalid strict cobiword
1729
+ sage: m = Matrix(ZZ, 3, 2, [0,1,1,1,0,1]) ; m
1730
+ [0 1]
1731
+ [1 1]
1732
+ [0 1]
1733
+ sage: list(RuleCoRSK().to_pairs(m))
1734
+ [(1, 2), (2, 2), (2, 1), (3, 2)]
1735
+ sage: m = Matrix(ZZ, 3, 2, [0,1,1,0,0,2]) ; m
1736
+ [0 1]
1737
+ [1 0]
1738
+ [0 2]
1739
+ sage: RuleCoRSK().to_pairs(m)
1740
+ Traceback (most recent call last):
1741
+ ...
1742
+ ValueError: coRSK requires a {0, 1}-matrix
1743
+ """
1744
+ if obj2 is None:
1745
+ try:
1746
+ itr = obj1._rsk_iter()
1747
+ except AttributeError:
1748
+ # If this is (something which looks like) a matrix
1749
+ # then build the generalized permutation
1750
+ try:
1751
+ t = []
1752
+ b = []
1753
+ for i, row in enumerate(obj1):
1754
+ for j, mult in reversed(list(enumerate(row))):
1755
+ if mult > 1:
1756
+ raise ValueError("coRSK requires a {0, 1}-matrix")
1757
+ if mult > 0:
1758
+ t.append(i+1)
1759
+ b.append(j+1)
1760
+ itr = zip(t, b)
1761
+ except TypeError:
1762
+ itr = zip(range(1, len(obj1)+1), obj1)
1763
+ else:
1764
+ if check:
1765
+ if len(obj1) != len(obj2):
1766
+ raise ValueError("the two arrays must be the same length")
1767
+ # Check it is a generalized permutation (i.e., biword):
1768
+ # that is, the pairs of corresponding entries of obj1
1769
+ # and obj2 are weakly increasing in lexicographic order.
1770
+ lt = 0
1771
+ lb = 0
1772
+ for t, b in zip(obj1, obj2):
1773
+ if t < lt or (t == lt and b >= lb):
1774
+ raise ValueError("invalid strict cobiword")
1775
+ lt = t
1776
+ lb = b
1777
+ itr = zip(obj1, obj2)
1778
+ return itr
1779
+
1780
+ def _forward_format_output(self, p, q, check_standard):
1781
+ r"""
1782
+ Return final output of the ``RSK`` correspondence from the
1783
+ output of the corresponding ``forward_rule``.
1784
+
1785
+ EXAMPLES::
1786
+
1787
+ sage: from sage.combinat.rsk import RuleCoRSK
1788
+ sage: isinstance(RuleCoRSK()._forward_format_output([[1,2,3,4,5]],
1789
+ ....: [[1,2,3,4,5]], True)[0], StandardTableau)
1790
+ True
1791
+ sage: isinstance(RuleCoRSK()._forward_format_output([[1,2,3,4,5]],
1792
+ ....: [[1,2,3,4,5]], False)[0], SemistandardTableau)
1793
+ True
1794
+ sage: isinstance(RuleCoRSK()._forward_format_output([[1,1,1,3,7]],
1795
+ ....: [[1,2,3,4,5]], True)[1], Tableau)
1796
+ True
1797
+ sage: isinstance(RuleCoRSK()._forward_format_output([[1,1,1,3,7]],
1798
+ ....: [[1,2,3,4,5]], False)[1], Tableau)
1799
+ True
1800
+ """
1801
+ from sage.combinat.tableau import SemistandardTableau, StandardTableau, Tableau
1802
+
1803
+ if check_standard:
1804
+ try:
1805
+ P = StandardTableau(p)
1806
+ except ValueError:
1807
+ P = SemistandardTableau(p)
1808
+ try:
1809
+ Q = StandardTableau(q)
1810
+ except ValueError:
1811
+ Q = Tableau(q)
1812
+ return [P, Q]
1813
+ return [SemistandardTableau(p), Tableau(q)]
1814
+
1815
+ def backward_rule(self, p, q, output):
1816
+ r"""
1817
+ Return the strict cobiword obtained by applying reverse
1818
+ coRSK insertion to a pair of tableaux ``(p, q)``.
1819
+
1820
+ INPUT:
1821
+
1822
+ - ``p``, ``q`` -- two tableaux of the same shape
1823
+
1824
+ - ``output`` -- (default: ``'array'``) if ``q`` is row-strict:
1825
+
1826
+ - ``'array'`` -- as a two-line array (i.e. strict cobiword)
1827
+ - ``'matrix'`` -- as a `\{0, 1\}`-matrix
1828
+
1829
+ and if ``q`` is standard, we can have the output:
1830
+
1831
+ - ``'word'`` -- as a word
1832
+
1833
+ and additionally if ``p`` is standard, we can also have the output:
1834
+
1835
+ - ``'permutation'`` -- as a permutation
1836
+
1837
+ EXAMPLES::
1838
+
1839
+ sage: from sage.combinat.rsk import RuleCoRSK
1840
+ sage: t1 = Tableau([[1, 1, 2], [2, 3], [4]])
1841
+ sage: t2 = Tableau([[1, 4, 5], [1, 4], [2]])
1842
+ sage: RuleCoRSK().backward_rule(t1, t2, 'array')
1843
+ [[1, 1, 2, 4, 4, 5], [4, 2, 1, 3, 1, 2]]
1844
+ """
1845
+ # Make a copy of p since this is destructive to it
1846
+ p_copy = [list(row) for row in p]
1847
+
1848
+ if q.is_standard():
1849
+ rev_word = [] # This will be our word in reverse
1850
+ d = {qij: i for i, Li in enumerate(q) for qij in Li}
1851
+ # d is now a dictionary which assigns to each integer k the
1852
+ # number of the row of q containing k.
1853
+
1854
+ for key in sorted(d, reverse=True):
1855
+ # Delete last entry from i-th row of p_copy
1856
+ i = d[key]
1857
+ x = p_copy[i].pop() # Always the right-most entry
1858
+ for row in reversed(p_copy[:i]):
1859
+ x = self.reverse_insertion(x, row)
1860
+ rev_word.append(x)
1861
+ return self._backward_format_output(rev_word, None, output, p.is_standard(), True)
1862
+
1863
+ upper_row = []
1864
+ lower_row = []
1865
+ # upper_row and lower_row will be the upper and lower rows of the
1866
+ # strict cobiword we get as a result, but both reversed.
1867
+ d = {}
1868
+ for row, Li in enumerate(q):
1869
+ for val in Li:
1870
+ if val in d:
1871
+ d[val].append(row)
1872
+ else:
1873
+ d[val] = [row]
1874
+ # d is now a dictionary which assigns to each integer k the
1875
+ # list of the rows of q containing k.
1876
+ for value, row_list in sorted(d.items(), reverse=True, key=lambda x: x[0]):
1877
+ for i in sorted(row_list, reverse=True):
1878
+ x = p_copy[i].pop() # Always the right-most entry
1879
+ for row in reversed(p_copy[:i]):
1880
+ x = self.reverse_insertion(x, row)
1881
+ lower_row.append(x)
1882
+ upper_row.append(value)
1883
+ return self._backward_format_output(lower_row, upper_row, output, p.is_standard(), False)
1884
+
1885
+
1886
+ class RuleSuperRSK(RuleRSK):
1887
+ r"""
1888
+ Rule for super RSK insertion.
1889
+
1890
+ Super RSK is based on `\epsilon`-insertion, a combination of
1891
+ row and column classical RSK insertion.
1892
+
1893
+ Super RSK insertion differs from the classical RSK insertion in the
1894
+ following ways:
1895
+
1896
+ * The input (in terms of biwords) is no longer an arbitrary biword,
1897
+ but rather a restricted super biword (i.e., a pair of two lists
1898
+ `[a_1, a_2, \ldots, a_n]` and `[b_1, b_2, \ldots, b_n]` that
1899
+ contains entries with even and odd parity and pairs with mixed
1900
+ parity entries do not repeat).
1901
+
1902
+ * The output still consists of two tableaux `(P, Q)` of equal
1903
+ shapes, but rather than both of them being semistandard, now
1904
+ they are semistandard super tableaux.
1905
+
1906
+ * The main difference is in the way bumping works. Instead of having
1907
+ only row bumping super RSK uses `\epsilon`-insertion, a combination
1908
+ of classical RSK bumping along the rows and a dual RSK like bumping
1909
+ (i.e. when a number `k_i` is inserted into the `i`-th row of `P`, it
1910
+ bumps out the first integer greater **or equal to** `k_i` in the column)
1911
+ along the column.
1912
+
1913
+ EXAMPLES::
1914
+
1915
+ sage: RSK([1], [1], insertion='superRSK')
1916
+ [[[1]], [[1]]]
1917
+ sage: RSK([1, 2], [1, 3], insertion='superRSK')
1918
+ [[[1, 3]], [[1, 2]]]
1919
+ sage: RSK([1, 2, 3], [1, 3, "3p"], insertion='superRSK')
1920
+ [[[1, 3], [3']], [[1, 2], [3]]]
1921
+ sage: RSK([1, 3, "3p", "2p"], insertion='superRSK')
1922
+ [[[1, 3', 3], [2']], [[1', 1, 2'], [2]]]
1923
+ sage: RSK(["1p", "2p", 2, 2, "3p", "3p", 3, 3],
1924
+ ....: ["1p", 1, "2p", 2, "3p", "3p", "3p", 3], insertion='superRSK')
1925
+ [[[1', 2, 3', 3], [1, 3'], [2'], [3']], [[1', 2, 3', 3], [2', 3'], [2], [3]]]
1926
+ sage: P = SemistandardSuperTableau([[1, '3p', 3], ['2p']])
1927
+ sage: Q = SemistandardSuperTableau([['1p', 1, '2p'], [2]])
1928
+ sage: RSK_inverse(P, Q, insertion=RSK.rules.superRSK)
1929
+ [[1', 1, 2', 2], [1, 3, 3', 2']]
1930
+
1931
+ We apply super RSK on Example 5.1 in [Muth2019]_::
1932
+
1933
+ sage: P,Q = RSK(["1p", "2p", 2, 2, "3p", "3p", 3, 3],
1934
+ ....: ["3p", 1, 2, 3, "3p", "3p", "2p", "1p"], insertion='superRSK')
1935
+ sage: (P, Q)
1936
+ ([[1', 2', 3', 3], [1, 2, 3'], [3']], [[1', 2, 2, 3'], [2', 3, 3], [3']])
1937
+ sage: ascii_art((P, Q))
1938
+ ( 1' 2' 3' 3 1' 2 2 3' )
1939
+ ( 1 2 3' 2' 3 3 )
1940
+ ( 3' , 3' )
1941
+ sage: RSK_inverse(P, Q, insertion=RSK.rules.superRSK)
1942
+ [[1', 2', 2, 2, 3', 3', 3, 3], [3', 1, 2, 3, 3', 3', 2', 1']]
1943
+
1944
+ Example 6.1 in [Muth2019]_::
1945
+
1946
+ sage: P,Q = RSK(["1p", "2p", 2, 2, "3p", "3p", 3, 3],
1947
+ ....: ["3p", 1, 2, 3, "3p", "3p", "2p", "1p"], insertion='superRSK')
1948
+ sage: ascii_art((P, Q))
1949
+ ( 1' 2' 3' 3 1' 2 2 3' )
1950
+ ( 1 2 3' 2' 3 3 )
1951
+ ( 3' , 3' )
1952
+ sage: RSK_inverse(P, Q, insertion=RSK.rules.superRSK)
1953
+ [[1', 2', 2, 2, 3', 3', 3, 3], [3', 1, 2, 3, 3', 3', 2', 1']]
1954
+
1955
+ sage: P,Q = RSK(["1p", 1, "2p", 2, "3p", "3p", "3p", 3],
1956
+ ....: [3, "2p", 3, 2, "3p", "3p", "1p", 2], insertion='superRSK')
1957
+ sage: ascii_art((P, Q))
1958
+ ( 1' 2 2 3' 1' 2' 3' 3 )
1959
+ ( 2' 3 3 1 2 3' )
1960
+ ( 3' , 3' )
1961
+ sage: RSK_inverse(P, Q, insertion=RSK.rules.superRSK)
1962
+ [[1', 1, 2', 2, 3', 3', 3', 3], [3, 2', 3, 2, 3', 3', 1', 2]]
1963
+
1964
+ Let us now call the inverse correspondence::
1965
+
1966
+ sage: P, Q = RSK([1, 2, 2, 2], [2, 1, 2, 3],
1967
+ ....: insertion=RSK.rules.superRSK)
1968
+ sage: RSK_inverse(P, Q, insertion=RSK.rules.superRSK)
1969
+ [[1, 2, 2, 2], [2, 1, 2, 3]]
1970
+
1971
+ When applied to two tableaux with only even parity elements, reverse super
1972
+ RSK insertion behaves identically to the usual reversel RSK insertion::
1973
+
1974
+ sage: t1 = Tableau([[1, 2, 5], [3], [4]])
1975
+ sage: t2 = Tableau([[1, 2, 3], [4], [5]])
1976
+ sage: RSK_inverse(t1, t2, insertion=RSK.rules.RSK)
1977
+ [[1, 2, 3, 4, 5], [1, 4, 5, 3, 2]]
1978
+ sage: t1 = SemistandardSuperTableau([[1, 2, 5], [3], [4]])
1979
+ sage: t2 = SemistandardSuperTableau([[1, 2, 3], [4], [5]])
1980
+ sage: RSK_inverse(t1, t2, insertion=RSK.rules.superRSK)
1981
+ [[1, 2, 3, 4, 5], [1, 4, 5, 3, 2]]
1982
+
1983
+ TESTS:
1984
+
1985
+ Empty objects::
1986
+
1987
+ sage: RSK(Word([]), insertion=RSK.rules.superRSK)
1988
+ [[], []]
1989
+ sage: RSK([], [], insertion=RSK.rules.superRSK)
1990
+ [[], []]
1991
+
1992
+ Check that :func:`RSK_inverse` is the inverse of :func:`RSK` on the
1993
+ different types of inputs/outputs::
1994
+
1995
+ sage: from sage.combinat.shifted_primed_tableau import PrimedEntry
1996
+ sage: RSK_inverse(SemistandardSuperTableau([]),
1997
+ ....: SemistandardSuperTableau([]), insertion=RSK.rules.superRSK)
1998
+ [[], []]
1999
+ sage: f = lambda p: RSK_inverse(*RSK(p, insertion=RSK.rules.superRSK),
2000
+ ....: insertion=RSK.rules.superRSK)
2001
+ sage: all(p == f(p)[1] for n in range(5) for p in Permutations(n))
2002
+ True
2003
+
2004
+ sage: SST = StandardSuperTableaux([3,2,1])
2005
+ sage: f = lambda P, Q: RSK(*RSK_inverse(P, Q, insertion=RSK.rules.superRSK),
2006
+ ....: insertion=RSK.rules.superRSK)
2007
+ sage: all([P, Q] == f(P, Q) for n in range(7) for la in Partitions(n)
2008
+ ....: for P in StandardSuperTableaux(la) for Q in StandardSuperTableaux(la))
2009
+ True
2010
+
2011
+ Checking that tableaux should be of same shape::
2012
+
2013
+ sage: RSK_inverse(SemistandardSuperTableau([[1, 2, 3]]),
2014
+ ....: SemistandardSuperTableau([[1, 2]]),
2015
+ ....: insertion=RSK.rules.superRSK)
2016
+ Traceback (most recent call last):
2017
+ ...
2018
+ ValueError: p(=[[1, 2, 3]]) and q(=[[1, 2]]) must have the same shape
2019
+ """
2020
+
2021
+ def to_pairs(self, obj1=None, obj2=None, check=True):
2022
+ r"""
2023
+ Given a valid input for the super RSK algorithm, such as
2024
+ two `n`-tuples ``obj1`` `= [a_1, a_2, \ldots, a_n]`
2025
+ and ``obj2`` `= [b_1, b_2, \ldots, b_n]` forming a restricted
2026
+ super biword (i.e., entries with even and odd parity and no
2027
+ repetition of corresponding pairs with mixed parity entries)
2028
+ return the array `[(a_1, b_1), (a_2, b_2), \ldots, (a_n, b_n)]`.
2029
+
2030
+ INPUT:
2031
+
2032
+ - ``obj1``, ``obj2`` -- anything representing a restricted super biword
2033
+ (see the doc of :meth:`forward_rule` for the
2034
+ encodings accepted)
2035
+
2036
+ - ``check`` -- boolean (default: ``True``); whether to check
2037
+ that ``obj1`` and ``obj2`` actually define a valid
2038
+ restricted super biword
2039
+
2040
+ EXAMPLES::
2041
+
2042
+ sage: from sage.combinat.rsk import RuleSuperRSK
2043
+ sage: list(RuleSuperRSK().to_pairs([2, '1p', 1],[1, 1, '1p']))
2044
+ [(2, 1), (1', 1), (1, 1')]
2045
+ sage: list(RuleSuperRSK().to_pairs([1, '1p', '2p']))
2046
+ [(1', 1), (1, 1'), (2', 2')]
2047
+ sage: list(RuleSuperRSK().to_pairs([1, 1], ['1p', '1p']))
2048
+ Traceback (most recent call last):
2049
+ ...
2050
+ ValueError: invalid restricted superbiword
2051
+ """
2052
+ from sage.combinat.shifted_primed_tableau import PrimedEntry
2053
+ # Initializing itr for itr = None case
2054
+ itr = None
2055
+ if obj2 is None:
2056
+ try:
2057
+ itr = obj1._rsk_iter()
2058
+ except AttributeError:
2059
+ # set recording list (obj1) to default value [1', 1, 2', 2, ...]
2060
+ obj2, obj1 = obj1, []
2061
+ a = ZZ.one() / ZZ(2)
2062
+ for i in range(len(obj2)):
2063
+ obj1.append(a)
2064
+ a = a + ZZ.one() / ZZ(2)
2065
+ else:
2066
+ if check:
2067
+ if len(obj1) != len(obj2):
2068
+ raise ValueError("the two arrays must be the same length")
2069
+ mixed_parity = []
2070
+ # Check it is a restricted superbiword: that is,
2071
+ # the entries can have even or odd parity, but repetition of
2072
+ # the pairs of corresponding entries of obj1
2073
+ # and obj2 with mixed-parity is not allowed
2074
+ for t, b in zip(obj1, obj2):
2075
+ if PrimedEntry(t).is_primed() != PrimedEntry(b).is_primed():
2076
+ if (t, b) in mixed_parity:
2077
+ raise ValueError("invalid restricted superbiword")
2078
+ else:
2079
+ mixed_parity.append((t, b))
2080
+ # Since the _rsk_iter() gives unprimed entries
2081
+ # We will create obj1 and obj2 from it.
2082
+ if itr:
2083
+ obj1, obj2 = [], []
2084
+ for i, j in itr:
2085
+ obj1.append(i)
2086
+ obj2.append(j)
2087
+ # Converting entries of obj1 and obj2 to PrimedEntry
2088
+ for i in range(len(obj1)):
2089
+ obj1[i] = PrimedEntry(obj1[i])
2090
+ obj2[i] = PrimedEntry(obj2[i])
2091
+ return zip(obj1, obj2)
2092
+
2093
+ def _get_col(self, t, col_index):
2094
+ r"""
2095
+ Return the column as a list of a given tableau ``t`` (list of lists)
2096
+ at index ``col_index`` (indexing starting from zero).
2097
+
2098
+ EXAMPLES::
2099
+
2100
+ sage: from sage.combinat.rsk import RuleSuperRSK
2101
+ sage: t = [[1,2,3,4], [5,6,7,8], [9,10]];
2102
+ sage: RuleSuperRSK()._get_col(t, 0)
2103
+ [1, 5, 9]
2104
+ sage: RuleSuperRSK()._get_col(t, 2)
2105
+ [3, 7]
2106
+ """
2107
+ # t is the tableau (list of lists)
2108
+ # compute how many rows will contribute to the col
2109
+ num_rows_long_enough = 0
2110
+ for row in t:
2111
+ if len(row) > col_index:
2112
+ num_rows_long_enough += 1
2113
+ else:
2114
+ break
2115
+ # create the col
2116
+ col = [t[row_index][col_index] for row_index in range(num_rows_long_enough)]
2117
+ return col
2118
+
2119
+ def _set_col(self, t, col_index, col):
2120
+ r"""
2121
+ Set the column of a given tableau ``t`` (list of lists) at
2122
+ index ``col_index`` (indexing starting from zero) as ``col``.
2123
+
2124
+ .. NOTE::
2125
+
2126
+ If ``length(col)`` is greater than the corresponding column in
2127
+ tableau ``t`` then only those rows of ``t`` will be set which
2128
+ have ``length(row) <= col_index``. Similarly if ``length(col)``
2129
+ is less than the corresponding column in tableau ``t`` then only
2130
+ those entries of the corresponding column in ``t`` which have row
2131
+ index less than ``length(col)`` will be set, rest will remain
2132
+ unchanged.
2133
+
2134
+ EXAMPLES::
2135
+
2136
+ sage: from sage.combinat.rsk import RuleSuperRSK
2137
+ sage: t = [[1,2,3,4], [5,6,7,8], [9,10]]
2138
+ sage: col = [1, 2, 3, 4]
2139
+ sage: RuleSuperRSK()._set_col(t, 0, col); t
2140
+ [[1, 2, 3, 4], [2, 6, 7, 8], [3, 10], [4]]
2141
+ sage: col = [1]
2142
+ sage: RuleSuperRSK()._set_col(t, 2, col); t
2143
+ [[1, 2, 1, 4], [2, 6, 7, 8], [3, 10], [4]]
2144
+ """
2145
+ # overwrite a column in tableau t (list of lists) with col
2146
+ for row_index, val in enumerate(col):
2147
+ # add a box/node if necessary
2148
+ if row_index == len(t):
2149
+ t.append([])
2150
+ if col_index == len(t[row_index]):
2151
+ t[row_index].append(None)
2152
+ # set value
2153
+ t[row_index][col_index] = val
2154
+
2155
+ def forward_rule(self, obj1, obj2, check_standard=False, check=True):
2156
+ r"""
2157
+ Return a pair of tableaux obtained by applying forward
2158
+ insertion to the restricted super biword ``[obj1, obj2]``.
2159
+
2160
+ INPUT:
2161
+
2162
+ - ``obj1``, ``obj2`` -- can be one of the following ways to
2163
+ represent a generalized permutation (or, equivalently,
2164
+ biword):
2165
+
2166
+ - two lists ``obj1`` and ``obj2`` of equal length,
2167
+ to be interpreted as the top row and the bottom row of
2168
+ the biword
2169
+
2170
+ - a word ``obj1`` in an ordered alphabet, to be
2171
+ interpreted as the bottom row of the biword (in this
2172
+ case, ``obj2`` is ``None``; the top row of the biword
2173
+ is understood to be `(1, 2, \ldots, n)` by default)
2174
+
2175
+ - any object ``obj1`` which has a method ``_rsk_iter()``,
2176
+ as long as this method returns an iterator yielding
2177
+ pairs of numbers, which then are interpreted as top
2178
+ entries and bottom entries in the biword (in this case,
2179
+ ``obj2`` is ``None``)
2180
+
2181
+ - ``check_standard`` -- boolean (default: ``False``); check if either
2182
+ of the resulting tableaux is a standard super tableau, and if so,
2183
+ typecast it as such
2184
+
2185
+ - ``check`` -- boolean (default: ``True``); whether to check
2186
+ that ``obj1`` and ``obj2`` actually define a valid
2187
+ restricted super biword
2188
+
2189
+ EXAMPLES::
2190
+
2191
+ sage: from sage.combinat.rsk import RuleSuperRSK
2192
+ sage: p, q = RuleSuperRSK().forward_rule([1, 2], [1, 3]); p
2193
+ [[1, 3]]
2194
+ sage: q
2195
+ [[1, 2]]
2196
+ sage: isinstance(p, SemistandardSuperTableau)
2197
+ True
2198
+ sage: isinstance(q, SemistandardSuperTableau)
2199
+ True
2200
+ """
2201
+ itr = self.to_pairs(obj1, obj2, check=check)
2202
+ p = [] # the "insertion" tableau
2203
+ q = [] # the "recording" tableau
2204
+ for i, j in itr:
2205
+ # loop
2206
+ row_index = -1
2207
+ col_index = -1
2208
+ epsilon = 1 if i.is_primed() else 0
2209
+ while True:
2210
+ if i.is_primed() == j.is_primed():
2211
+ # row insertion
2212
+ row_index += 1
2213
+ if row_index == len(p):
2214
+ p.append([j])
2215
+ q.append([i])
2216
+ break
2217
+ else:
2218
+ j1, col_index = self.insertion(j, p[row_index], epsilon=epsilon)
2219
+ if j1 is None:
2220
+ p[row_index].append(j)
2221
+ q[row_index].append(i)
2222
+ break
2223
+ else:
2224
+ j = j1
2225
+ else:
2226
+ # column insertion
2227
+ col_index += 1
2228
+ if not p or col_index == len(p[0]):
2229
+ self._set_col(p, col_index, [j])
2230
+ self._set_col(q, col_index, [i])
2231
+ break
2232
+ else:
2233
+ # retrieve column
2234
+ c = self._get_col(p, col_index)
2235
+ j1, row_index = self.insertion(j, c, epsilon=epsilon)
2236
+ if j1 is None:
2237
+ c.append(j)
2238
+ self._set_col(p, col_index, c)
2239
+ if col_index == 0:
2240
+ q.append([])
2241
+ q[row_index].append(i)
2242
+ break
2243
+ else:
2244
+ j = j1
2245
+ self._set_col(p, col_index, c)
2246
+ return self._forward_format_output(p, q, check_standard=check_standard)
2247
+
2248
+ def insertion(self, j, r, epsilon=0):
2249
+ r"""
2250
+ Insert the letter ``j`` from the second row of the biword
2251
+ into the row ``r`` using dual RSK insertion or classical
2252
+ Schensted insertion depending on the value of ``epsilon``,
2253
+ if there is bumping to be done.
2254
+
2255
+ The row `r` is modified in place if bumping occurs. The bumped-out
2256
+ entry, if it exists, is returned.
2257
+
2258
+ EXAMPLES::
2259
+
2260
+ sage: from sage.combinat.rsk import RuleSuperRSK
2261
+ sage: from bisect import bisect_left, bisect_right
2262
+ sage: r = [1, 3, 3, 3, 4]
2263
+ sage: j = 3
2264
+ sage: j, y_pos = RuleSuperRSK().insertion(j, r, epsilon=0); r
2265
+ [1, 3, 3, 3, 3]
2266
+ sage: j
2267
+ 4
2268
+ sage: y_pos
2269
+ 4
2270
+ sage: r = [1, 3, 3, 3, 4]
2271
+ sage: j = 3
2272
+ sage: j, y_pos = RuleSuperRSK().insertion(j, r, epsilon=1); r
2273
+ [1, 3, 3, 3, 4]
2274
+ sage: j
2275
+ 3
2276
+ sage: y_pos
2277
+ 1
2278
+ """
2279
+ bisect = bisect_right if epsilon == 0 else bisect_left
2280
+
2281
+ if (r[-1] < j) or (r[-1] == j and epsilon == 0):
2282
+ return None, len(r) # j needs to be added at the end of the list r.
2283
+ # Figure out where to insert j into the list r. The
2284
+ # bisect command returns the position of the least
2285
+ # element of r greater than j. We will call it y.
2286
+ y_pos = bisect(r, j)
2287
+ # Switch j and y
2288
+ j, r[y_pos] = r[y_pos], j
2289
+ return j, y_pos
2290
+
2291
+ def _forward_format_output(self, p, q, check_standard):
2292
+ r"""
2293
+ Return final output of the ``RSK`` (here, super RSK)
2294
+ correspondence from the output of the corresponding
2295
+ ``forward_rule``.
2296
+
2297
+ EXAMPLES::
2298
+
2299
+ sage: from sage.combinat.rsk import RuleSuperRSK
2300
+ sage: isinstance(RuleSuperRSK()._forward_format_output(
2301
+ ....: [['1p', 1, '2p']], [['1p', '1', '2p']], True)[0],
2302
+ ....: StandardSuperTableau)
2303
+ True
2304
+ sage: isinstance(RuleSuperRSK()._forward_format_output(
2305
+ ....: [[1, '2p', 3]], [[1, 2, 3]], False)[0],
2306
+ ....: SemistandardSuperTableau)
2307
+ True
2308
+ sage: isinstance(RuleSuperRSK()._forward_format_output(
2309
+ ....: [[1, 1, 3]], [[1, 2, 3]], True)[0],
2310
+ ....: SemistandardSuperTableau)
2311
+ True
2312
+ """
2313
+ from sage.combinat.tableau import StandardTableau
2314
+ from sage.combinat.super_tableau import SemistandardSuperTableau, StandardSuperTableau
2315
+
2316
+ if not p:
2317
+ return [StandardTableau([]), StandardTableau([])]
2318
+ if check_standard:
2319
+ try:
2320
+ P = StandardSuperTableau(p)
2321
+ except ValueError:
2322
+ P = SemistandardSuperTableau(p)
2323
+ try:
2324
+ Q = StandardSuperTableau(q)
2325
+ except ValueError:
2326
+ Q = SemistandardSuperTableau(q)
2327
+ return [P, Q]
2328
+ return [SemistandardSuperTableau(p), SemistandardSuperTableau(q)]
2329
+
2330
+ def backward_rule(self, p, q, output='array'):
2331
+ r"""
2332
+ Return the restricted super biword obtained by applying reverse
2333
+ super RSK insertion to a pair of tableaux ``(p, q)``.
2334
+
2335
+ INPUT:
2336
+
2337
+ - ``p``, ``q`` -- two tableaux of the same shape
2338
+
2339
+ - ``output`` -- (default: ``'array'``) if ``q`` is row-strict:
2340
+
2341
+ - ``'array'`` -- as a two-line array (i.e. restricted super biword)
2342
+
2343
+ and if ``q`` is standard, we can have the output:
2344
+
2345
+ - ``'word'`` -- as a word
2346
+
2347
+ EXAMPLES::
2348
+
2349
+ sage: from sage.combinat.rsk import RuleSuperRSK
2350
+ sage: t1 = SemistandardSuperTableau([['1p', '3p', '4p'], [2], [3]])
2351
+ sage: t2 = SemistandardSuperTableau([[1, 2, 4], [3], [5]])
2352
+ sage: RuleSuperRSK().backward_rule(t1, t2, 'array')
2353
+ [[1, 2, 3, 4, 5], [4', 3, 3', 2, 1']]
2354
+ sage: t1 = SemistandardSuperTableau([[1, 3], ['3p']])
2355
+ sage: t2 = SemistandardSuperTableau([[1, 2], [3]])
2356
+ sage: RuleSuperRSK().backward_rule(t1, t2, 'array')
2357
+ [[1, 2, 3], [1, 3, 3']]
2358
+ """
2359
+ p_copy = [list(row) for row in p]
2360
+ upper_row = []
2361
+ lower_row = []
2362
+ # upper_row and lower_row will be the upper and lower rows of the
2363
+ # generalized permutation we get as a result, but both reversed
2364
+ d = {}
2365
+ for row, Li in enumerate(q):
2366
+ for col, val in enumerate(Li):
2367
+ if val in d and col in d[val]:
2368
+ d[val][col].append(row)
2369
+ elif val not in d:
2370
+ d[val] = {col: [row]}
2371
+ else:
2372
+ d[val][col] = [row]
2373
+ # d is now a double family such that for every integers k and j,
2374
+ # the value d[k][j] is the list of rows i such that the (i, j)-th
2375
+ # cell of q is filled with k.
2376
+ for value, iter_dict in sorted(d.items(), reverse=True, key=lambda x: x[0]):
2377
+ epsilon = 1 if value.is_primed() else 0
2378
+ if epsilon == 1:
2379
+ iter_copy = dict(iter_dict)
2380
+ iter_dict = {}
2381
+ for k, v in iter_copy.items():
2382
+ for vi in v:
2383
+ if vi in iter_dict:
2384
+ iter_dict[vi].append(k)
2385
+ else:
2386
+ iter_dict[vi] = [k]
2387
+ for key in sorted(iter_dict, reverse=True):
2388
+ for rows in iter_dict[key]:
2389
+ row_index, col_index = (rows, key) if epsilon == 0 else (key, rows)
2390
+ x = p_copy[row_index].pop() # Always the right-most entry
2391
+ while True:
2392
+ if value.is_primed() == x.is_primed():
2393
+ # row bumping
2394
+ row_index -= 1
2395
+ if row_index < 0:
2396
+ break
2397
+ x, col_index = self.reverse_insertion(x, p_copy[row_index], epsilon=epsilon)
2398
+ else:
2399
+ # column bumping
2400
+ col_index -= 1
2401
+ if col_index < 0:
2402
+ break
2403
+ c = self._get_col(p_copy, col_index)
2404
+ x, row_index = self.reverse_insertion(x, c, epsilon=epsilon)
2405
+ self._set_col(p_copy, col_index, c)
2406
+ upper_row.append(value)
2407
+ lower_row.append(x)
2408
+ return self._backward_format_output(lower_row, upper_row, output, q.is_standard())
2409
+
2410
+ def reverse_insertion(self, x, row, epsilon=0):
2411
+ r"""
2412
+ Reverse bump the row ``row`` of the current insertion tableau
2413
+ with the number ``x`` using dual RSK insertion or classical
2414
+ Schensted insertion depending on the value of `epsilon`.
2415
+
2416
+ The row ``row`` is modified in place. The bumped-out entry
2417
+ is returned along with the bumped position.
2418
+
2419
+ EXAMPLES::
2420
+
2421
+ sage: from sage.combinat.rsk import RuleSuperRSK
2422
+ sage: from bisect import bisect_left, bisect_right
2423
+ sage: r = [1, 3, 3, 3, 4]
2424
+ sage: j = 2
2425
+ sage: j, y = RuleSuperRSK().reverse_insertion(j, r, epsilon=0); r
2426
+ [2, 3, 3, 3, 4]
2427
+ sage: j
2428
+ 1
2429
+ sage: y
2430
+ 0
2431
+ sage: r = [1, 3, 3, 3, 4]
2432
+ sage: j = 3
2433
+ sage: j, y = RuleSuperRSK().reverse_insertion(j, r, epsilon=0); r
2434
+ [3, 3, 3, 3, 4]
2435
+ sage: j
2436
+ 1
2437
+ sage: y
2438
+ 0
2439
+ sage: r = [1, 3, 3, 3, 4]
2440
+ sage: j = (3)
2441
+ sage: j, y = RuleSuperRSK().reverse_insertion(j, r, epsilon=1); r
2442
+ [1, 3, 3, 3, 4]
2443
+ sage: j
2444
+ 3
2445
+ sage: y
2446
+ 3
2447
+ """
2448
+ bisect = bisect_left if epsilon == 0 else bisect_right
2449
+ y_pos = bisect(row, x) - 1
2450
+ # switch x and y
2451
+ x, row[y_pos] = row[y_pos], x
2452
+ return x, y_pos
2453
+
2454
+ def _backward_format_output(self, lower_row, upper_row, output,
2455
+ q_is_standard):
2456
+ r"""
2457
+ Return the final output of the ``RSK_inverse`` correspondence
2458
+ from the output of the corresponding ``backward_rule``.
2459
+
2460
+ .. NOTE::
2461
+
2462
+ The default implementation of ``backward_rule`` lists
2463
+ bumped-out entries in the order in which the reverse
2464
+ bumping happens, which is *opposite* to the order of the
2465
+ final output.
2466
+
2467
+ EXAMPLES::
2468
+
2469
+ sage: from sage.combinat.rsk import RuleSuperRSK
2470
+ sage: from sage.combinat.shifted_primed_tableau import PrimedEntry
2471
+ sage: RuleSuperRSK()._backward_format_output([PrimedEntry('1p'),
2472
+ ....: PrimedEntry(1), PrimedEntry('3p'), PrimedEntry(9)],
2473
+ ....: [PrimedEntry(1), PrimedEntry('2p'), PrimedEntry('3p'),
2474
+ ....: PrimedEntry(4)], 'array', False)
2475
+ [[4, 3', 2', 1], [9, 3', 1, 1']]
2476
+ sage: RuleSuperRSK()._backward_format_output([PrimedEntry(1),
2477
+ ....: PrimedEntry('2p'), PrimedEntry('3p'), PrimedEntry(4)],
2478
+ ....: [PrimedEntry('1p'), PrimedEntry(1), PrimedEntry('2p'),
2479
+ ....: PrimedEntry(2)], 'word', True)
2480
+ word: 4,3',2',1
2481
+ sage: RuleSuperRSK()._backward_format_output([PrimedEntry(1),
2482
+ ....: PrimedEntry(2), PrimedEntry(3), PrimedEntry(4)],
2483
+ ....: [PrimedEntry('1p'), PrimedEntry(1), PrimedEntry('2p'),
2484
+ ....: PrimedEntry(2)], 'word', True)
2485
+ word: 4321
2486
+ """
2487
+ if output == 'array':
2488
+ return [list(reversed(upper_row)), list(reversed(lower_row))]
2489
+ if output == 'word':
2490
+ if q_is_standard:
2491
+ from sage.combinat.words.word import Word
2492
+ return Word(reversed(lower_row))
2493
+ else:
2494
+ raise TypeError("q must be standard to have a %s as "
2495
+ "valid output" % output)
2496
+ raise ValueError("invalid output option")
2497
+
2498
+
2499
+ class RuleStar(Rule):
2500
+ r"""
2501
+ Rule for `\star`-insertion.
2502
+
2503
+ The `\star`-insertion is similar to the classical RSK algorithm
2504
+ and is defined in [MPPS2020]_. The bottom row of the increasing
2505
+ Hecke biword is a word in the 0-Hecke monoid that is fully
2506
+ commutative. When inserting a letter `x` into a row `R`, there
2507
+ are three cases:
2508
+
2509
+ - Case 1: If `R` is empty or `x > \max(R)`, append `x` to row `R`
2510
+ and terminate.
2511
+
2512
+ - Case 2: Otherwise if `x` is not in `R`, locate the smallest `y` in `R`
2513
+ with `y > x`. Bump `y` with `x` and insert `y` into the next row.
2514
+
2515
+ - Case 3: Otherwise, if `x` is in `R`, locate the smallest `y` in `R` with
2516
+ `y \leq x` and interval `[y,x]` contained in `R`. Row `R` remains
2517
+ unchanged and `y` is to be inserted into the next row.
2518
+
2519
+ The `\star`-insertion returns a pair consisting a conjugate of a
2520
+ semistandard tableau and a semistandard tableau. It is a bijection from the
2521
+ collection of all increasing Hecke biwords whose bottom row is a fully
2522
+ commutative word to pairs (P, Q) of tableaux of the same shape such that
2523
+ P is conjugate semistandard, Q is semistandard and the row reading word of
2524
+ P is fully commutative [MPPS2020]_.
2525
+
2526
+ EXAMPLES:
2527
+
2528
+ As an example of `\star`-insertion, we reproduce Example 28 in [MPPS2020]_::
2529
+
2530
+ sage: # needs sage.groups
2531
+ sage: from sage.combinat.rsk import RuleStar
2532
+ sage: p,q = RuleStar().forward_rule([1,1,2,2,4,4], [1,3,2,4,2,4])
2533
+ sage: ascii_art(p, q)
2534
+ 1 2 4 1 1 2
2535
+ 1 4 2 4
2536
+ 3 4
2537
+ sage: line1,line2 = RuleStar().backward_rule(p, q)
2538
+ sage: line1,line2
2539
+ ([1, 1, 2, 2, 4, 4], [1, 3, 2, 4, 2, 4])
2540
+ sage: RSK_inverse(p, q, output='DecreasingHeckeFactorization', insertion='Star')
2541
+ (4, 2)()(4, 2)(3, 1)
2542
+
2543
+ sage: # needs sage.groups
2544
+ sage: from sage.combinat.crystals.fully_commutative_stable_grothendieck import DecreasingHeckeFactorization
2545
+ sage: h = DecreasingHeckeFactorization([[4, 2], [], [4, 2], [3, 1]])
2546
+ sage: RSK_inverse(*RSK(h,insertion='Star'),insertion='Star',
2547
+ ....: output='DecreasingHeckeFactorization')
2548
+ (4, 2)()(4, 2)(3, 1)
2549
+ sage: p,q = RSK(h, insertion='Star')
2550
+ sage: ascii_art(p, q)
2551
+ 1 2 4 1 1 2
2552
+ 1 4 2 4
2553
+ 3 4
2554
+ sage: RSK_inverse(p, q, insertion='Star')
2555
+ [[1, 1, 2, 2, 4, 4], [1, 3, 2, 4, 2, 4]]
2556
+ sage: f = RSK_inverse(p, q, output='DecreasingHeckeFactorization', insertion='Star')
2557
+ sage: f == h
2558
+ True
2559
+
2560
+ .. WARNING::
2561
+
2562
+ When ``output`` is set to ``'DecreasingHeckeFactorization'``, the
2563
+ inverse of `\star`-insertion of `(P,Q)` returns a decreasing
2564
+ factorization whose number of factors is the maximum entry of `Q`::
2565
+
2566
+ sage: # needs sage.groups
2567
+ sage: from sage.combinat.crystals.fully_commutative_stable_grothendieck import DecreasingHeckeFactorization
2568
+ sage: h1 = DecreasingHeckeFactorization([[],[3,1],[1]]); h1
2569
+ ()(3, 1)(1)
2570
+ sage: P,Q = RSK(h1, insertion='Star')
2571
+ sage: ascii_art(P, Q)
2572
+ 1 3 1 2
2573
+ 1 2
2574
+ sage: h2 = RSK_inverse(P, Q, insertion='Star',
2575
+ ....: output='DecreasingHeckeFactorization'); h2
2576
+ (3, 1)(1)
2577
+
2578
+ TESTS:
2579
+
2580
+ Check that :func:`RSK` is the inverse of :func:`RSK_inverse` for various
2581
+ outputs/inputs::
2582
+
2583
+ sage: from sage.combinat.partition import Partitions_n
2584
+ sage: shapes = [shape for n in range(7) for shape in Partitions_n(n)]
2585
+ sage: row_reading = lambda T: [x for row in reversed(T) for x in row]
2586
+ sage: from sage.monoids.hecke_monoid import HeckeMonoid
2587
+ sage: H = HeckeMonoid(SymmetricGroup(4+1))
2588
+ sage: from sage.combinat import permutation
2589
+ sage: reduce = lambda w: permutation.from_reduced_word(H.from_reduced_word(w).reduced_word())
2590
+ sage: fc = lambda w: not reduce(w).has_pattern([3,2,1])
2591
+ sage: FC_tabs = [T for shape in shapes
2592
+ ....: for T in SemistandardTableaux(shape, max_entry=4)
2593
+ ....: if fc(row_reading(T.conjugate()))]
2594
+ sage: Checks = []
2595
+ sage: for T in FC_tabs: # long time
2596
+ ....: shape = T.shape().conjugate()
2597
+ ....: P = T.conjugate()
2598
+ ....: Checks += [all((P,Q) == tuple(RSK(*RSK_inverse(P, Q,
2599
+ ....: insertion='Star', output='array'),
2600
+ ....: insertion='Star'))
2601
+ ....: for Q in SemistandardTableaux(shape, max_entry=5))]
2602
+ sage: all(Checks)
2603
+ True
2604
+ sage: Checks = []
2605
+ sage: for T in FC_tabs: # long time
2606
+ ....: shape = T.shape().conjugate()
2607
+ ....: P = T.conjugate()
2608
+ ....: Checks += [all((P,Q) == tuple(RSK(RSK_inverse(P, Q,
2609
+ ....: insertion='Star', output='DecreasingHeckeFactorization'),
2610
+ ....: insertion='Star'))
2611
+ ....: for Q in SemistandardTableaux(shape, max_entry=5))]
2612
+ sage: all(Checks)
2613
+ True
2614
+ sage: Checks = []
2615
+ sage: for T in FC_tabs:
2616
+ ....: shape = T.shape().conjugate()
2617
+ ....: P = T.conjugate()
2618
+ ....: for Q in StandardTableaux(shape, max_entry=5):
2619
+ ....: Checks += [(P,Q) == tuple(RSK(RSK_inverse(P, Q,
2620
+ ....: insertion='Star', output='word'),
2621
+ ....: insertion='Star'))]
2622
+ sage: all(Checks)
2623
+ True
2624
+
2625
+ Check that :func:`RSK_inverse` is the inverse of :func:`RSK` on arrays
2626
+ and words::
2627
+
2628
+ sage: S = SymmetricGroup(3+1)
2629
+ sage: from sage.combinat import permutation
2630
+ sage: FC = [x
2631
+ ....: for x in S
2632
+ ....: if (not permutation.from_reduced_word(
2633
+ ....: x.reduced_word()).has_pattern([3,2,1]) and
2634
+ ....: x.reduced_word())]
2635
+ sage: Triples = [(w, factors, ex)
2636
+ ....: for w in FC
2637
+ ....: for factors in range(2, 5+1)
2638
+ ....: for ex in range(4)]
2639
+ sage: Checks = []
2640
+ sage: for t in Triples:
2641
+ ....: B = crystals.FullyCommutativeStableGrothendieck(*t)
2642
+ ....: Checks += [all(b.to_increasing_hecke_biword() ==
2643
+ ....: RSK_inverse(*RSK(
2644
+ ....: *b.to_increasing_hecke_biword(),
2645
+ ....: insertion='Star'), insertion='Star')
2646
+ ....: for b in B)]
2647
+ sage: all(Checks)
2648
+ True
2649
+
2650
+ sage: from sage.monoids.hecke_monoid import HeckeMonoid
2651
+ sage: Checks = []
2652
+ sage: H = HeckeMonoid(SymmetricGroup(3+1))
2653
+ sage: reduce = lambda w: permutation.from_reduced_word(H.from_reduced_word(w).reduced_word())
2654
+ sage: fc = lambda w: not reduce(w).has_pattern([3,2,1])
2655
+ sage: words = [w for n in range(10) for w in Words(3, n) if fc(w)]
2656
+ sage: all([all(w == RSK_inverse(*RSK(w, insertion='Star'),
2657
+ ....: insertion='Star', output='word') for w in words)])
2658
+ True
2659
+ """
2660
+
2661
+ def forward_rule(self, obj1, obj2=None, check_braid=True):
2662
+ r"""
2663
+ Return a pair of tableaux obtained by applying forward insertion
2664
+ to the increasing Hecke biword ``[obj1, obj2]``.
2665
+
2666
+ INPUT:
2667
+
2668
+ - ``obj1``, ``obj2`` -- can be one of the following ways to represent a
2669
+ biword (or, equivalently, an increasing 0-Hecke factorization) that
2670
+ is fully commutative:
2671
+
2672
+ - two lists ``obj1`` and ``obj2`` of equal length, to be
2673
+ interpreted as the top row and the bottom row of the biword.
2674
+
2675
+ - a word ``obj1`` in an ordered alphabet, to be interpreted as
2676
+ the bottom row of the biword (in this case, ``obj2`` is ``None``;
2677
+ the top row of the biword is understood to be `(1,2,\ldots,n)`
2678
+ by default).
2679
+
2680
+ - a DecreasingHeckeFactorization ``obj1``, the whose increasing
2681
+ Hecke biword will be interpreted as the bottom row; the top row is
2682
+ understood to be the indices of the factors for each letter in
2683
+ this biword.
2684
+
2685
+ - ``check_braid`` -- boolean (default: ``True``); indicator to validate
2686
+ that input is associated to a fully commutative word in the 0-Hecke
2687
+ monoid, validation is performed if set to ``True``. Οtherwise, this
2688
+ validation is ignored.
2689
+
2690
+ EXAMPLES::
2691
+
2692
+ sage: # needs sage.groups
2693
+ sage: from sage.combinat.rsk import RuleStar
2694
+ sage: p,q = RuleStar().forward_rule([1,1,2,3,3], [2,3,3,1,3]); p,q
2695
+ ([[1, 3], [2, 3], [2]], [[1, 1], [2, 3], [3]])
2696
+ sage: p,q = RuleStar().forward_rule([2,3,3,1,3]); p,q
2697
+ ([[1, 3], [2, 3], [2]], [[1, 2], [3, 5], [4]])
2698
+ sage: p,q = RSK([1,1,2,3,3], [2,3,3,1,3], insertion=RSK.rules.Star); p,q
2699
+ ([[1, 3], [2, 3], [2]], [[1, 1], [2, 3], [3]])
2700
+
2701
+ sage: # needs sage.groups
2702
+ sage: from sage.combinat.crystals.fully_commutative_stable_grothendieck import DecreasingHeckeFactorization
2703
+ sage: h = DecreasingHeckeFactorization([[3, 1], [3], [3, 2]])
2704
+ sage: p,q = RSK(h, insertion=RSK.rules.Star); p,q
2705
+ ([[1, 3], [2, 3], [2]], [[1, 1], [2, 3], [3]])
2706
+
2707
+ TESTS:
2708
+
2709
+ Empty objects::
2710
+
2711
+ sage: # needs sage.groups
2712
+ sage: from sage.combinat.rsk import RuleStar
2713
+ sage: p,q = RuleStar().forward_rule([]); p,q
2714
+ ([], [])
2715
+
2716
+ sage: # needs sage.groups
2717
+ sage: from sage.combinat.crystals.fully_commutative_stable_grothendieck import DecreasingHeckeFactorization
2718
+ sage: h = DecreasingHeckeFactorization([[],[]])
2719
+ sage: p,q = RuleStar().forward_rule(h); p,q
2720
+ ([], [])
2721
+
2722
+ Invalid inputs::
2723
+
2724
+ sage: # needs sage.groups
2725
+ sage: p,q = RuleStar().forward_rule([1,1,2,3,3], [2,2,3,1,3])
2726
+ Traceback (most recent call last):
2727
+ ...
2728
+ ValueError: [1, 1, 2, 3, 3], [2, 2, 3, 1, 3] is not an increasing factorization
2729
+ sage: p,q = RuleStar().forward_rule([1,1,2,2,4,4], [1,3,2,4,1,3])
2730
+ Traceback (most recent call last):
2731
+ ...
2732
+ ValueError: the Star insertion is not defined for non-fully commutative words
2733
+ """
2734
+ if obj2 is None and obj1 is not None:
2735
+ from sage.combinat.crystals.fully_commutative_stable_grothendieck import DecreasingHeckeFactorization
2736
+ if not isinstance(obj1, DecreasingHeckeFactorization):
2737
+ obj2 = obj1
2738
+ obj1 = list(range(1, len(obj1)+1))
2739
+ else:
2740
+ h = obj1
2741
+ obj1 = sum([[h.factors-i]*len(h.value[i]) for i in reversed(range(h.factors))], [])
2742
+ obj2 = [i for f in h.value[::-1] for i in reversed(f)]
2743
+ if len(obj1) != len(obj2):
2744
+ raise ValueError(f"{obj1} and {obj2} have different number of elements")
2745
+ for i in range(len(obj1)-1):
2746
+ if obj1[i] > obj1[i+1] or (obj1[i] == obj1[i+1] and obj2[i] >= obj2[i+1]):
2747
+ raise ValueError(f"{obj1}, {obj2} is not an increasing factorization")
2748
+ if check_braid:
2749
+ N = max(obj2)+1 if obj2 else 1
2750
+ from sage.monoids.hecke_monoid import HeckeMonoid
2751
+ from sage.groups.perm_gps.permgroup_named import SymmetricGroup
2752
+ H = HeckeMonoid(SymmetricGroup(N))
2753
+ h = H.from_reduced_word(obj2)
2754
+ from sage.combinat import permutation
2755
+ p = permutation.from_reduced_word(h.reduced_word())
2756
+ if p.has_pattern([3, 2, 1]):
2757
+ raise ValueError("the Star insertion is not defined for non-fully commutative words")
2758
+
2759
+ p = [] # the "insertion" tableau
2760
+ q = [] # the "recording" tableau
2761
+ for i, j in zip(obj1, obj2):
2762
+ for r, qr in zip(p, q):
2763
+ j1 = self.insertion(j, r)
2764
+ if j1 is None:
2765
+ r.append(j)
2766
+ qr.append(i) # Values are always inserted to the right
2767
+ break
2768
+ else:
2769
+ j = j1
2770
+ else:
2771
+ # We made through all of the rows of p without breaking
2772
+ # so we need to add a new row to p and q.
2773
+ p.append([j])
2774
+ q.append([i])
2775
+ from sage.combinat.tableau import Tableau, SemistandardTableau
2776
+ p = Tableau(p)
2777
+ q = SemistandardTableau(q)
2778
+ return [p, q]
2779
+
2780
+ def backward_rule(self, p, q, output='array'):
2781
+ r"""
2782
+ Return the increasing Hecke biword obtained by applying reverse
2783
+ `\star`-insertion to a pair of tableaux ``(p, q)``.
2784
+
2785
+ INPUT:
2786
+
2787
+ - ``p``, ``q`` -- two tableaux of the same shape, where ``p`` is the
2788
+ conjugate of a semistandard tableau, whose reading word is fully
2789
+ commutative and ``q`` is a semistandard tableau.
2790
+
2791
+ - ``output`` -- (default: ``'array'``) if ``q`` is semi-standard:
2792
+
2793
+ - ``'array'`` -- as a two-line array (i.e. generalized permutation
2794
+ or biword) that is an increasing Hecke biword
2795
+ - ``'DecreasingHeckeFactorization'`` -- as a decreasing
2796
+ factorization in the 0-Hecke monoid
2797
+
2798
+ and if ``q`` is standard:
2799
+
2800
+ - ``'word'`` -- as a (possibly non-reduced) word in the 0-Hecke
2801
+ monoid
2802
+
2803
+ .. WARNING::
2804
+
2805
+ When output is 'DecreasingHeckeFactorization', the number of factors
2806
+ in the output is the largest number in ``obj1``.
2807
+
2808
+ EXAMPLES::
2809
+
2810
+ sage: # needs sage.groups
2811
+ sage: from sage.combinat.rsk import RuleStar
2812
+ sage: p,q = RuleStar().forward_rule([1,1,2,2,4,4], [1,3,2,4,2,4])
2813
+ sage: ascii_art(p, q)
2814
+ 1 2 4 1 1 2
2815
+ 1 4 2 4
2816
+ 3 4
2817
+ sage: line1,line2 = RuleStar().backward_rule(p, q); line1,line2
2818
+ ([1, 1, 2, 2, 4, 4], [1, 3, 2, 4, 2, 4])
2819
+ sage: RuleStar().backward_rule(p, q, output = 'DecreasingHeckeFactorization')
2820
+ (4, 2)()(4, 2)(3, 1)
2821
+
2822
+ TESTS:
2823
+
2824
+ Empty objects::
2825
+
2826
+ sage: # needs sage.groups
2827
+ sage: RuleStar().backward_rule(Tableau([]), Tableau([]))
2828
+ [[], []]
2829
+ sage: RuleStar().backward_rule(Tableau([]), Tableau([]), output='word')
2830
+ word:
2831
+ sage: RuleStar().backward_rule(Tableau([]), Tableau([]),output='DecreasingHeckeFactorization')
2832
+ ()
2833
+ """
2834
+ from sage.combinat.tableau import SemistandardTableaux
2835
+ if p.shape() != q.shape():
2836
+ raise ValueError("p(=%s) and q(=%s) must have the same shape" % (p, q))
2837
+ if q not in SemistandardTableaux():
2838
+ raise ValueError("q(=%s) must be a semistandard tableau" % q)
2839
+ if p.conjugate() not in SemistandardTableaux():
2840
+ raise ValueError("the conjugate of p(=%s) must be a semistandard tableau" % p.conjugate())
2841
+
2842
+ row_reading = [ele for row in reversed(p) for ele in row]
2843
+ N = max(row_reading + [0]) + 1
2844
+ from sage.monoids.hecke_monoid import HeckeMonoid
2845
+ from sage.groups.perm_gps.permgroup_named import SymmetricGroup
2846
+ H = HeckeMonoid(SymmetricGroup(N))
2847
+ h = H.from_reduced_word(row_reading)
2848
+ from sage.combinat import permutation
2849
+ w = permutation.from_reduced_word(h.reduced_word())
2850
+ if w.has_pattern([3, 2, 1]):
2851
+ raise ValueError(f"the row reading word of the insertion tableau {p} is not fully-commutative")
2852
+
2853
+ p_copy = p.to_list()
2854
+ line1 = []
2855
+ line2 = []
2856
+ d = {}
2857
+ for i, row in enumerate(q):
2858
+ for j, val in enumerate(row):
2859
+ if val in d:
2860
+ d[val][j] = i
2861
+ else:
2862
+ d[val] = {j: i}
2863
+ # d is now a double family such that for all integers k and j,
2864
+ # d[k][j]=i means that (i, j)-th cell of q is filled with k.
2865
+ for value, row_dict in sorted(d.items(), key=lambda x: -x[0]):
2866
+ for j in sorted(row_dict, reverse=True):
2867
+ i = row_dict[j]
2868
+ x = p_copy[i].pop() # Always the right-most entry
2869
+ while i > 0:
2870
+ i -= 1
2871
+ row = p_copy[i]
2872
+ x = self.reverse_insertion(x, row)
2873
+ line2.append(x)
2874
+ line1.append(value)
2875
+ return self._backward_format_output(line1[::-1], line2[::-1], output)
2876
+
2877
+ def insertion(self, b, r):
2878
+ r"""
2879
+ Insert the letter ``b`` from the second row of the biword into the row
2880
+ ``r`` using `\star`-insertion defined in [MPPS2020]_.
2881
+
2882
+ The row `r` is modified in place if bumping occurs and `b` is not in
2883
+ row `r`. The bumped-out entry, if it exists, is returned.
2884
+
2885
+ EXAMPLES::
2886
+
2887
+ sage: from sage.combinat.rsk import RuleStar
2888
+ sage: RuleStar().insertion(3, [1,2,4,5])
2889
+ 4
2890
+ sage: RuleStar().insertion(3, [1,2,3,5])
2891
+ 1
2892
+ sage: RuleStar().insertion(6, [1,2,3,5]) is None
2893
+ True
2894
+ """
2895
+ if r[-1] < b:
2896
+ return None # append b to the end of row r
2897
+ if b in r:
2898
+ k = b
2899
+ while k in r:
2900
+ k -= 1
2901
+ k += 1
2902
+ else:
2903
+ y_pos = bisect_right(r, b)
2904
+ k = r[y_pos]
2905
+ r[y_pos] = b
2906
+ return k
2907
+
2908
+ def reverse_insertion(self, x, r):
2909
+ """
2910
+ Reverse bump the row ``r`` of the current insertion tableau ``p``
2911
+ with number ``x``, provided that ``r`` is the ``i``-th row of ``p``.
2912
+
2913
+ The row ``r`` is modified in place. The bumped-out entry is returned.
2914
+
2915
+ EXAMPLES::
2916
+
2917
+ sage: from sage.combinat.rsk import RuleStar
2918
+ sage: RuleStar().reverse_insertion(4, [1,2,3,5])
2919
+ 3
2920
+ sage: RuleStar().reverse_insertion(1, [1,2,3,5])
2921
+ 3
2922
+ sage: RuleStar().reverse_insertion(5, [1,2,3,5])
2923
+ 5
2924
+ """
2925
+ if x in r:
2926
+ y = x
2927
+ while y in r:
2928
+ y += 1
2929
+ y -= 1
2930
+ else:
2931
+ y_pos = bisect_left(r, x) - 1
2932
+ y = r[y_pos]
2933
+ r[y_pos] = x
2934
+ x = y
2935
+ return x
2936
+
2937
+ def _backward_format_output(self, obj1, obj2, output):
2938
+ r"""
2939
+ Return the final output of the ``RSK_inverse`` correspondence from
2940
+ the output of the corresponding ``backward_rule``.
2941
+
2942
+ EXAMPLES::
2943
+
2944
+ sage: # needs sage.groups
2945
+ sage: from sage.combinat.rsk import RuleStar
2946
+ sage: RuleStar()._backward_format_output([1, 1, 2, 2, 4, 4], [1, 3, 2, 4, 2, 4], 'array')
2947
+ [[1, 1, 2, 2, 4, 4], [1, 3, 2, 4, 2, 4]]
2948
+ sage: RuleStar()._backward_format_output([1, 1, 2, 2, 4, 4], [1, 3, 2, 4, 2, 4], 'DecreasingHeckeFactorization')
2949
+ (4, 2)()(4, 2)(3, 1)
2950
+ sage: RuleStar()._backward_format_output([1, 2, 3, 4, 5, 6], [4, 2, 4, 2, 3, 1], 'word')
2951
+ word: 424231
2952
+ """
2953
+ if len(obj1) != len(obj2):
2954
+ raise ValueError(f"{obj1} and {obj2} are of different lengths")
2955
+ if output == 'array':
2956
+ return [obj1, obj2]
2957
+ elif output == 'word':
2958
+ if obj1 == list(range(1, len(obj1)+1)):
2959
+ from sage.combinat.words.word import Word
2960
+ return Word(obj2)
2961
+ else:
2962
+ raise TypeError("upper row must be standard")
2963
+ elif output == 'DecreasingHeckeFactorization':
2964
+ from sage.combinat.crystals.fully_commutative_stable_grothendieck import DecreasingHeckeFactorization
2965
+ obj1.reverse()
2966
+ obj2.reverse()
2967
+ df = []
2968
+ for j in range(len(obj1)):
2969
+ if j == 0:
2970
+ df.append([])
2971
+ if j > 0 and obj1[j] < obj1[j-1]:
2972
+ df.extend([] for _ in range(obj1[j-1]-obj1[j]))
2973
+ df[-1].append(obj2[j])
2974
+ if obj1:
2975
+ df.extend([] for a in range(obj1[-1]-1))
2976
+ # If biword is empty, return a decreasing factorization with 1 factor
2977
+ else:
2978
+ df.append([])
2979
+ return DecreasingHeckeFactorization(df)
2980
+
2981
+
2982
+ class InsertionRules:
2983
+ r"""
2984
+ Catalog of rules for RSK-like insertion algorithms.
2985
+ """
2986
+ RSK = RuleRSK
2987
+ EG = RuleEG
2988
+ Hecke = RuleHecke
2989
+ dualRSK = RuleDualRSK
2990
+ coRSK = RuleCoRSK
2991
+ superRSK = RuleSuperRSK
2992
+ Star = RuleStar
2993
+
2994
+ #####################################################################
2995
+
2996
+
2997
+ def RSK(obj1=None, obj2=None, insertion=InsertionRules.RSK, check_standard=False, **options):
2998
+ r"""
2999
+ Perform the Robinson-Schensted-Knuth (RSK) correspondence.
3000
+
3001
+ The Robinson-Schensted-Knuth (RSK) correspondence (also known
3002
+ as the RSK algorithm) is most naturally stated as a bijection
3003
+ between generalized permutations (also known as two-line arrays,
3004
+ biwords, ...) and pairs of semi-standard Young tableaux `(P, Q)`
3005
+ of identical shape. The tableau `P` is known as the insertion
3006
+ tableau, and `Q` is known as the recording tableau.
3007
+
3008
+ The basic operation is known as row insertion `P \leftarrow k`
3009
+ (where `P` is a given semi-standard Young tableau, and `k` is an
3010
+ integer). Row insertion is a recursive algorithm which starts by
3011
+ setting `k_0 = k`, and in its `i`-th step inserts the number `k_i`
3012
+ into the `i`-th row of `P` (we start counting the rows at `0`) by
3013
+ replacing the first integer greater than `k_i` in the row by `k_i`
3014
+ and defines `k_{i+1}` as the integer that has been replaced. If no
3015
+ integer greater than `k_i` exists in the `i`-th row, then `k_i` is
3016
+ simply appended to the row and the algorithm terminates at this point.
3017
+
3018
+ A *generalized permutation* (or *biword*) is a list
3019
+ `((j_0, k_0), (j_1, k_1), \ldots, (j_{\ell-1}, k_{\ell-1}))`
3020
+ of pairs such that the letters `j_0, j_1, \ldots, j_{\ell-1}`
3021
+ are weakly increasing (that is,
3022
+ `j_0 \leq j_1 \leq \cdots \leq j_{\ell-1}`), whereas the letters
3023
+ `k_i` satisfy `k_i \leq k_{i+1}` whenever `j_i = j_{i+1}`.
3024
+ The `\ell`-tuple `(j_0, j_1, \ldots, j_{\ell-1})` is called the
3025
+ *top line* of this generalized permutation,
3026
+ whereas the `\ell`-tuple `(k_0, k_1, \ldots, k_{\ell-1})` is
3027
+ called its *bottom line*.
3028
+
3029
+ Now the RSK algorithm, applied to a generalized permutation
3030
+ `p = ((j_0, k_0), (j_1, k_1), \ldots, (j_{\ell-1}, k_{\ell-1}))`
3031
+ (encoded as a lexicographically sorted list of pairs) starts by
3032
+ initializing two semi-standard tableaux `P_0` and `Q_0` as empty
3033
+ tableaux. For each nonnegative integer `t` starting at `0`, take
3034
+ the pair `(j_t, k_t)` from `p` and set
3035
+ `P_{t+1} = P_t \leftarrow k_t`, and define `Q_{t+1}` by adding a
3036
+ new box filled with `j_t` to the tableau `Q_t` at the same
3037
+ location the row insertion on `P_t` ended (that is to say, adding
3038
+ a new box with entry `j_t` such that `P_{t+1}` and `Q_{t+1}` have
3039
+ the same shape). The iterative process stops when `t` reaches the
3040
+ size of `p`, and the pair `(P_t, Q_t)` at this point is the image
3041
+ of `p` under the Robinson-Schensted-Knuth correspondence.
3042
+
3043
+ This correspondence has been introduced in [Knu1970]_, where it has
3044
+ been referred to as "Construction A".
3045
+
3046
+ For more information, see Chapter 7 in [Sta-EC2]_.
3047
+
3048
+ We also note that integer matrices are in bijection with generalized
3049
+ permutations. Furthermore, we can convert any word `w` (and, in
3050
+ particular, any permutation) to a generalized permutation by
3051
+ considering the top row to be `(1, 2, \ldots, n)` where `n` is the
3052
+ length of `w`.
3053
+
3054
+ The optional argument ``insertion`` allows to specify an alternative
3055
+ insertion procedure to be used instead of the standard
3056
+ Robinson-Schensted-Knuth insertion.
3057
+
3058
+ INPUT:
3059
+
3060
+ - ``obj1``, ``obj2`` -- can be one of the following:
3061
+
3062
+ - a word in an ordered alphabet (in this case, ``obj1`` is said
3063
+ word, and ``obj2`` is ``None``)
3064
+ - an integer matrix
3065
+ - two lists of equal length representing a generalized permutation
3066
+ (namely, the lists `(j_0, j_1, \ldots, j_{\ell-1})` and
3067
+ `(k_0, k_1, \ldots, k_{\ell-1})` represent the generalized
3068
+ permutation
3069
+ `((j_0, k_0), (j_1, k_1), \ldots, (j_{\ell-1}, k_{\ell-1}))`)
3070
+ - any object which has a method ``_rsk_iter()`` which returns an
3071
+ iterator over the object represented as generalized permutation or
3072
+ a pair of lists (in this case, ``obj1`` is said object,
3073
+ and ``obj2`` is ``None``).
3074
+
3075
+ - ``insertion`` -- (default: ``RSK.rules.RSK``) the following types
3076
+ of insertion are currently supported:
3077
+
3078
+ - ``RSK.rules.RSK`` (or ``'RSK'``) -- Robinson-Schensted-Knuth
3079
+ insertion (:class:`~sage.combinat.rsk.RuleRSK`)
3080
+ - ``RSK.rules.EG`` (or ``'EG'``) -- Edelman-Greene insertion
3081
+ (only for reduced words of permutations/elements of a type `A`
3082
+ Coxeter group) (:class:`~sage.combinat.rsk.RuleEG`)
3083
+ - ``RSK.rules.Hecke`` -- (or ``'hecke'``) Hecke insertion (only
3084
+ guaranteed for generalized permutations whose top row is strictly
3085
+ increasing) (:class:`~sage.combinat.rsk.RuleHecke`)
3086
+ - ``RSK.rules.dualRSK`` (or ``'dualRSK'``) -- Dual RSK insertion
3087
+ (only for strict biwords) (:class:`~sage.combinat.rsk.RuleDualRSK`)
3088
+ - ``RSK.rules.coRSK`` (or ``'coRSK'``) -- CoRSK insertion (only
3089
+ for strict cobiwords) (:class:`~sage.combinat.rsk.RuleCoRSK`)
3090
+ - ``RSK.rules.superRSK`` (or ``'super'``) -- Super RSK insertion (only for
3091
+ restricted super biwords) (:class:`~sage.combinat.rsk.RuleSuperRSK`)
3092
+ - ``RSK.rules.Star`` (or ``'Star'``) -- `\star`-insertion (only for
3093
+ fully commutative words in the 0-Hecke monoid)
3094
+ (:class:`~sage.combinat.rsk.RuleStar`)
3095
+
3096
+ - ``check_standard`` -- boolean (default: ``False``); check if either of
3097
+ the resulting tableaux is a standard tableau, and if so, typecast it
3098
+ as such
3099
+
3100
+ For precise information about constraints on the input and output,
3101
+ as well as the definition of the algorithm (if it is not standard
3102
+ RSK), see the particular :class:`~sage.combinat.rsk.Rule` class.
3103
+
3104
+ EXAMPLES:
3105
+
3106
+ If we only input one row, it is understood that the top row
3107
+ should be `(1, 2, \ldots, n)`::
3108
+
3109
+ sage: RSK([3,3,2,4,1])
3110
+ [[[1, 3, 4], [2], [3]], [[1, 2, 4], [3], [5]]]
3111
+ sage: RSK(Word([3,3,2,4,1]))
3112
+ [[[1, 3, 4], [2], [3]], [[1, 2, 4], [3], [5]]]
3113
+ sage: RSK(Word([2,3,3,2,1,3,2,3]))
3114
+ [[[1, 2, 2, 3, 3], [2, 3], [3]], [[1, 2, 3, 6, 8], [4, 7], [5]]]
3115
+
3116
+ We can provide a generalized permutation::
3117
+
3118
+ sage: RSK([1, 2, 2, 2], [2, 1, 1, 2])
3119
+ [[[1, 1, 2], [2]], [[1, 2, 2], [2]]]
3120
+ sage: RSK(Word([1,1,3,4,4]), [1,4,2,1,3])
3121
+ [[[1, 1, 3], [2], [4]], [[1, 1, 4], [3], [4]]]
3122
+ sage: RSK([1,3,3,4,4], Word([6,2,2,1,7]))
3123
+ [[[1, 2, 7], [2], [6]], [[1, 3, 4], [3], [4]]]
3124
+
3125
+ We can provide a matrix::
3126
+
3127
+ sage: RSK(matrix([[0,1],[2,1]]))
3128
+ [[[1, 1, 2], [2]], [[1, 2, 2], [2]]]
3129
+
3130
+ We can also provide something looking like a matrix::
3131
+
3132
+ sage: RSK([[0,1],[2,1]])
3133
+ [[[1, 1, 2], [2]], [[1, 2, 2], [2]]]
3134
+
3135
+ There is also :func:`~sage.combinat.rsk.RSK_inverse` which performs
3136
+ the inverse of the bijection on a pair of semistandard tableaux. We
3137
+ note that the inverse function takes 2 separate tableaux as inputs, so
3138
+ to compose with :func:`~sage.combinat.rsk.RSK`, we need to use the
3139
+ python ``*`` on the output::
3140
+
3141
+ sage: RSK_inverse(*RSK([1, 2, 2, 2], [2, 1, 1, 2]))
3142
+ [[1, 2, 2, 2], [2, 1, 1, 2]]
3143
+ sage: P,Q = RSK([1, 2, 2, 2], [2, 1, 1, 2])
3144
+ sage: RSK_inverse(P, Q)
3145
+ [[1, 2, 2, 2], [2, 1, 1, 2]]
3146
+
3147
+ TESTS:
3148
+
3149
+ Empty objects::
3150
+
3151
+ sage: RSK(Permutation([]))
3152
+ [[], []]
3153
+ sage: RSK(Word([]))
3154
+ [[], []]
3155
+ sage: RSK(matrix([[]]))
3156
+ [[], []]
3157
+ sage: RSK([], [])
3158
+ [[], []]
3159
+ sage: RSK([[]])
3160
+ [[], []]
3161
+ sage: RSK(Word([]), insertion=RSK.rules.EG)
3162
+ [[], []]
3163
+ sage: RSK(Word([]), insertion=RSK.rules.Hecke)
3164
+ [[], []]
3165
+ """
3166
+ if isinstance(insertion, str):
3167
+ if insertion == 'RSK':
3168
+ insertion = RSK.rules.RSK
3169
+ elif insertion == 'EG':
3170
+ insertion = RSK.rules.EG
3171
+ elif insertion == 'hecke':
3172
+ insertion = RSK.rules.Hecke
3173
+ elif insertion == 'dualRSK':
3174
+ insertion = RSK.rules.dualRSK
3175
+ elif insertion == 'coRSK':
3176
+ insertion = RSK.rules.coRSK
3177
+ elif insertion == 'superRSK':
3178
+ insertion = RSK.rules.superRSK
3179
+ elif insertion == 'Star':
3180
+ insertion = RSK.rules.Star
3181
+ else:
3182
+ raise ValueError("invalid input")
3183
+
3184
+ rule = insertion()
3185
+ if not isinstance(rule, Rule):
3186
+ raise TypeError("the insertion must be an instance of Rule")
3187
+
3188
+ if obj1 is None and obj2 is None:
3189
+ if 'matrix' in options:
3190
+ obj1 = matrix(options['matrix'])
3191
+ else:
3192
+ raise ValueError("invalid input")
3193
+
3194
+ if isinstance(obj1, Matrix):
3195
+ obj1 = obj1.rows()
3196
+
3197
+ output = rule.forward_rule(obj1, obj2, check_standard)
3198
+ return output
3199
+
3200
+
3201
+ robinson_schensted_knuth = RSK
3202
+ RSK.rules = InsertionRules
3203
+
3204
+
3205
+ def RSK_inverse(p, q, output='array', insertion=InsertionRules.RSK):
3206
+ r"""
3207
+ Return the generalized permutation corresponding to the pair of
3208
+ tableaux `(p, q)` under the inverse of the Robinson-Schensted-Knuth
3209
+ correspondence.
3210
+
3211
+ For more information on the bijection, see :func:`RSK`.
3212
+
3213
+ INPUT:
3214
+
3215
+ - ``p``, ``q`` -- two semi-standard tableaux of the same shape, or
3216
+ (in the case when Hecke insertion is used) an increasing tableau and
3217
+ a set-valued tableau of the same shape (see the note below for the
3218
+ format of the set-valued tableau)
3219
+
3220
+ - ``output`` -- (default: ``'array'``) if ``q`` is semi-standard:
3221
+
3222
+ - ``'array'`` -- as a two-line array (i.e. generalized permutation or
3223
+ biword)
3224
+ - ``'matrix'`` -- as an integer matrix
3225
+
3226
+ and if ``q`` is standard, we can also have the output:
3227
+
3228
+ - ``'word'`` -- as a word
3229
+
3230
+ and additionally if ``p`` is standard, we can also have the output:
3231
+
3232
+ - ``'permutation'`` -- as a permutation
3233
+
3234
+ - ``insertion`` -- (default: ``RSK.rules.RSK``) the insertion algorithm
3235
+ used in the bijection. Currently the following are supported:
3236
+
3237
+ - ``RSK.rules.RSK`` (or ``'RSK'``) -- Robinson-Schensted-Knuth
3238
+ insertion (:class:`~sage.combinat.rsk.RuleRSK`)
3239
+ - ``RSK.rules.EG`` (or ``'EG'``) -- Edelman-Greene insertion
3240
+ (only for reduced words of permutations/elements of a type `A`
3241
+ Coxeter group) (:class:`~sage.combinat.rsk.RuleEG`)
3242
+ - ``RSK.rules.Hecke`` (or ``'hecke'``) -- Hecke insertion (only
3243
+ guaranteed for generalized permutations whose top row is strictly
3244
+ increasing) (:class:`~sage.combinat.rsk.RuleHecke`)
3245
+ - ``RSK.rules.dualRSK`` (or ``'dualRSK'``) -- Dual RSK insertion
3246
+ (only for strict biwords) (:class:`~sage.combinat.rsk.RuleDualRSK`)
3247
+ - ``RSK.rules.coRSK`` (or ``'coRSK'``) -- CoRSK insertion (only
3248
+ for strict cobiwords) (:class:`~sage.combinat.rsk.RuleCoRSK`)
3249
+ - ``RSK.rules.superRSK`` (or ``'super'``) -- Super RSK insertion (only for
3250
+ restricted super biwords) (:class:`~sage.combinat.rsk.RuleSuperRSK`)
3251
+ - ``RSK.rules.Star`` (or ``'Star'``) -- `\star`-insertion (only for
3252
+ fully commutative words in the 0-Hecke monoid)
3253
+ (:class:`~sage.combinat.rsk.RuleStar`)
3254
+
3255
+ For precise information about constraints on the input and
3256
+ output, see the particular :class:`~sage.combinat.rsk.Rule` class.
3257
+
3258
+ .. NOTE::
3259
+
3260
+ In the case of Hecke insertion, the input variable ``q`` should
3261
+ be a set-valued tableau, encoded as a tableau whose entries are
3262
+ strictly increasing tuples of positive integers. Each such tuple
3263
+ encodes the set of its entries.
3264
+
3265
+ EXAMPLES:
3266
+
3267
+ If both ``p`` and ``q`` are standard::
3268
+
3269
+ sage: t1 = Tableau([[1, 2, 5], [3], [4]])
3270
+ sage: t2 = Tableau([[1, 2, 3], [4], [5]])
3271
+ sage: RSK_inverse(t1, t2)
3272
+ [[1, 2, 3, 4, 5], [1, 4, 5, 3, 2]]
3273
+ sage: RSK_inverse(t1, t2, 'word')
3274
+ word: 14532
3275
+ sage: RSK_inverse(t1, t2, 'matrix')
3276
+ [1 0 0 0 0]
3277
+ [0 0 0 1 0]
3278
+ [0 0 0 0 1]
3279
+ [0 0 1 0 0]
3280
+ [0 1 0 0 0]
3281
+ sage: RSK_inverse(t1, t2, 'permutation')
3282
+ [1, 4, 5, 3, 2]
3283
+ sage: RSK_inverse(t1, t1, 'permutation')
3284
+ [1, 4, 3, 2, 5]
3285
+ sage: RSK_inverse(t2, t2, 'permutation')
3286
+ [1, 2, 5, 4, 3]
3287
+ sage: RSK_inverse(t2, t1, 'permutation')
3288
+ [1, 5, 4, 2, 3]
3289
+
3290
+ If the first tableau is semistandard::
3291
+
3292
+ sage: p = Tableau([[1,2,2],[3]]); q = Tableau([[1,2,4],[3]])
3293
+ sage: ret = RSK_inverse(p, q); ret
3294
+ [[1, 2, 3, 4], [1, 3, 2, 2]]
3295
+ sage: RSK_inverse(p, q, 'word')
3296
+ word: 1322
3297
+
3298
+ In general::
3299
+
3300
+ sage: p = Tableau([[1,2,2],[2]]); q = Tableau([[1,3,3],[2]])
3301
+ sage: RSK_inverse(p, q)
3302
+ [[1, 2, 3, 3], [2, 1, 2, 2]]
3303
+ sage: RSK_inverse(p, q, 'matrix')
3304
+ [0 1]
3305
+ [1 0]
3306
+ [0 2]
3307
+
3308
+ Using Hecke insertion::
3309
+
3310
+ sage: w = [5, 4, 3, 1, 4, 2, 5, 5]
3311
+ sage: pq = RSK(w, insertion=RSK.rules.Hecke)
3312
+ sage: RSK_inverse(*pq, insertion=RSK.rules.Hecke, output='list')
3313
+ [5, 4, 3, 1, 4, 2, 5, 5]
3314
+
3315
+ .. NOTE::
3316
+
3317
+ The constructor of ``Tableau`` accepts not only semistandard
3318
+ tableaux, but also arbitrary lists that are fillings of a
3319
+ partition diagram. (And such lists are used, e.g., for the
3320
+ set-valued tableau ``q`` that is passed to
3321
+ ``RSK_inverse(p, q, insertion='hecke')``.)
3322
+ The user is responsible for ensuring that the tableaux passed to
3323
+ ``RSK_inverse`` are of the right types (semistandard, standard,
3324
+ increasing, set-valued as needed).
3325
+
3326
+ TESTS:
3327
+
3328
+ From empty tableaux::
3329
+
3330
+ sage: RSK_inverse(Tableau([]), Tableau([]))
3331
+ [[], []]
3332
+
3333
+ Check that :func:`RSK_inverse` is the inverse of :func:`RSK` on the
3334
+ different types of inputs/outputs::
3335
+
3336
+ sage: f = lambda p: RSK_inverse(*RSK(p), output='permutation')
3337
+ sage: all(p == f(p) for n in range(7) for p in Permutations(n))
3338
+ True
3339
+ sage: all(RSK_inverse(*RSK(w), output='word') == w for n in range(4)
3340
+ ....: for w in Words(5, n))
3341
+ True
3342
+ sage: from sage.combinat.integer_matrices import IntegerMatrices
3343
+ sage: M = IntegerMatrices([1,2,2,1], [3,1,1,1])
3344
+ sage: all(RSK_inverse(*RSK(m), output='matrix') == m for m in M)
3345
+ True
3346
+
3347
+ sage: n = ZZ.random_element(200)
3348
+ sage: p = Permutations(n).random_element()
3349
+ sage: is_fine = True if p == f(p) else p ; is_fine
3350
+ True
3351
+
3352
+ Both tableaux must be of the same shape::
3353
+
3354
+ sage: RSK_inverse(Tableau([[1,2,3]]), Tableau([[1,2]]))
3355
+ Traceback (most recent call last):
3356
+ ...
3357
+ ValueError: p(=[[1, 2, 3]]) and q(=[[1, 2]]) must have the same shape
3358
+
3359
+ Check that :issue:`20430` is fixed::
3360
+
3361
+ sage: RSK([1,1,1,1,1,1,1,2,2,2,3], [1,1,1,1,1,1,3,2,2,2,1])
3362
+ [[[1, 1, 1, 1, 1, 1, 1, 2, 2], [2], [3]],
3363
+ [[1, 1, 1, 1, 1, 1, 1, 2, 2], [2], [3]]]
3364
+ sage: t = SemistandardTableau([[1, 1, 1, 1, 1, 1, 1, 2, 2], [2], [3]])
3365
+ sage: RSK_inverse(t, t, 'array')
3366
+ [[1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3],
3367
+ [1, 1, 1, 1, 1, 1, 3, 2, 2, 2, 1]]
3368
+ """
3369
+ if isinstance(insertion, str):
3370
+ if insertion == 'RSK':
3371
+ insertion = RSK.rules.RSK
3372
+ elif insertion == 'EG':
3373
+ insertion = RSK.rules.EG
3374
+ elif insertion == 'hecke':
3375
+ insertion = RSK.rules.Hecke
3376
+ elif insertion == 'dualRSK':
3377
+ insertion = RSK.rules.dualRSK
3378
+ elif insertion == 'coRSK':
3379
+ insertion = RSK.rules.coRSK
3380
+ elif insertion == 'superRSK':
3381
+ insertion = RSK.rules.superRSK
3382
+ elif insertion == 'Star':
3383
+ insertion = RSK.rules.Star
3384
+ else:
3385
+ raise ValueError("invalid input")
3386
+
3387
+ rule = insertion()
3388
+ if not isinstance(rule, Rule):
3389
+ raise TypeError("the insertion must be an instance of Rule")
3390
+
3391
+ if p.shape() != q.shape():
3392
+ raise ValueError(f"p(={p}) and q(={q}) must have the same shape")
3393
+
3394
+ answer = rule.backward_rule(p, q, output)
3395
+ return answer
3396
+
3397
+
3398
+ robinson_schensted_knuth_inverse = RSK_inverse
3399
+
3400
+
3401
+ def to_matrix(t, b):
3402
+ r"""
3403
+ Return the integer matrix corresponding to a two-line array.
3404
+
3405
+ INPUT:
3406
+
3407
+ - ``t`` -- the top row of the array
3408
+ - ``b`` -- the bottom row of the array
3409
+
3410
+ OUTPUT:
3411
+
3412
+ An `m \times n`-matrix (where `m` and `n` are the maximum entries in
3413
+ `t` and `b` respectively) whose `(i, j)`-th entry, for any `i` and `j`,
3414
+ is the number of all positions `k` satisfying `t_k = i` and `b_k = j`.
3415
+
3416
+ EXAMPLES::
3417
+
3418
+ sage: from sage.combinat.rsk import to_matrix
3419
+ sage: to_matrix([1, 1, 3, 3, 4], [2, 3, 1, 1, 3])
3420
+ [0 1 1]
3421
+ [0 0 0]
3422
+ [2 0 0]
3423
+ [0 0 1]
3424
+ """
3425
+ n = len(b)
3426
+ if len(t) != n:
3427
+ raise ValueError("the two arrays must be the same length")
3428
+
3429
+ # Build the dictionary of entries since the matrix
3430
+ # is typically (very) sparse
3431
+ entries = {}
3432
+ for i in range(n):
3433
+ pos = (t[i]-1, b[i]-1)
3434
+ if pos in entries:
3435
+ entries[pos] += 1
3436
+ else:
3437
+ entries[pos] = 1
3438
+ return matrix(entries, sparse=True)