passagemath-combinat 10.6.42__cp314-cp314-musllinux_1_2_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (400) hide show
  1. passagemath_combinat/__init__.py +3 -0
  2. passagemath_combinat-10.6.42.dist-info/METADATA +160 -0
  3. passagemath_combinat-10.6.42.dist-info/RECORD +400 -0
  4. passagemath_combinat-10.6.42.dist-info/WHEEL +5 -0
  5. passagemath_combinat-10.6.42.dist-info/top_level.txt +3 -0
  6. passagemath_combinat.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
  7. passagemath_combinat.libs/libsymmetrica-81fe8739.so.3.0.0 +0 -0
  8. sage/algebras/affine_nil_temperley_lieb.py +263 -0
  9. sage/algebras/all.py +24 -0
  10. sage/algebras/all__sagemath_combinat.py +35 -0
  11. sage/algebras/askey_wilson.py +935 -0
  12. sage/algebras/associated_graded.py +345 -0
  13. sage/algebras/cellular_basis.py +350 -0
  14. sage/algebras/cluster_algebra.py +2766 -0
  15. sage/algebras/down_up_algebra.py +860 -0
  16. sage/algebras/free_algebra.py +1698 -0
  17. sage/algebras/free_algebra_element.py +345 -0
  18. sage/algebras/free_algebra_quotient.py +405 -0
  19. sage/algebras/free_algebra_quotient_element.py +295 -0
  20. sage/algebras/free_zinbiel_algebra.py +885 -0
  21. sage/algebras/hall_algebra.py +783 -0
  22. sage/algebras/hecke_algebras/all.py +4 -0
  23. sage/algebras/hecke_algebras/ariki_koike_algebra.py +1796 -0
  24. sage/algebras/hecke_algebras/ariki_koike_specht_modules.py +475 -0
  25. sage/algebras/hecke_algebras/cubic_hecke_algebra.py +3520 -0
  26. sage/algebras/hecke_algebras/cubic_hecke_base_ring.py +1473 -0
  27. sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py +1079 -0
  28. sage/algebras/iwahori_hecke_algebra.py +3095 -0
  29. sage/algebras/jordan_algebra.py +1773 -0
  30. sage/algebras/lie_conformal_algebras/abelian_lie_conformal_algebra.py +113 -0
  31. sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py +156 -0
  32. sage/algebras/lie_conformal_algebras/all.py +18 -0
  33. sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra.py +134 -0
  34. sage/algebras/lie_conformal_algebras/examples.py +43 -0
  35. sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra.py +131 -0
  36. sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py +139 -0
  37. sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py +174 -0
  38. sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py +167 -0
  39. sage/algebras/lie_conformal_algebras/freely_generated_lie_conformal_algebra.py +107 -0
  40. sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py +135 -0
  41. sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py +353 -0
  42. sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +236 -0
  43. sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py +78 -0
  44. sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py +328 -0
  45. sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra.py +117 -0
  46. sage/algebras/lie_conformal_algebras/neveu_schwarz_lie_conformal_algebra.py +86 -0
  47. sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra.py +82 -0
  48. sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra.py +205 -0
  49. sage/algebras/nil_coxeter_algebra.py +191 -0
  50. sage/algebras/q_commuting_polynomials.py +673 -0
  51. sage/algebras/q_system.py +608 -0
  52. sage/algebras/quantum_clifford.py +959 -0
  53. sage/algebras/quantum_groups/ace_quantum_onsager.py +693 -0
  54. sage/algebras/quantum_groups/all.py +9 -0
  55. sage/algebras/quantum_groups/fock_space.py +2219 -0
  56. sage/algebras/quantum_groups/q_numbers.py +207 -0
  57. sage/algebras/quantum_groups/quantum_group_gap.py +2695 -0
  58. sage/algebras/quantum_groups/representations.py +591 -0
  59. sage/algebras/quantum_matrix_coordinate_algebra.py +1006 -0
  60. sage/algebras/quantum_oscillator.py +623 -0
  61. sage/algebras/quaternion_algebra.py +20 -0
  62. sage/algebras/quaternion_algebra_element.py +55 -0
  63. sage/algebras/rational_cherednik_algebra.py +525 -0
  64. sage/algebras/schur_algebra.py +670 -0
  65. sage/algebras/shuffle_algebra.py +1011 -0
  66. sage/algebras/splitting_algebra.py +779 -0
  67. sage/algebras/tensor_algebra.py +709 -0
  68. sage/algebras/yangian.py +1082 -0
  69. sage/algebras/yokonuma_hecke_algebra.py +1018 -0
  70. sage/all__sagemath_combinat.py +35 -0
  71. sage/combinat/SJT.py +255 -0
  72. sage/combinat/affine_permutation.py +2405 -0
  73. sage/combinat/algebraic_combinatorics.py +55 -0
  74. sage/combinat/all.py +53 -0
  75. sage/combinat/all__sagemath_combinat.py +195 -0
  76. sage/combinat/alternating_sign_matrix.py +2063 -0
  77. sage/combinat/baxter_permutations.py +346 -0
  78. sage/combinat/bijectionist.py +3220 -0
  79. sage/combinat/binary_recurrence_sequences.py +1180 -0
  80. sage/combinat/blob_algebra.py +685 -0
  81. sage/combinat/catalog_partitions.py +27 -0
  82. sage/combinat/chas/all.py +23 -0
  83. sage/combinat/chas/fsym.py +1180 -0
  84. sage/combinat/chas/wqsym.py +2601 -0
  85. sage/combinat/cluster_complex.py +326 -0
  86. sage/combinat/colored_permutations.py +2039 -0
  87. sage/combinat/colored_permutations_representations.py +964 -0
  88. sage/combinat/composition_signed.py +142 -0
  89. sage/combinat/composition_tableau.py +855 -0
  90. sage/combinat/constellation.py +1729 -0
  91. sage/combinat/core.py +751 -0
  92. sage/combinat/counting.py +12 -0
  93. sage/combinat/crystals/affine.py +742 -0
  94. sage/combinat/crystals/affine_factorization.py +518 -0
  95. sage/combinat/crystals/affinization.py +331 -0
  96. sage/combinat/crystals/alcove_path.py +2013 -0
  97. sage/combinat/crystals/all.py +22 -0
  98. sage/combinat/crystals/bkk_crystals.py +141 -0
  99. sage/combinat/crystals/catalog.py +115 -0
  100. sage/combinat/crystals/catalog_elementary_crystals.py +18 -0
  101. sage/combinat/crystals/catalog_infinity_crystals.py +33 -0
  102. sage/combinat/crystals/catalog_kirillov_reshetikhin.py +18 -0
  103. sage/combinat/crystals/crystals.py +257 -0
  104. sage/combinat/crystals/direct_sum.py +260 -0
  105. sage/combinat/crystals/elementary_crystals.py +1251 -0
  106. sage/combinat/crystals/fast_crystals.py +441 -0
  107. sage/combinat/crystals/fully_commutative_stable_grothendieck.py +1205 -0
  108. sage/combinat/crystals/generalized_young_walls.py +1076 -0
  109. sage/combinat/crystals/highest_weight_crystals.py +436 -0
  110. sage/combinat/crystals/induced_structure.py +695 -0
  111. sage/combinat/crystals/infinity_crystals.py +730 -0
  112. sage/combinat/crystals/kac_modules.py +863 -0
  113. sage/combinat/crystals/kirillov_reshetikhin.py +4196 -0
  114. sage/combinat/crystals/kyoto_path_model.py +497 -0
  115. sage/combinat/crystals/letters.cpython-314-x86_64-linux-musl.so +0 -0
  116. sage/combinat/crystals/letters.pxd +79 -0
  117. sage/combinat/crystals/letters.pyx +3056 -0
  118. sage/combinat/crystals/littelmann_path.py +1518 -0
  119. sage/combinat/crystals/monomial_crystals.py +1262 -0
  120. sage/combinat/crystals/multisegments.py +462 -0
  121. sage/combinat/crystals/mv_polytopes.py +467 -0
  122. sage/combinat/crystals/pbw_crystal.py +511 -0
  123. sage/combinat/crystals/pbw_datum.cpython-314-x86_64-linux-musl.so +0 -0
  124. sage/combinat/crystals/pbw_datum.pxd +4 -0
  125. sage/combinat/crystals/pbw_datum.pyx +487 -0
  126. sage/combinat/crystals/polyhedral_realization.py +372 -0
  127. sage/combinat/crystals/spins.cpython-314-x86_64-linux-musl.so +0 -0
  128. sage/combinat/crystals/spins.pxd +21 -0
  129. sage/combinat/crystals/spins.pyx +756 -0
  130. sage/combinat/crystals/star_crystal.py +290 -0
  131. sage/combinat/crystals/subcrystal.py +464 -0
  132. sage/combinat/crystals/tensor_product.py +1177 -0
  133. sage/combinat/crystals/tensor_product_element.cpython-314-x86_64-linux-musl.so +0 -0
  134. sage/combinat/crystals/tensor_product_element.pxd +35 -0
  135. sage/combinat/crystals/tensor_product_element.pyx +1870 -0
  136. sage/combinat/crystals/virtual_crystal.py +420 -0
  137. sage/combinat/cyclic_sieving_phenomenon.py +204 -0
  138. sage/combinat/debruijn_sequence.cpython-314-x86_64-linux-musl.so +0 -0
  139. sage/combinat/debruijn_sequence.pyx +355 -0
  140. sage/combinat/decorated_permutation.py +270 -0
  141. sage/combinat/degree_sequences.cpython-314-x86_64-linux-musl.so +0 -0
  142. sage/combinat/degree_sequences.pyx +588 -0
  143. sage/combinat/derangements.py +527 -0
  144. sage/combinat/descent_algebra.py +1008 -0
  145. sage/combinat/diagram.py +1551 -0
  146. sage/combinat/diagram_algebras.py +5886 -0
  147. sage/combinat/dyck_word.py +4349 -0
  148. sage/combinat/e_one_star.py +1623 -0
  149. sage/combinat/enumerated_sets.py +123 -0
  150. sage/combinat/expnums.cpython-314-x86_64-linux-musl.so +0 -0
  151. sage/combinat/expnums.pyx +148 -0
  152. sage/combinat/fast_vector_partitions.cpython-314-x86_64-linux-musl.so +0 -0
  153. sage/combinat/fast_vector_partitions.pyx +346 -0
  154. sage/combinat/fqsym.py +1977 -0
  155. sage/combinat/free_dendriform_algebra.py +954 -0
  156. sage/combinat/free_prelie_algebra.py +1141 -0
  157. sage/combinat/fully_commutative_elements.py +1077 -0
  158. sage/combinat/fully_packed_loop.py +1523 -0
  159. sage/combinat/gelfand_tsetlin_patterns.py +1409 -0
  160. sage/combinat/gray_codes.py +311 -0
  161. sage/combinat/grossman_larson_algebras.py +667 -0
  162. sage/combinat/growth.py +4352 -0
  163. sage/combinat/hall_polynomial.py +188 -0
  164. sage/combinat/hillman_grassl.py +866 -0
  165. sage/combinat/integer_matrices.py +329 -0
  166. sage/combinat/integer_vectors_mod_permgroup.py +1238 -0
  167. sage/combinat/k_tableau.py +4564 -0
  168. sage/combinat/kazhdan_lusztig.py +215 -0
  169. sage/combinat/key_polynomial.py +885 -0
  170. sage/combinat/knutson_tao_puzzles.py +2286 -0
  171. sage/combinat/lr_tableau.py +311 -0
  172. sage/combinat/matrices/all.py +24 -0
  173. sage/combinat/matrices/hadamard_matrix.py +3790 -0
  174. sage/combinat/matrices/latin.py +2912 -0
  175. sage/combinat/misc.py +401 -0
  176. sage/combinat/multiset_partition_into_sets_ordered.py +3541 -0
  177. sage/combinat/ncsf_qsym/all.py +21 -0
  178. sage/combinat/ncsf_qsym/combinatorics.py +317 -0
  179. sage/combinat/ncsf_qsym/generic_basis_code.py +1427 -0
  180. sage/combinat/ncsf_qsym/ncsf.py +5637 -0
  181. sage/combinat/ncsf_qsym/qsym.py +4053 -0
  182. sage/combinat/ncsf_qsym/tutorial.py +447 -0
  183. sage/combinat/ncsym/all.py +21 -0
  184. sage/combinat/ncsym/bases.py +855 -0
  185. sage/combinat/ncsym/dual.py +593 -0
  186. sage/combinat/ncsym/ncsym.py +2076 -0
  187. sage/combinat/necklace.py +551 -0
  188. sage/combinat/non_decreasing_parking_function.py +634 -0
  189. sage/combinat/nu_dyck_word.py +1474 -0
  190. sage/combinat/output.py +861 -0
  191. sage/combinat/parallelogram_polyomino.py +4326 -0
  192. sage/combinat/parking_functions.py +1602 -0
  193. sage/combinat/partition_algebra.py +1998 -0
  194. sage/combinat/partition_kleshchev.py +1982 -0
  195. sage/combinat/partition_shifting_algebras.py +584 -0
  196. sage/combinat/partition_tuple.py +3114 -0
  197. sage/combinat/path_tableaux/all.py +13 -0
  198. sage/combinat/path_tableaux/catalog.py +29 -0
  199. sage/combinat/path_tableaux/dyck_path.py +380 -0
  200. sage/combinat/path_tableaux/frieze.py +476 -0
  201. sage/combinat/path_tableaux/path_tableau.py +728 -0
  202. sage/combinat/path_tableaux/semistandard.py +510 -0
  203. sage/combinat/perfect_matching.py +779 -0
  204. sage/combinat/plane_partition.py +3300 -0
  205. sage/combinat/q_bernoulli.cpython-314-x86_64-linux-musl.so +0 -0
  206. sage/combinat/q_bernoulli.pyx +128 -0
  207. sage/combinat/quickref.py +81 -0
  208. sage/combinat/recognizable_series.py +2051 -0
  209. sage/combinat/regular_sequence.py +4316 -0
  210. sage/combinat/regular_sequence_bounded.py +543 -0
  211. sage/combinat/restricted_growth.py +81 -0
  212. sage/combinat/ribbon.py +20 -0
  213. sage/combinat/ribbon_shaped_tableau.py +489 -0
  214. sage/combinat/ribbon_tableau.py +1180 -0
  215. sage/combinat/rigged_configurations/all.py +46 -0
  216. sage/combinat/rigged_configurations/bij_abstract_class.py +548 -0
  217. sage/combinat/rigged_configurations/bij_infinity.py +370 -0
  218. sage/combinat/rigged_configurations/bij_type_A.py +163 -0
  219. sage/combinat/rigged_configurations/bij_type_A2_dual.py +338 -0
  220. sage/combinat/rigged_configurations/bij_type_A2_even.py +218 -0
  221. sage/combinat/rigged_configurations/bij_type_A2_odd.py +199 -0
  222. sage/combinat/rigged_configurations/bij_type_B.py +900 -0
  223. sage/combinat/rigged_configurations/bij_type_C.py +267 -0
  224. sage/combinat/rigged_configurations/bij_type_D.py +771 -0
  225. sage/combinat/rigged_configurations/bij_type_D_tri.py +392 -0
  226. sage/combinat/rigged_configurations/bij_type_D_twisted.py +576 -0
  227. sage/combinat/rigged_configurations/bij_type_E67.py +402 -0
  228. sage/combinat/rigged_configurations/bijection.py +143 -0
  229. sage/combinat/rigged_configurations/kleber_tree.py +1475 -0
  230. sage/combinat/rigged_configurations/kr_tableaux.py +1898 -0
  231. sage/combinat/rigged_configurations/rc_crystal.py +461 -0
  232. sage/combinat/rigged_configurations/rc_infinity.py +540 -0
  233. sage/combinat/rigged_configurations/rigged_configuration_element.py +2403 -0
  234. sage/combinat/rigged_configurations/rigged_configurations.py +1918 -0
  235. sage/combinat/rigged_configurations/rigged_partition.cpython-314-x86_64-linux-musl.so +0 -0
  236. sage/combinat/rigged_configurations/rigged_partition.pxd +15 -0
  237. sage/combinat/rigged_configurations/rigged_partition.pyx +680 -0
  238. sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +499 -0
  239. sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py +428 -0
  240. sage/combinat/rsk.py +3438 -0
  241. sage/combinat/schubert_polynomial.py +508 -0
  242. sage/combinat/set_partition.py +3318 -0
  243. sage/combinat/set_partition_iterator.cpython-314-x86_64-linux-musl.so +0 -0
  244. sage/combinat/set_partition_iterator.pyx +136 -0
  245. sage/combinat/set_partition_ordered.py +1590 -0
  246. sage/combinat/sf/abreu_nigro.py +346 -0
  247. sage/combinat/sf/all.py +52 -0
  248. sage/combinat/sf/character.py +576 -0
  249. sage/combinat/sf/classical.py +319 -0
  250. sage/combinat/sf/dual.py +996 -0
  251. sage/combinat/sf/elementary.py +549 -0
  252. sage/combinat/sf/hall_littlewood.py +1028 -0
  253. sage/combinat/sf/hecke.py +336 -0
  254. sage/combinat/sf/homogeneous.py +464 -0
  255. sage/combinat/sf/jack.py +1428 -0
  256. sage/combinat/sf/k_dual.py +1458 -0
  257. sage/combinat/sf/kfpoly.py +447 -0
  258. sage/combinat/sf/llt.py +789 -0
  259. sage/combinat/sf/macdonald.py +2019 -0
  260. sage/combinat/sf/monomial.py +525 -0
  261. sage/combinat/sf/multiplicative.py +113 -0
  262. sage/combinat/sf/new_kschur.py +1786 -0
  263. sage/combinat/sf/ns_macdonald.py +964 -0
  264. sage/combinat/sf/orthogonal.py +246 -0
  265. sage/combinat/sf/orthotriang.py +355 -0
  266. sage/combinat/sf/powersum.py +963 -0
  267. sage/combinat/sf/schur.py +880 -0
  268. sage/combinat/sf/sf.py +1653 -0
  269. sage/combinat/sf/sfa.py +7053 -0
  270. sage/combinat/sf/symplectic.py +253 -0
  271. sage/combinat/sf/witt.py +721 -0
  272. sage/combinat/shifted_primed_tableau.py +2735 -0
  273. sage/combinat/shuffle.py +830 -0
  274. sage/combinat/sidon_sets.py +146 -0
  275. sage/combinat/similarity_class_type.py +1721 -0
  276. sage/combinat/sine_gordon.py +618 -0
  277. sage/combinat/six_vertex_model.py +784 -0
  278. sage/combinat/skew_partition.py +2053 -0
  279. sage/combinat/skew_tableau.py +2989 -0
  280. sage/combinat/sloane_functions.py +8935 -0
  281. sage/combinat/specht_module.py +1403 -0
  282. sage/combinat/species/all.py +48 -0
  283. sage/combinat/species/characteristic_species.py +321 -0
  284. sage/combinat/species/composition_species.py +273 -0
  285. sage/combinat/species/cycle_species.py +284 -0
  286. sage/combinat/species/empty_species.py +155 -0
  287. sage/combinat/species/functorial_composition_species.py +148 -0
  288. sage/combinat/species/generating_series.py +673 -0
  289. sage/combinat/species/library.py +148 -0
  290. sage/combinat/species/linear_order_species.py +169 -0
  291. sage/combinat/species/misc.py +83 -0
  292. sage/combinat/species/partition_species.py +290 -0
  293. sage/combinat/species/permutation_species.py +268 -0
  294. sage/combinat/species/product_species.py +423 -0
  295. sage/combinat/species/recursive_species.py +476 -0
  296. sage/combinat/species/set_species.py +192 -0
  297. sage/combinat/species/species.py +820 -0
  298. sage/combinat/species/structure.py +539 -0
  299. sage/combinat/species/subset_species.py +243 -0
  300. sage/combinat/species/sum_species.py +225 -0
  301. sage/combinat/subword.py +564 -0
  302. sage/combinat/subword_complex.py +2122 -0
  303. sage/combinat/subword_complex_c.cpython-314-x86_64-linux-musl.so +0 -0
  304. sage/combinat/subword_complex_c.pyx +119 -0
  305. sage/combinat/super_tableau.py +821 -0
  306. sage/combinat/superpartition.py +1154 -0
  307. sage/combinat/symmetric_group_algebra.py +3774 -0
  308. sage/combinat/symmetric_group_representations.py +1830 -0
  309. sage/combinat/t_sequences.py +877 -0
  310. sage/combinat/tableau.py +9506 -0
  311. sage/combinat/tableau_residues.py +860 -0
  312. sage/combinat/tableau_tuple.py +5353 -0
  313. sage/combinat/tiling.py +2432 -0
  314. sage/combinat/triangles_FHM.py +777 -0
  315. sage/combinat/tutorial.py +1857 -0
  316. sage/combinat/vector_partition.py +337 -0
  317. sage/combinat/words/abstract_word.py +1722 -0
  318. sage/combinat/words/all.py +59 -0
  319. sage/combinat/words/alphabet.py +268 -0
  320. sage/combinat/words/finite_word.py +7201 -0
  321. sage/combinat/words/infinite_word.py +113 -0
  322. sage/combinat/words/lyndon_word.py +652 -0
  323. sage/combinat/words/morphic.py +351 -0
  324. sage/combinat/words/morphism.py +3878 -0
  325. sage/combinat/words/paths.py +2932 -0
  326. sage/combinat/words/shuffle_product.py +278 -0
  327. sage/combinat/words/suffix_trees.py +1873 -0
  328. sage/combinat/words/word.py +769 -0
  329. sage/combinat/words/word_char.cpython-314-x86_64-linux-musl.so +0 -0
  330. sage/combinat/words/word_char.pyx +847 -0
  331. sage/combinat/words/word_datatypes.cpython-314-x86_64-linux-musl.so +0 -0
  332. sage/combinat/words/word_datatypes.pxd +4 -0
  333. sage/combinat/words/word_datatypes.pyx +1067 -0
  334. sage/combinat/words/word_generators.py +2026 -0
  335. sage/combinat/words/word_infinite_datatypes.py +1218 -0
  336. sage/combinat/words/word_options.py +99 -0
  337. sage/combinat/words/words.py +2396 -0
  338. sage/data_structures/all__sagemath_combinat.py +1 -0
  339. sage/databases/all__sagemath_combinat.py +13 -0
  340. sage/databases/findstat.py +4897 -0
  341. sage/databases/oeis.py +2058 -0
  342. sage/databases/sloane.py +393 -0
  343. sage/dynamics/all__sagemath_combinat.py +14 -0
  344. sage/dynamics/cellular_automata/all.py +7 -0
  345. sage/dynamics/cellular_automata/catalog.py +34 -0
  346. sage/dynamics/cellular_automata/elementary.py +612 -0
  347. sage/dynamics/cellular_automata/glca.py +477 -0
  348. sage/dynamics/cellular_automata/solitons.py +1463 -0
  349. sage/dynamics/finite_dynamical_system.py +1249 -0
  350. sage/dynamics/finite_dynamical_system_catalog.py +382 -0
  351. sage/games/all.py +7 -0
  352. sage/games/hexad.py +704 -0
  353. sage/games/quantumino.py +591 -0
  354. sage/games/sudoku.py +889 -0
  355. sage/games/sudoku_backtrack.cpython-314-x86_64-linux-musl.so +0 -0
  356. sage/games/sudoku_backtrack.pyx +189 -0
  357. sage/groups/all__sagemath_combinat.py +1 -0
  358. sage/groups/indexed_free_group.py +489 -0
  359. sage/libs/all__sagemath_combinat.py +6 -0
  360. sage/libs/lrcalc/__init__.py +1 -0
  361. sage/libs/lrcalc/lrcalc.py +525 -0
  362. sage/libs/symmetrica/__init__.py +7 -0
  363. sage/libs/symmetrica/all.py +101 -0
  364. sage/libs/symmetrica/kostka.pxi +168 -0
  365. sage/libs/symmetrica/part.pxi +193 -0
  366. sage/libs/symmetrica/plet.pxi +42 -0
  367. sage/libs/symmetrica/sab.pxi +196 -0
  368. sage/libs/symmetrica/sb.pxi +332 -0
  369. sage/libs/symmetrica/sc.pxi +192 -0
  370. sage/libs/symmetrica/schur.pxi +956 -0
  371. sage/libs/symmetrica/symmetrica.cpython-314-x86_64-linux-musl.so +0 -0
  372. sage/libs/symmetrica/symmetrica.pxi +1172 -0
  373. sage/libs/symmetrica/symmetrica.pyx +39 -0
  374. sage/monoids/all.py +13 -0
  375. sage/monoids/automatic_semigroup.py +1054 -0
  376. sage/monoids/free_abelian_monoid.py +315 -0
  377. sage/monoids/free_abelian_monoid_element.cpython-314-x86_64-linux-musl.so +0 -0
  378. sage/monoids/free_abelian_monoid_element.pxd +16 -0
  379. sage/monoids/free_abelian_monoid_element.pyx +397 -0
  380. sage/monoids/free_monoid.py +335 -0
  381. sage/monoids/free_monoid_element.py +431 -0
  382. sage/monoids/hecke_monoid.py +65 -0
  383. sage/monoids/string_monoid.py +817 -0
  384. sage/monoids/string_monoid_element.py +547 -0
  385. sage/monoids/string_ops.py +143 -0
  386. sage/monoids/trace_monoid.py +972 -0
  387. sage/rings/all__sagemath_combinat.py +2 -0
  388. sage/sat/all.py +4 -0
  389. sage/sat/boolean_polynomials.py +405 -0
  390. sage/sat/converters/__init__.py +6 -0
  391. sage/sat/converters/anf2cnf.py +14 -0
  392. sage/sat/converters/polybori.py +611 -0
  393. sage/sat/solvers/__init__.py +5 -0
  394. sage/sat/solvers/cryptominisat.py +287 -0
  395. sage/sat/solvers/dimacs.py +783 -0
  396. sage/sat/solvers/picosat.py +228 -0
  397. sage/sat/solvers/sat_lp.py +156 -0
  398. sage/sat/solvers/satsolver.cpython-314-x86_64-linux-musl.so +0 -0
  399. sage/sat/solvers/satsolver.pxd +3 -0
  400. sage/sat/solvers/satsolver.pyx +405 -0
@@ -0,0 +1,4352 @@
1
+ # sage_setup: distribution = sagemath-combinat
2
+ # sage.doctest: needs sage.combinat sage.graphs
3
+ r"""
4
+ Growth diagrams and dual graded graphs
5
+
6
+ AUTHORS:
7
+
8
+ - Martin Rubey (2016-09): Initial version
9
+ - Martin Rubey (2017-09): generalize, more rules, improve documentation
10
+ - Travis Scrimshaw (2017-09): switch to rule-based framework
11
+
12
+ .. TODO::
13
+
14
+ - provide examples for the P and Q-symbol in the skew case
15
+ - implement a method providing a visualization of the growth
16
+ diagram with all labels, perhaps as LaTeX code
17
+ - when shape is given, check that it is compatible with filling
18
+ or labels
19
+ - optimize rules, mainly for :class:`RuleRSK` and
20
+ :class:`RuleBurge`
21
+ - implement backward rules for :class:`GrowthDiagram.rules.Domino`
22
+ - implement backward rule from [LLMSSZ2013]_, [LS2007]_
23
+ - make semistandard extension generic
24
+ - accommodate dual filtered graphs
25
+
26
+ A guided tour
27
+ =============
28
+
29
+ Growth diagrams, invented by Sergey Fomin [Fom1994]_, [Fom1995]_,
30
+ provide a vast generalization of the Robinson-Schensted-Knuth (RSK)
31
+ correspondence between matrices with nonnegative integer entries and
32
+ pairs of semistandard Young tableaux of the same shape.
33
+
34
+ The main fact is that many correspondences similar to RSK can be
35
+ defined by providing a pair of so-called local rules: a 'forward'
36
+ rule, whose input are three vertices `y`, `t` and `x` of a certain
37
+ directed graph (in the case of Robinson-Schensted: the directed graph
38
+ corresponding to Young's lattice) and an integer (in the case of
39
+ Robinson-Schensted: `0` or `1`), and whose output is a fourth vertex
40
+ `z`. This rule should be invertible in the following sense: there is
41
+ a so-called 'backward' rule that recovers the integer and `t` given
42
+ `y`, `z` and `x`.
43
+
44
+ As an example, the growth rules for the classical RSK correspondence
45
+ are provided by :class:`RuleRSK`. To produce a growth diagram, pass
46
+ the desired rule and a permutation to :class:`GrowthDiagram`::
47
+
48
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
49
+ sage: w = [2,3,6,1,4,5]; G = GrowthDiagram(RuleRSK, w); G
50
+ 0 0 0 1 0 0
51
+ 1 0 0 0 0 0
52
+ 0 1 0 0 0 0
53
+ 0 0 0 0 1 0
54
+ 0 0 0 0 0 1
55
+ 0 0 1 0 0 0
56
+
57
+ The forward rule just mentioned assigns 49 partitions to the corners
58
+ of each of the 36 cells of this matrix (i.e., 49 the vertices of a
59
+ `(6+1) \times (6+1)` grid graph), with the exception of the corners
60
+ on the left and top boundary, which are initialized with the empty
61
+ partition. More precisely, for each cell, the
62
+ :meth:`~sage.combinat.growth.RuleRSK.forward_rule` computes the
63
+ partition `z` labelling the lower right corner, given the content `c`
64
+ of a cell and the other three partitions::
65
+
66
+ t --- x
67
+ | c |
68
+ y --- z
69
+
70
+ .. WARNING::
71
+
72
+ Note that a growth diagram is printed with matrix coordinates,
73
+ the origin being in the top-left corner. Therefore, the growth
74
+ is from the top left to the bottom right!
75
+
76
+ The partitions along the boundary opposite of the origin, reading
77
+ from the bottom left to the top right, are obtained by using the
78
+ method :meth:`~sage.combinat.growth.GrowthDiagram.out_labels`::
79
+
80
+ sage: G.out_labels()
81
+ [[],
82
+ [1],
83
+ [2],
84
+ [3],
85
+ [3, 1],
86
+ [3, 2],
87
+ [4, 2],
88
+ [4, 1],
89
+ [3, 1],
90
+ [2, 1],
91
+ [1, 1],
92
+ [1],
93
+ []]
94
+
95
+ However, in the case of a rectangular filling, it is more practical
96
+ to split this sequence of labels in two. Interpreting the sequence
97
+ of partitions along the right boundary as a standard Young tableau,
98
+ we then obtain the so-called
99
+ :meth:`~sage.combinat.growth.RulePartitions.P_symbol`, the partitions
100
+ along the bottom boundary yield the so-called
101
+ :meth:`~sage.combinat.growth.RulePartitions.Q_symbol`. These
102
+ coincide with the output of the classical
103
+ :func:`~sage.combinat.rsk.RSK` insertion algorithm::
104
+
105
+ sage: ascii_art([G.P_symbol(), G.Q_symbol()])
106
+ [ 1 3 4 5 1 2 3 6 ]
107
+ [ 2 6 , 4 5 ]
108
+ sage: ascii_art(RSK(w))
109
+ [ 1 3 4 5 1 2 3 6 ]
110
+ [ 2 6 , 4 5 ]
111
+
112
+ The filling can be recovered knowing the partitions labelling the
113
+ corners of the bottom and the right boundary alone, by repeatedly
114
+ applying the :meth:`~sage.combinat.growth.RuleRSK.backward_rule`.
115
+ Therefore, to initialize a :class:`GrowthDiagram`, we can provide
116
+ these labels instead of the filling::
117
+
118
+ sage: GrowthDiagram(RuleRSK, labels=G.out_labels())
119
+ 0 0 0 1 0 0
120
+ 1 0 0 0 0 0
121
+ 0 1 0 0 0 0
122
+ 0 0 0 0 1 0
123
+ 0 0 0 0 0 1
124
+ 0 0 1 0 0 0
125
+
126
+ Invocation
127
+ ----------
128
+
129
+ In general, growth diagrams are defined for `0-1`-fillings of
130
+ arbitrary skew shapes. In the case of the Robinson-Schensted-Knuth
131
+ correspondence, even arbitrary nonnegative integers are allowed. In
132
+ other cases, entries may be either zero or an `r`-th root of unity -
133
+ for example, :class:`~sage.combinat.growth.RuleDomino` insertion is
134
+ defined for signed permutations, that is, `r=2`. Traditionally, words
135
+ and permutations are also used to specify a filling in special cases.
136
+
137
+ To accommodate all this, the filling may be passed in various ways.
138
+ The most general possibility is to pass a dictionary of coordinates
139
+ to (signed) entries, where zeros can be omitted. In this case, when
140
+ the parameter ``shape`` is not explicitly specified, it is assumed
141
+ to be the minimal rectangle containing the origin and all coordinates
142
+ with nonzero entries.
143
+
144
+ For example, consider the following generalized permutation::
145
+
146
+ 1 2 2 2 4 4
147
+ 4 2 3 3 2 3
148
+
149
+ that we encode as the dictionary::
150
+
151
+ sage: P = {(1-1,4-1): 1, (2-1,2-1): 1, (2-1,3-1): 2, (4-1,2-1): 1, (4-1,3-1): 1}
152
+
153
+ Note that we are subtracting `1` from all entries because of
154
+ zero-based indexing, we obtain::
155
+
156
+ sage: GrowthDiagram(RuleRSK, P)
157
+ 0 0 0 0
158
+ 0 1 0 1
159
+ 0 2 0 1
160
+ 1 0 0 0
161
+
162
+ Alternatively, we could create the same growth diagram using a
163
+ matrix.
164
+
165
+ Let us also mention that one can pass the arguments specifying
166
+ a growth diagram directly to the rule::
167
+
168
+ sage: RuleRSK(P)
169
+ 0 0 0 0
170
+ 0 1 0 1
171
+ 0 2 0 1
172
+ 1 0 0 0
173
+
174
+ In contrast to the classical insertion algorithms, growth diagrams
175
+ immediately generalize to fillings whose shape is an arbitrary skew
176
+ partition::
177
+
178
+ sage: GrowthDiagram(RuleRSK, [3,1,2], shape=SkewPartition([[3,3,2],[1,1]]))
179
+ . 1 0
180
+ . 0 1
181
+ 1 0
182
+
183
+ As an important example, consider the Stanley-Sundaram correspondence
184
+ between oscillating tableaux and (partial) perfect matchings.
185
+ Perfect matchings of `\{1, \ldots, 2r\}` are in bijection with
186
+ `0-1`-fillings of a triangular shape with `2r-1` rows, such that for
187
+ each `k` there is either exactly one nonzero entry in row `k` or
188
+ exactly one nonzero entry in column `2r-k`. Explicitly, if `(i,j)`
189
+ is a pair in the perfect matching, the entry in column `i-1` and row
190
+ `2r-j` equals `1`. For example::
191
+
192
+ sage: m = [[1,5],[3,4],[2,7],[6,8]]
193
+ sage: G = RuleRSK({(i-1, 8-j): 1 for i,j in m}, shape=[7,6,5,4,3,2,1]); G
194
+ 0 0 0 0 0 1 0
195
+ 0 1 0 0 0 0
196
+ 0 0 0 0 0
197
+ 1 0 0 0
198
+ 0 0 1
199
+ 0 0
200
+ 0
201
+
202
+ The partitions labelling the bottom-right corners along the boundary
203
+ opposite of the origin then form a so-called oscillating tableau -
204
+ the remaining partitions along the bottom-right boundary are
205
+ redundant::
206
+
207
+ sage: G.out_labels()[1::2]
208
+ [[1], [1, 1], [2, 1], [1, 1], [1], [1, 1], [1]]
209
+
210
+ Another great advantage of growth diagrams is that we immediately
211
+ have access to a skew version of the correspondence, by providing
212
+ different initialization for the labels on the side of the origin.
213
+ We reproduce the original example of Bruce Sagan and Richard Stanley,
214
+ see also Tom Roby's thesis [Rob1991]_::
215
+
216
+ sage: w = {(1-1,4-1): 1, (2-1,2-1): 1, (4-1,3-1): 1}
217
+ sage: T = SkewTableau([[None, None], [None, 5], [1]])
218
+ sage: U = SkewTableau([[None, None], [None, 3], [5]])
219
+ sage: labels = T.to_chain()[::-1] + U.to_chain()[1:]
220
+ sage: G = GrowthDiagram(RuleRSK, filling=w, shape=[5,5,5,5,5], labels=labels); G
221
+ 0 0 0 0 0
222
+ 0 1 0 0 0
223
+ 0 0 0 1 0
224
+ 1 0 0 0 0
225
+ 0 0 0 0 0
226
+ sage: ascii_art([G.P_symbol(), G.Q_symbol()])
227
+ [ . . 2 3 . . 1 4 ]
228
+ [ . . . . ]
229
+ [ . 4 . 2 ]
230
+ [ 1 3 ]
231
+ [ 5 , 5 ]
232
+
233
+ Similarly, there is a correspondence for skew oscillating tableau.
234
+ Let us conclude by reproducing Example 4.2.6 from [Rob1991]_. The
235
+ oscillating tableau, as given, is::
236
+
237
+ sage: o = [[2,1],[2,2],[3,2],[4,2],[4,1],[4,1,1],[3,1,1],[3,1],[3,2],[3,1],[2,1]]
238
+
239
+ From this, we have to construct the list of labels of the corners
240
+ along the bottom-right boundary. The labels with odd indices are
241
+ given by the oscillating tableau, the other labels are obtained by
242
+ taking the smaller of the two neighbouring partitions::
243
+
244
+ sage: l = [o[i//2] if is_even(i) else min(o[(i-1)//2],o[(i+1)//2])
245
+ ....: for i in range(2*len(o)-1)]
246
+ sage: la = list(range(len(o)-2, 0, -1))
247
+ sage: G = RuleRSK(labels=l[1:-1], shape=la); G
248
+ 0 0 0 0 0 0 0 1 0
249
+ 0 1 0 0 0 0 0 0
250
+ 0 0 0 0 0 0 0
251
+ 0 0 0 0 0 0
252
+ 0 0 1 0 0
253
+ 0 0 0 0
254
+ 0 0 0
255
+ 0 0
256
+ 0
257
+
258
+ The skew tableaux can now be read off the partitions labelling the
259
+ left and the top boundary. These can be accessed using the method
260
+ :meth:`~sage.combinat.growth.GrowthDiagram.in_labels`::
261
+
262
+ sage: ascii_art(SkewTableau(chain=G.in_labels()[len(o)-2:]),
263
+ ....: SkewTableau(chain=G.in_labels()[len(o)-2::-1]))
264
+ . 1 . 7
265
+ 5 4
266
+
267
+ Rules currently available
268
+ -------------------------
269
+
270
+ As mentioned at the beginning, the Robinson-Schensted-Knuth
271
+ correspondence is just a special case of growth diagrams. In
272
+ particular, we have implemented the following local rules:
273
+
274
+ - RSK (:class:`~sage.combinat.growth.RuleRSK`).
275
+ - A variation of RSK originally due to Burge
276
+ (:class:`~sage.combinat.growth.RuleBurge`).
277
+ - A correspondence producing binary words originally due to Viennot
278
+ (:class:`~sage.combinat.growth.RuleBinaryWord`).
279
+ - A correspondence producing domino tableaux
280
+ (:class:`~sage.combinat.growth.RuleDomino`) originally due
281
+ to Barbasch and Vogan.
282
+ - A correspondence for shifted shapes
283
+ (:class:`~sage.combinat.growth.RuleShiftedShapes`), where the
284
+ original insertion algorithm is due to Sagan and Worley, and Haiman.
285
+ - The Sylvester correspondence, producing binary trees
286
+ (:class:`~sage.combinat.growth.RuleSylvester`).
287
+ - The Young-Fibonacci correspondence
288
+ (:class:`~sage.combinat.growth.RuleYoungFibonacci`).
289
+ - LLMS insertion (:class:`~sage.combinat.growth.RuleLLMS`).
290
+
291
+ Background
292
+ ----------
293
+
294
+ At the heart of Fomin's framework is the notion of dual graded
295
+ graphs. This is a pair of digraphs `P, Q` (multiple edges being
296
+ allowed) on the same set of vertices `V`, that satisfy the following
297
+ conditions:
298
+
299
+ * the graphs are graded, that is, there is a function `\rho : V \to
300
+ \NN`, such that for any edge `(v, w)` of `P` and also of `Q` we
301
+ have `\rho(w) = \rho(v) + 1`,
302
+
303
+ * there is a vertex `0` with rank zero, and
304
+
305
+ * there is a positive integer `r` such that `DU = UD + rI` on the
306
+ free `\ZZ`-module `\ZZ[V]`, where `D` is the down operator of `Q`,
307
+ assigning to each vertex the formal sum of its predecessors, `U` is
308
+ the up operator of `P`, assigning to each vertex the formal sum of
309
+ its successors, and `I` is the identity operator.
310
+
311
+ Note that the condition `DU = UD + rI` is symmetric with respect to
312
+ the interchange of the graphs `P` and `Q`, because the up operator of
313
+ a graph is the transpose of its down operator.
314
+
315
+ For example, taking for both `P` and `Q` to be Young's lattice and
316
+ `r=1`, we obtain the dual graded graphs for classical Schensted
317
+ insertion.
318
+
319
+ Given such a pair of graphs, there is a bijection between the
320
+ `r`-colored permutations on `k` letters and pairs `(p, q)`, where `p`
321
+ is a path in `P` from zero to a vertex of rank `k` and `q` is a path
322
+ in `Q` from zero to the same vertex.
323
+
324
+ It turns out that - in principle - this bijection can always be
325
+ described by so-called local forward and backward rules, see
326
+ [Fom1995]_ for a detailed description. Knowing at least the forward
327
+ rules, or the backward rules, you can implement your own growth
328
+ diagram class.
329
+
330
+ Implementing your own growth diagrams
331
+ -------------------------------------
332
+
333
+ The class :class:`GrowthDiagram` is written so that it is easy to
334
+ implement growth diagrams you come across in your research.
335
+ Moreover, the class tolerates some deviations from Fomin's
336
+ definitions. For example, although the general
337
+ Robinson-Schensted-Knuth correspondence between integer matrices and
338
+ semistandard tableaux is, strictly speaking, not a growth on dual
339
+ graded graphs, it is supported by our framework.
340
+
341
+ For illustration, let us implement a growth diagram class with the
342
+ backward rule only. Suppose that the vertices of the graph are the
343
+ nonnegative integers, the rank is given by the integer itself, and
344
+ the backward rule is `(y, z, x) \mapsto (\min(x,y), 0)` if `y = z`
345
+ or `x = z` and `(y, z, x) \mapsto (\min(x,y), 1)` otherwise.
346
+
347
+ We first need to import the base class for a rule::
348
+
349
+ sage: from sage.combinat.growth import Rule
350
+
351
+ Next, we implement the backward rule and the rank function and
352
+ provide the bottom element ``zero`` of the graph. For more
353
+ information, see :class:`~sage.combinat.growth.Rule`. ::
354
+
355
+ sage: class RulePascal(Rule):
356
+ ....: zero = 0
357
+ ....: def rank(self, v): return v
358
+ ....: def backward_rule(self, y, z, x):
359
+ ....: return (min(x,y), 0 if y==z or x==z else 1)
360
+
361
+ We can now compute the filling corresponding to a sequence of labels
362
+ as follows::
363
+
364
+ sage: GrowthDiagram(RulePascal(), labels=[0,1,2,1,2,1,0])
365
+ 1 0 0
366
+ 0 0 1
367
+ 0 1
368
+
369
+ Of course, since we have not provided the forward rule, we cannot
370
+ compute the labels belonging to a filling::
371
+
372
+ sage: GrowthDiagram(RulePascal(), [3,1,2])
373
+ Traceback (most recent call last):
374
+ ...
375
+ AttributeError: 'RulePascal' object has no attribute 'forward_rule'...
376
+
377
+ We now re-implement the rule where we provide the dual graded graphs::
378
+
379
+ sage: class RulePascal(Rule):
380
+ ....: zero = 0
381
+ ....: def rank(self, v): return v
382
+ ....: def backward_rule(self, y, z, x):
383
+ ....: return (min(x,y), 0 if y==z or x==z else 1)
384
+ ....: def vertices(self, n): return [n]
385
+ ....: def is_P_edge(self, v, w): return w == v + 1
386
+ ....: def is_Q_edge(self, v, w): return w == v + 1
387
+
388
+ Are they really dual? ::
389
+
390
+ sage: RulePascal()._check_duality(3)
391
+ Traceback (most recent call last):
392
+ ...
393
+ ValueError: D U - U D differs from 1 I for vertex 3:
394
+ D U = [3]
395
+ U D + 1 I = [3, 3]
396
+
397
+ With our current definition, duality fails - in fact, there are no
398
+ dual graded graphs on the integers without multiple edges.
399
+ Consequently, also the backward rule cannot work as ``backward_rule``
400
+ requires additional information (the edge labels as arguments).
401
+
402
+ Let us thus continue with the example from Section 4.7 of [Fom1995]_
403
+ instead, which defines dual graded graphs with multiple edges on the
404
+ integers. The color ``self.zero_edge``, which defaults to ``0`` is
405
+ reserved for degenerate edges, but may be abused for the unique edge
406
+ if one of the graphs has no multiple edges. For greater clarity in
407
+ this example we set it to ``None``::
408
+
409
+ sage: class RulePascal(Rule):
410
+ ....: zero = 0
411
+ ....: has_multiple_edges = True
412
+ ....: zero_edge = None
413
+ ....: def rank(self, v): return v
414
+ ....: def vertices(self, n): return [n]
415
+ ....: def is_P_edge(self, v, w): return [0] if w == v + 1 else []
416
+ ....: def is_Q_edge(self, v, w): return list(range(w)) if w == v+1 else []
417
+
418
+ We verify these are `1` dual at level `5`::
419
+
420
+ sage: RulePascal()._check_duality(5)
421
+
422
+ Finally, let us provide the backward rule. The arguments of the rule
423
+ are vertices together with the edge labels now, specifying the path
424
+ from the lower left to the upper right of the cell. The horizontal
425
+ edges come from `Q`, whereas the vertical edges come from `P`.
426
+
427
+ Thus, the definition in Section 4.7 of [Fom1995]_ translates as
428
+ follows::
429
+
430
+ sage: class RulePascal(Rule):
431
+ ....: zero = 0
432
+ ....: has_multiple_edges = True
433
+ ....: zero_edge = None
434
+ ....: def rank(self, v): return v
435
+ ....: def vertices(self, n): return [n]
436
+ ....: def is_P_edge(self, v, w): return [0] if w == v + 1 else []
437
+ ....: def is_Q_edge(self, v, w): return list(range(w)) if w == v+1 else []
438
+ ....: def backward_rule(self, y, g, z, h, x):
439
+ ....: if g is None:
440
+ ....: return (0, x, None, 0)
441
+ ....: if h is None:
442
+ ....: return (None, y, g, 0)
443
+ ....: if g == 0:
444
+ ....: return (None, y, None, 1)
445
+ ....: else:
446
+ ....: return (0, x-1, g-1, 0)
447
+
448
+ The labels are now alternating between vertices and edge-colors::
449
+
450
+ sage: GrowthDiagram(RulePascal(), labels=[0,0,1,0,2,0,1,0,0])
451
+ 1 0
452
+ 0 1
453
+
454
+ sage: GrowthDiagram(RulePascal(), labels=[0,0,1,1,2,0,1,0,0])
455
+ 0 1
456
+ 1 0
457
+ """
458
+
459
+ # ****************************************************************************
460
+ # Copyright (C) 2017 Martin Rubey <martin.rubey at tuwien.ac.at>
461
+ # 2017 Travis Scrimshaw <tcscrims at gmail.com>
462
+ #
463
+ # Distributed under the terms of the GNU General Public License (GPL)
464
+ #
465
+ # This code is distributed in the hope that it will be useful,
466
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
467
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
468
+ # General Public License for more details.
469
+ #
470
+ # The full text of the GPL is available at:
471
+ #
472
+ # https://www.gnu.org/licenses/
473
+ # ***************************************************************************
474
+ from __future__ import annotations
475
+ from copy import copy
476
+ from itertools import zip_longest
477
+
478
+ from sage.structure.sage_object import SageObject
479
+ from sage.structure.unique_representation import UniqueRepresentation
480
+ from sage.combinat.words.word import Word
481
+ from sage.combinat.words.words import Words
482
+ from sage.combinat.binary_tree import BinaryTree, BinaryTrees, LabelledBinaryTree
483
+ from sage.combinat.composition import Compositions
484
+ from sage.combinat.partition import _Partitions, Partitions
485
+ from sage.combinat.skew_partition import SkewPartition
486
+ from sage.combinat.skew_tableau import SkewTableau
487
+ from sage.combinat.core import Core, Cores
488
+ from sage.combinat.k_tableau import WeakTableau, StrongTableau
489
+ from sage.combinat.shifted_primed_tableau import ShiftedPrimedTableau
490
+ from sage.misc.lazy_import import lazy_import
491
+
492
+ lazy_import('sage.graphs.digraph', 'DiGraph')
493
+ lazy_import('sage.combinat.posets.posets', 'Poset')
494
+
495
+
496
+ def _make_partition(l):
497
+ """
498
+ Return the list as a partition.
499
+
500
+ This is intended to be fast, so checks are bypassed.
501
+
502
+ TESTS::
503
+
504
+ sage: from sage.combinat.growth import _make_partition
505
+ sage: p = _make_partition([3,2,1,0]); p
506
+ [3, 2, 1]
507
+
508
+ sage: p.parent()
509
+ Partitions
510
+ """
511
+ return _Partitions.element_class(_Partitions, l)
512
+
513
+
514
+ class GrowthDiagram(SageObject):
515
+ r"""
516
+ A generalized Schensted growth diagram in the sense of Fomin.
517
+
518
+ Growth diagrams were introduced by Sergey Fomin [Fom1994]_,
519
+ [Fom1995]_ and provide a vast generalization of the
520
+ Robinson-Schensted-Knuth (RSK) correspondence between matrices
521
+ with nonnegative integer entries and pairs of semistandard Young
522
+ tableaux of the same shape.
523
+
524
+ A growth diagram is based on the notion of *dual graded graphs*,
525
+ a pair of digraphs `P, Q` (multiple edges being allowed) on the
526
+ same set of vertices `V`, that satisfy the following conditions:
527
+
528
+ * the graphs are graded, that is, there is a function `\rho:
529
+ V \to \NN`, such that for any edge `(v, w)` of `P` and also
530
+ of `Q` we have `\rho(w) = \rho(v) + 1`,
531
+
532
+ * there is a vertex `0` with rank zero, and
533
+
534
+ * there is a positive integer `r` such that `DU = UD + rI` on the
535
+ free `\ZZ`-module `\ZZ[V]`, where `D` is the down operator of
536
+ `Q`, assigning to each vertex the formal sum of its
537
+ predecessors, `U` is the up operator of `P`, assigning to each
538
+ vertex the formal sum of its successors, and `I` is the
539
+ identity operator.
540
+
541
+ Growth diagrams are defined by providing a pair of local rules: a
542
+ 'forward' rule, whose input are three vertices `y`, `t` and `x`
543
+ of the dual graded graphs and an integer, and whose output is a
544
+ fourth vertex `z`. This rule should be invertible in the
545
+ following sense: there is a so-called 'backward' rule that
546
+ recovers the integer and `t` given `y`, `z` and `x`.
547
+
548
+ All implemented growth diagram rules are available by
549
+ ``GrowthDiagram.rules.<tab>``. The current list is:
550
+
551
+ - :class:`~sage.combinat.growth.RuleRSK` -- RSK
552
+ - :class:`~sage.combinat.growth.RuleBurge` -- a variation of RSK
553
+ originally due to Burge
554
+ - :class:`~sage.combinat.growth.RuleBinaryWord` -- a correspondence
555
+ producing binary words originally due to Viennot
556
+ - :class:`~sage.combinat.growth.RuleDomino` -- a correspondence
557
+ producing domino tableaux originally due to Barbasch and Vogan
558
+ - :class:`~sage.combinat.growth.RuleShiftedShapes` -- a correspondence
559
+ for shifted shapes, where the original insertion algorithm is due
560
+ to Sagan and Worley, and Haiman.
561
+ - :class:`~sage.combinat.growth.RuleSylvester` -- the Sylvester
562
+ correspondence, producing binary trees
563
+ - :class:`~sage.combinat.growth.RuleYoungFibonacci` -- the
564
+ Young-Fibonacci correspondence
565
+ - :class:`~sage.combinat.growth.RuleLLMS` -- LLMS insertion
566
+
567
+ INPUT:
568
+
569
+ - ``rule`` -- :class:`~sage.combinat.growth.Rule`;
570
+ the growth diagram rule
571
+
572
+ - ``filling`` -- (optional) a dictionary whose keys are coordinates
573
+ and values are integers, a list of lists of integers, or a word
574
+ with integer values; if a word, then negative letters but without
575
+ repetitions are allowed and interpreted as coloured permutations
576
+
577
+ - ``shape`` -- (optional) a (possibly skew) partition
578
+
579
+ - ``labels`` -- (optional) a list that specifies a path whose length
580
+ in the half-perimeter of the shape; more details given below
581
+
582
+ If ``filling`` is not given, then the growth diagram is determined
583
+ by applying the backward rule to ``labels`` decorating the
584
+ boundary opposite of the origin of the ``shape``. In this case,
585
+ ``labels`` are interpreted as labelling the boundary opposite of
586
+ the origin.
587
+
588
+ Otherwise, ``shape`` is inferred from ``filling`` or ``labels`` if
589
+ possible and ``labels`` is set to ``rule.zero`` if not specified.
590
+ Here, ``labels`` are labelling the boundary on the side of the origin.
591
+
592
+ For ``labels``, if ``rule.has_multiple_edges`` is ``True``, then the
593
+ elements should be of the form `(v_1, e_1, \ldots, e_{n-1}, v_n)`,
594
+ where `n` is the half-perimeter of ``shape``, and `(v_{i-1}, e_i, v_i)`
595
+ is an edge in the dual graded graph for all `i`. Otherwise, it is a
596
+ list of `n` vertices.
597
+
598
+ .. NOTE::
599
+
600
+ Coordinates are of the form ``(col, row)`` where the origin is
601
+ in the upper left, to be consistent with permutation matrices
602
+ and skew tableaux (in English convention). This is different
603
+ from Fomin's convention, who uses a Cartesian coordinate system.
604
+
605
+ Conventions are chosen such that for permutations, the same
606
+ growth diagram is constructed when passing the permutation
607
+ matrix instead.
608
+
609
+ EXAMPLES:
610
+
611
+ We create a growth diagram using the forward RSK rule and a permutation::
612
+
613
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
614
+ sage: pi = Permutation([4, 1, 2, 3])
615
+ sage: G = GrowthDiagram(RuleRSK, pi); G
616
+ 0 1 0 0
617
+ 0 0 1 0
618
+ 0 0 0 1
619
+ 1 0 0 0
620
+ sage: G.out_labels()
621
+ [[], [1], [1, 1], [2, 1], [3, 1], [3], [2], [1], []]
622
+
623
+ Passing the permutation matrix instead gives the same result::
624
+
625
+ sage: G = GrowthDiagram(RuleRSK, pi.to_matrix()) # needs sage.modules
626
+ sage: ascii_art([G.P_symbol(), G.Q_symbol()]) # needs sage.modules
627
+ [ 1 2 3 1 3 4 ]
628
+ [ 4 , 2 ]
629
+
630
+ We give the same example but using a skew shape::
631
+
632
+ sage: shape = SkewPartition([[4,4,4,2],[1,1]])
633
+ sage: G = GrowthDiagram(RuleRSK, pi, shape=shape); G
634
+ . 1 0 0
635
+ . 0 1 0
636
+ 0 0 0 1
637
+ 1 0
638
+ sage: G.out_labels()
639
+ [[], [1], [1, 1], [1], [2], [3], [2], [1], []]
640
+
641
+ We construct a growth diagram using the backwards RSK rule by
642
+ specifying the labels::
643
+
644
+ sage: GrowthDiagram(RuleRSK, labels=G.out_labels())
645
+ 0 1 0 0
646
+ 0 0 1 0
647
+ 0 0 0 1
648
+ 1 0
649
+ """
650
+
651
+ def __init__(self, rule, filling=None, shape=None, labels=None):
652
+ r"""
653
+ Initialize ``self``.
654
+
655
+ TESTS::
656
+
657
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
658
+ sage: w = [3,3,2,4,1]; G = GrowthDiagram(RuleRSK, w)
659
+ sage: [G.P_symbol(), G.Q_symbol()]
660
+ [[[1, 3, 4], [2], [3]], [[1, 2, 4], [3], [5]]]
661
+ sage: RSK(w)
662
+ [[[1, 3, 4], [2], [3]], [[1, 2, 4], [3], [5]]]
663
+
664
+ sage: TestSuite(G).run()
665
+
666
+ sage: GrowthDiagram(RuleRSK)
667
+ Traceback (most recent call last):
668
+ ...
669
+ ValueError: please provide a filling or a sequence of labels
670
+ """
671
+ if not isinstance(rule, Rule):
672
+ raise TypeError("the rule must be an instance of Rule")
673
+ self.rule = rule
674
+
675
+ if filling is None:
676
+ if labels is None:
677
+ raise ValueError("please provide a filling or a sequence of labels")
678
+
679
+ labels = self._process_labels(labels)
680
+
681
+ if shape is None:
682
+ shape = self._shape_from_labels(labels)
683
+
684
+ self._lambda, self._mu = self._process_shape(shape)
685
+ self._out_labels = labels
686
+ self._check_labels(self._out_labels)
687
+ self._shrink()
688
+ else:
689
+ self._filling, (self._lambda, self._mu) = self._process_filling_and_shape(filling, shape)
690
+
691
+ if labels is None:
692
+ rule = self.rule
693
+ if rule.has_multiple_edges:
694
+ self._in_labels = [rule.zero, rule.zero_edge]*(self.half_perimeter()-1) + [rule.zero]
695
+ else:
696
+ self._in_labels = [rule.zero] * self.half_perimeter()
697
+ else:
698
+ labels = self._process_labels(labels)
699
+ self._in_labels = labels
700
+
701
+ self._check_labels(self._in_labels)
702
+ self._grow()
703
+
704
+ def filling(self):
705
+ r"""
706
+ Return the filling of the diagram as a dictionary.
707
+
708
+ EXAMPLES::
709
+
710
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
711
+ sage: G = GrowthDiagram(RuleRSK, [[0,1,0], [1,0,2]])
712
+ sage: G.filling()
713
+ {(0, 1): 1, (1, 0): 1, (2, 1): 2}
714
+ """
715
+ return self._filling
716
+
717
+ def conjugate(self):
718
+ r"""
719
+ Return the conjugate growth diagram of ``self``.
720
+
721
+ This is the growth diagram with the filling reflected over the
722
+ main diagonal.
723
+
724
+ The sequence of labels along the boundary on the side of the
725
+ origin is the reversal of the corresponding sequence of the
726
+ original growth diagram.
727
+
728
+ When the filling is a permutation, the conjugate filling
729
+ corresponds to its inverse.
730
+
731
+ EXAMPLES::
732
+
733
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
734
+ sage: G = GrowthDiagram(RuleRSK, [[0,1,0], [1,0,2]])
735
+ sage: Gc = G.conjugate()
736
+ sage: (Gc.P_symbol(), Gc.Q_symbol()) == (G.Q_symbol(), G.P_symbol())
737
+ True
738
+
739
+ TESTS:
740
+
741
+ Check that labels and shape are handled correctly::
742
+
743
+ sage: o = [[2,1],[2,2],[3,2],[4,2],[4,1],[4,1,1],[3,1,1],[3,1],[3,2],[3,1],[2,1]]
744
+ sage: l = [o[i//2] if is_even(i) else min(o[(i-1)//2],o[(i+1)//2])
745
+ ....: for i in range(2*len(o)-1)]
746
+ sage: la = list(range(len(o)-2, 0, -1))
747
+ sage: G = RuleRSK(labels=l[1:-1], shape=la)
748
+ sage: G.out_labels() == G.conjugate().out_labels()[::-1]
749
+ True
750
+ """
751
+ F = {(j,i): v for (i,j),v in self._filling.items()}
752
+ return GrowthDiagram(self.rule,
753
+ filling=F,
754
+ shape=self.shape().conjugate(),
755
+ labels=self.in_labels()[::-1])
756
+
757
+ def rotate(self):
758
+ r"""
759
+ Return the growth diagram with the filling rotated by 180 degrees.
760
+
761
+ The rotated growth diagram is initialized with
762
+ ``labels=None``, that is, all labels along the boundary on
763
+ the side of the origin are set to ``rule.zero``.
764
+
765
+ For RSK-growth diagrams and rectangular fillings, this
766
+ corresponds to evacuation of the `P`- and the `Q`-symbol.
767
+
768
+ EXAMPLES::
769
+
770
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
771
+ sage: G = GrowthDiagram(RuleRSK, [[0,1,0], [1,0,2]])
772
+ sage: Gc = G.rotate()
773
+ sage: ascii_art([Gc.P_symbol(), Gc.Q_symbol()])
774
+ [ 1 1 1 1 1 2 ]
775
+ [ 2 , 3 ]
776
+
777
+ sage: ascii_art([Tableau(t).evacuation()
778
+ ....: for t in [G.P_symbol(), G.Q_symbol()]])
779
+ [ 1 1 1 1 1 2 ]
780
+ [ 2 , 3 ]
781
+
782
+ TESTS:
783
+
784
+ Check that shape is handled correctly::
785
+
786
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
787
+ sage: G = GrowthDiagram(RuleRSK,
788
+ ....: filling={(0,2):1, (3,1):2, (2,1):3},
789
+ ....: shape=SkewPartition([[5,5,5,3],[3,1]]))
790
+ sage: G
791
+ . . . 0 0
792
+ . 0 3 2 0
793
+ 1 0 0 0 0
794
+ 0 0 0
795
+ sage: G.rotate()
796
+ . . 0 0 0
797
+ 0 0 0 0 1
798
+ 0 2 3 0
799
+ 0 0
800
+ """
801
+ l = self._lambda[0]
802
+ h = len(self._lambda)
803
+ shape_lambda = [l - p for p in self._mu] + [l] * (h - len(self._mu))
804
+ shape_mu = [l - p for p in self._lambda]
805
+ shape = SkewPartition([shape_lambda[::-1], shape_mu[::-1]])
806
+ F = {(l-i-1, h-j-1): v for (i,j),v in self._filling.items()}
807
+ return GrowthDiagram(self.rule,
808
+ filling=F,
809
+ shape=shape)
810
+
811
+ def half_perimeter(self):
812
+ r"""
813
+ Return half the perimeter of the shape of the growth diagram.
814
+
815
+ TESTS::
816
+
817
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
818
+ sage: G = GrowthDiagram(RuleRSK, {(0,1):1, (2,0):1}, SkewPartition([[3,1],[1]])); G
819
+ . 0 1
820
+ 1
821
+ sage: G.half_perimeter()
822
+ 6
823
+ """
824
+ # Assume that ``self._lambda`` is already set.
825
+ if not self._lambda:
826
+ return 1
827
+ return self._lambda[0] + len(self._lambda) + 1
828
+
829
+ def shape(self):
830
+ r"""
831
+ Return the shape of the growth diagram as a skew partition.
832
+
833
+ .. WARNING::
834
+
835
+ In the literature the label on the corner opposite of the
836
+ origin of a rectangular filling is often called the shape
837
+ of the filling. This method returns the shape of the
838
+ region instead.
839
+
840
+ EXAMPLES::
841
+
842
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
843
+ sage: GrowthDiagram(RuleRSK, [1]).shape()
844
+ [1] / []
845
+ """
846
+ return SkewPartition([self._lambda, self._mu])
847
+
848
+ def out_labels(self):
849
+ r"""
850
+ Return the labels along the boundary opposite of the origin.
851
+
852
+ EXAMPLES::
853
+
854
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
855
+ sage: G = GrowthDiagram(RuleRSK, [[0,1,0], [1,0,2]])
856
+ sage: G.out_labels()
857
+ [[], [1], [1, 1], [3, 1], [1], []]
858
+ """
859
+ return self._out_labels
860
+
861
+ def in_labels(self):
862
+ r"""
863
+ Return the labels along the boundary on the side of the origin.
864
+
865
+ EXAMPLES::
866
+
867
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
868
+ sage: G = GrowthDiagram(RuleRSK, labels=[[2,2],[3,2],[3,3],[3,2]]); G
869
+ 1 0
870
+ sage: G.in_labels()
871
+ [[2, 2], [2, 2], [2, 2], [3, 2]]
872
+ """
873
+ return self._in_labels
874
+
875
+ def P_symbol(self):
876
+ r"""
877
+ Return the labels along the vertical boundary of a rectangular
878
+ growth diagram as a generalized standard tableau.
879
+
880
+ EXAMPLES::
881
+
882
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
883
+ sage: G = GrowthDiagram(RuleRSK, [[0,1,0], [1,0,2]])
884
+ sage: ascii_art([G.P_symbol(), G.Q_symbol()])
885
+ [ 1 2 2 1 3 3 ]
886
+ [ 2 , 2 ]
887
+ """
888
+ return self.rule.P_symbol(self.P_chain())
889
+
890
+ def Q_symbol(self):
891
+ r"""
892
+ Return the labels along the horizontal boundary of a rectangular
893
+ growth diagram as a generalized standard tableau.
894
+
895
+ EXAMPLES::
896
+
897
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
898
+ sage: G = GrowthDiagram(RuleRSK, [[0,1,0], [1,0,2]])
899
+ sage: ascii_art([G.P_symbol(), G.Q_symbol()])
900
+ [ 1 2 2 1 3 3 ]
901
+ [ 2 , 2 ]
902
+ """
903
+ return self.rule.Q_symbol(self.Q_chain())
904
+
905
+ def P_chain(self):
906
+ r"""
907
+ Return the labels along the vertical boundary of a rectangular
908
+ growth diagram.
909
+
910
+ EXAMPLES::
911
+
912
+ sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
913
+ sage: G = GrowthDiagram(BinaryWord, [4, 1, 2, 3])
914
+ sage: G.P_chain()
915
+ [word: , word: 1, word: 11, word: 111, word: 1011]
916
+
917
+ Check that :issue:`25631` is fixed::
918
+
919
+ sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
920
+ sage: BinaryWord(filling = {}).P_chain()
921
+ [word: ]
922
+ """
923
+ if not self.is_rectangular():
924
+ raise ValueError("the P symbol is only defined for rectangular shapes")
925
+ if self._lambda:
926
+ if self.rule.has_multiple_edges:
927
+ r = 2*self._lambda[0]
928
+ else:
929
+ r = self._lambda[0]
930
+ else:
931
+ r = 0
932
+ return self._out_labels[r:][::-1]
933
+
934
+ def Q_chain(self):
935
+ r"""
936
+ Return the labels along the horizontal boundary of a rectangular
937
+ growth diagram.
938
+
939
+ EXAMPLES::
940
+
941
+ sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
942
+ sage: G = GrowthDiagram(BinaryWord, [[0,1,0,0], [0,0,1,0], [0,0,0,1], [1,0,0,0]])
943
+ sage: G.Q_chain()
944
+ [word: , word: 1, word: 10, word: 101, word: 1011]
945
+
946
+ Check that :issue:`25631` is fixed::
947
+
948
+ sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
949
+ sage: BinaryWord(filling = {}).Q_chain()
950
+ [word: ]
951
+ """
952
+ if not self.is_rectangular():
953
+ raise ValueError("the Q symbol is only defined for rectangular shapes")
954
+ if self._lambda:
955
+ if self.rule.has_multiple_edges:
956
+ r = 2*self._lambda[0]+1
957
+ else:
958
+ r = self._lambda[0]+1
959
+ else:
960
+ r = 1
961
+ return self._out_labels[:r]
962
+
963
+ def is_rectangular(self):
964
+ r"""
965
+ Return ``True`` if the shape of the growth diagram is rectangular.
966
+
967
+ EXAMPLES::
968
+
969
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
970
+ sage: GrowthDiagram(RuleRSK, [2,3,1]).is_rectangular()
971
+ True
972
+ sage: GrowthDiagram(RuleRSK, [[1,0,1],[0,1]]).is_rectangular()
973
+ False
974
+ """
975
+ return (all(x == 0 for x in self._mu)
976
+ and all(x == self._lambda[0] for x in self._lambda))
977
+
978
+ def to_word(self):
979
+ r"""
980
+ Return the filling as a word, if the shape is rectangular and
981
+ there is at most one nonzero entry in each column, which must
982
+ be 1.
983
+
984
+ EXAMPLES::
985
+
986
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
987
+ sage: w = [3,3,2,4,1]; G = GrowthDiagram(RuleRSK, w)
988
+ sage: G
989
+ 0 0 0 0 1
990
+ 0 0 1 0 0
991
+ 1 1 0 0 0
992
+ 0 0 0 1 0
993
+ sage: G.to_word()
994
+ [3, 3, 2, 4, 1]
995
+ """
996
+ if not self.is_rectangular():
997
+ raise ValueError("can only convert fillings of rectangular shapes to words")
998
+ w = [0] * self._lambda[0]
999
+ for ((i,j), v) in self._filling.items():
1000
+ if v != 0:
1001
+ if v == 1:
1002
+ if w[i] == 0:
1003
+ w[i] = j+1
1004
+ else:
1005
+ raise ValueError("can only convert fillings with at"
1006
+ " most one entry per column to words")
1007
+ elif v == -1:
1008
+ if w[i] == 0:
1009
+ w[i] = -(j+1)
1010
+ else:
1011
+ raise ValueError("can only convert fillings with at"
1012
+ " most one entry per column to words")
1013
+ else:
1014
+ raise ValueError("can only convert 0-1 fillings to words;"
1015
+ " try 'to_biword'")
1016
+ return w
1017
+
1018
+ def to_biword(self):
1019
+ r"""
1020
+ Return the filling as a biword, if the shape is rectangular.
1021
+
1022
+ EXAMPLES::
1023
+
1024
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
1025
+ sage: P = Tableau([[1,2,2],[2]])
1026
+ sage: Q = Tableau([[1,3,3],[2]])
1027
+ sage: bw = RSK_inverse(P, Q); bw
1028
+ [[1, 2, 3, 3], [2, 1, 2, 2]]
1029
+ sage: G = GrowthDiagram(RuleRSK, labels=Q.to_chain()[:-1]+P.to_chain()[::-1]); G
1030
+ 0 1 0
1031
+ 1 0 2
1032
+
1033
+ sage: P = SemistandardTableau([[1, 1, 2], [2]])
1034
+ sage: Q = SemistandardTableau([[1, 2, 2], [2]])
1035
+ sage: G = GrowthDiagram(RuleRSK, labels=Q.to_chain()[:-1]+P.to_chain()[::-1]); G
1036
+ 0 2
1037
+ 1 1
1038
+ sage: G.to_biword()
1039
+ ([1, 2, 2, 2], [2, 1, 1, 2])
1040
+ sage: RSK([1, 2, 2, 2], [2, 1, 1, 2])
1041
+ [[[1, 1, 2], [2]], [[1, 2, 2], [2]]]
1042
+ """
1043
+ if not self.is_rectangular():
1044
+ raise ValueError("can only convert fillings of rectangular shapes to words")
1045
+ w1 = []
1046
+ w2 = []
1047
+ for ((i,j), v) in sorted(self._filling.items()):
1048
+ if v >= 0:
1049
+ w1.extend([i+1]*v)
1050
+ w2.extend([j+1]*v)
1051
+ else:
1052
+ raise ValueError("can only convert fillings with"
1053
+ " nonnegative entries to words")
1054
+ return (w1, w2)
1055
+
1056
+ def __iter__(self):
1057
+ r"""
1058
+ Return the rows of the filling.
1059
+
1060
+ TESTS::
1061
+
1062
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
1063
+ sage: G = GrowthDiagram(RuleRSK, {(0,1):1, (1,0):1}, SkewPartition([[2,1],[1]]))
1064
+ sage: list(G)
1065
+ [[None, 1], [1]]
1066
+
1067
+ sage: pi = Permutation([2,3,1,6,4,5])
1068
+ sage: G = GrowthDiagram(RuleRSK, pi)
1069
+ sage: list(G)
1070
+ [[0, 0, 1, 0, 0, 0],
1071
+ [1, 0, 0, 0, 0, 0],
1072
+ [0, 1, 0, 0, 0, 0],
1073
+ [0, 0, 0, 0, 1, 0],
1074
+ [0, 0, 0, 0, 0, 1],
1075
+ [0, 0, 0, 1, 0, 0]]
1076
+ """
1077
+ return ([None]*self._mu[r] + [self._filling.get((self._mu[r]+j,r), 0)
1078
+ for j in range(self._lambda[r]-self._mu[r])]
1079
+ for r in range(len(self._lambda)))
1080
+
1081
+ def _repr_(self):
1082
+ r"""
1083
+ Return a string with the filling of the growth diagram
1084
+ as a skew tableau.
1085
+
1086
+ TESTS::
1087
+
1088
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
1089
+ sage: GrowthDiagram(RuleRSK, {(0,1):1, (1,0):1}, SkewPartition([[2,1],[1]]))
1090
+ . 1
1091
+ 1
1092
+
1093
+ sage: GrowthDiagram(RuleRSK, {(0,1):1, (2,0):1}, SkewPartition([[3,1],[1]]))
1094
+ . 0 1
1095
+ 1
1096
+ """
1097
+ return SkewTableau(expr=[self._mu,
1098
+ [[self._filling.get((self._mu[r]+j,r), 0)
1099
+ for j in range(self._lambda[r]-self._mu[r])]
1100
+ for r in range(len(self._lambda))][::-1]])._repr_diagram()
1101
+
1102
+ def __eq__(self, other):
1103
+ r"""
1104
+ Return ``True`` if the growth diagram ``other`` has the same
1105
+ shape and the same filling as ``self``.
1106
+
1107
+ EXAMPLES:
1108
+
1109
+ Equality ignores zeros in fillings::
1110
+
1111
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
1112
+ sage: G1 = GrowthDiagram(RuleRSK, {(0, 1): 1, (1, 0): 1})
1113
+ sage: G2 = GrowthDiagram(RuleRSK, {(0, 0): 0, (0, 1): 1, (1, 0): 1})
1114
+ sage: G1 == G2
1115
+ True
1116
+
1117
+ Growth diagrams with different shapes are different::
1118
+
1119
+ sage: G1 = GrowthDiagram(RuleRSK, [[0,1,0],[1,0]])
1120
+ sage: G2 = GrowthDiagram(RuleRSK, [[0,1,0],[1]])
1121
+ sage: G1 == G2
1122
+ False
1123
+
1124
+ Growth diagrams with different rules are different::
1125
+
1126
+ sage: G1 = GrowthDiagram(RuleRSK, {(0, 1): 1, (1, 0): 1})
1127
+ sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
1128
+ sage: G2 = GrowthDiagram(BinaryWord, {(0, 1): 1, (1, 0): 1})
1129
+ sage: G1 == G2
1130
+ False
1131
+ """
1132
+ return (type(self) is type(other) and
1133
+ self.rule == other.rule and
1134
+ self._lambda == other._lambda and
1135
+ self._mu == other._mu and
1136
+ self._filling == other._filling)
1137
+
1138
+ def __ne__(self, other):
1139
+ r"""
1140
+ Return ``True`` if the growth diagram ``other`` does not have the
1141
+ same shape and the same filling as ``self``.
1142
+
1143
+ TESTS:
1144
+
1145
+ Equality ignores zeros in fillings::
1146
+
1147
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
1148
+ sage: G1 = GrowthDiagram(RuleRSK, {(0, 1): 1, (1, 0): 1})
1149
+ sage: G2 = GrowthDiagram(RuleRSK, {(0, 0): 0, (0, 1): 1, (1, 0): 1})
1150
+ sage: G1 != G2
1151
+ False
1152
+
1153
+ Growth diagrams with different shapes are different::
1154
+
1155
+ sage: G1 = GrowthDiagram(RuleRSK, [[0,1,0],[1,0]])
1156
+ sage: G2 = GrowthDiagram(RuleRSK, [[0,1,0],[1]])
1157
+ sage: G1 != G2
1158
+ True
1159
+
1160
+ Growth diagrams with different rules are different::
1161
+
1162
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
1163
+ sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
1164
+ sage: G1 = GrowthDiagram(RuleRSK, {(0, 1): 1, (1, 0): 1})
1165
+ sage: G2 = GrowthDiagram(BinaryWord, {(0, 1): 1, (1, 0): 1})
1166
+ sage: G1 != G2
1167
+ True
1168
+ """
1169
+ return not (self == other)
1170
+
1171
+ def _process_labels(self, labels):
1172
+ r"""
1173
+ Return the list of labels such that each element has the
1174
+ correct type from the rule.
1175
+
1176
+ .. WARNING::
1177
+
1178
+ Assumes that ``self.rule`` is set.
1179
+
1180
+ EXAMPLES::
1181
+
1182
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
1183
+ sage: labels = [[], [1], [1,1], [1], []]
1184
+ sage: G = GrowthDiagram(RuleRSK, labels=labels) # indirect doctest
1185
+ sage: G.out_labels()[2].parent()
1186
+ Partitions
1187
+ """
1188
+ rule = self.rule
1189
+ if rule.has_multiple_edges:
1190
+ return [rule.normalize_vertex(val) if i % 2 == 0 else val
1191
+ for i, val in enumerate(labels)]
1192
+ else:
1193
+ return [rule.normalize_vertex(la) for la in labels]
1194
+
1195
+ def _shape_from_labels(self, labels):
1196
+ r"""
1197
+ Determine the shape of the growth diagram given a list of labels
1198
+ during initialization.
1199
+
1200
+ The shape can be determined from the labels if the size of
1201
+ each label differs from the size of its successor.
1202
+
1203
+ Otherwise raise an error.
1204
+
1205
+ .. WARNING::
1206
+
1207
+ Assumes that ``self.rule`` and ``self.rank`` is set.
1208
+
1209
+ TESTS::
1210
+
1211
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
1212
+ sage: labels = [[],[2],[1],[],[1],[]]
1213
+ sage: G = GrowthDiagram(RuleRSK, labels=labels); G
1214
+ 0 1
1215
+ 1
1216
+ 1
1217
+ sage: G._shape_from_labels(G.out_labels())
1218
+ [2, 1, 1]
1219
+
1220
+ sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
1221
+ sage: Shifted({(0, 0): 1}).out_labels()
1222
+ [[], 1, [1], 0, []]
1223
+ sage: Shifted(labels=[[], 1, [2], 0, []]) # indirect doctest
1224
+ Traceback (most recent call last):
1225
+ ...
1226
+ ValueError: [] has smaller rank than [2] but there is no edge of color 1 in Q
1227
+ """
1228
+ # we can determine the shape even if is_P_edge is not implemented
1229
+ rule = self.rule
1230
+ is_P_edge = getattr(rule, "is_P_edge", None)
1231
+ is_Q_edge = getattr(rule, "is_Q_edge", None)
1232
+ if rule.has_multiple_edges:
1233
+ def right_left_multi(la, mu, e) -> int:
1234
+ if rule.rank(la) < rule.rank(mu):
1235
+ if is_Q_edge is not None and e not in is_Q_edge(la, mu):
1236
+ raise ValueError("%s has smaller rank than %s but there is no edge of color %s in Q" % (la, mu, e))
1237
+ return 1
1238
+ elif rule.rank(la) > rule.rank(mu):
1239
+ if is_P_edge is not None and e not in is_P_edge(mu, la):
1240
+ raise ValueError("%s has smaller rank than %s but there is no edge of color %s in P" % (mu, la, e))
1241
+ return 0
1242
+ raise ValueError("can only determine the shape of the growth"
1243
+ " diagram if ranks of successive labels differ")
1244
+ return _Partitions.from_zero_one([right_left_multi(labels[i], labels[i+2], labels[i+1])
1245
+ for i in range(0, len(labels)-2, 2)])
1246
+ else:
1247
+ def right_left(la, mu) -> int:
1248
+ if rule.rank(la) < rule.rank(mu):
1249
+ if is_Q_edge is not None and not is_Q_edge(la, mu):
1250
+ raise ValueError("%s has smaller rank than %s but is not covered by it in Q" % (la, mu))
1251
+ return 1
1252
+ elif rule.rank(la) > rule.rank(mu):
1253
+ if is_P_edge is not None and not is_P_edge(mu, la):
1254
+ raise ValueError("%s has smaller rank than %s but is not covered by it in P" % (mu, la))
1255
+ return 0
1256
+ raise ValueError("can only determine the shape of the growth"
1257
+ " diagram if ranks of successive labels differ")
1258
+ return _Partitions.from_zero_one([right_left(labels[i], labels[i+1])
1259
+ for i in range(len(labels)-1)])
1260
+
1261
+ def _check_labels(self, labels):
1262
+ r"""
1263
+ Check sanity of the parameter ``labels``.
1264
+
1265
+ .. WARNING::
1266
+
1267
+ Assumes that ``self.rule`` and ``self._lambda`` is set.
1268
+
1269
+ TESTS::
1270
+
1271
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
1272
+ sage: GrowthDiagram(RuleRSK, shape=[1], labels=[[], [1]]) # indirect doctest
1273
+ Traceback (most recent call last):
1274
+ ...
1275
+ ValueError: the number of labels is 2, but for this shape we need 3
1276
+
1277
+ sage: GrowthDiagram(RuleRSK, labels=[[], [1], [2], [2,1]]) # indirect doctest
1278
+ Traceback (most recent call last):
1279
+ ...
1280
+ ValueError: the number of labels is 4, but for this shape we need 1
1281
+
1282
+ .. TODO::
1283
+
1284
+ Can we do something more sensible when the chain
1285
+ of labels is strictly increasing?
1286
+ """
1287
+ half_perimeter = self.half_perimeter()
1288
+ if self.rule.has_multiple_edges:
1289
+ if not (len(labels) % 2):
1290
+ raise ValueError("only a list of odd length can specify a path, but %s has even length" % len(labels))
1291
+ path_length = (len(labels) + 1) / 2
1292
+ else:
1293
+ path_length = len(labels)
1294
+
1295
+ if path_length != half_perimeter:
1296
+ raise ValueError("the number of labels is %s, but for this shape we need %s"
1297
+ % (path_length, half_perimeter))
1298
+
1299
+ def _process_shape(self, shape):
1300
+ r"""
1301
+ Return a pair of partitions as lists describing the region
1302
+ of the growth diagram.
1303
+
1304
+ TESTS:
1305
+
1306
+ ``shape`` is a skew partition::
1307
+
1308
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
1309
+ sage: filling = []
1310
+ sage: shape = SkewPartition([[4,2,1,1],[2,1,1]])
1311
+ sage: G = GrowthDiagram(RuleRSK, filling, shape) # indirect doctest
1312
+ sage: G._lambda, G._mu
1313
+ ([4, 2, 1, 1], [2, 1, 1, 0])
1314
+
1315
+ ``shape`` is a partition::
1316
+
1317
+ sage: filling = []
1318
+ sage: shape = Partition([3,2,1,1])
1319
+ sage: G = GrowthDiagram(RuleRSK, filling, shape) # indirect doctest
1320
+ sage: G._lambda, G._mu
1321
+ ([3, 2, 1, 1], [0, 0, 0, 0])
1322
+ """
1323
+ try:
1324
+ shape = _Partitions(shape)
1325
+ except ValueError:
1326
+ try:
1327
+ shape = SkewPartition(shape)
1328
+ except ValueError:
1329
+ raise ValueError("cannot make sense of shape %s" % shape)
1330
+ return ( list(shape[0]),
1331
+ list(shape[1]) + [0]*(len(shape[0])-len(shape[1])) )
1332
+ return (list(shape), [0]*len(shape))
1333
+
1334
+ def _process_filling_and_shape(self, filling, shape):
1335
+ r"""
1336
+ Return a dict ``F`` such that ``F[(i,j)]`` is the element in row
1337
+ ``i`` and column ``j`` and a pair of partitions describing the
1338
+ region of the growth diagram.
1339
+
1340
+ TESTS:
1341
+
1342
+ ``filling`` is a dict of coordinates::
1343
+
1344
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
1345
+ sage: pi = Permutation([2,3,1,6,4,5])
1346
+ sage: G = GrowthDiagram(RuleRSK, {(i,pi[i]-1):1 for i in range(len(pi))}) # indirect doctest
1347
+ sage: G._filling
1348
+ {(0, 1): 1, (1, 2): 1, (2, 0): 1, (3, 5): 1, (4, 3): 1, (5, 4): 1}
1349
+ sage: G.shape()
1350
+ [6, 6, 6, 6, 6, 6] / []
1351
+
1352
+ ``filling`` is a dict of dicts::
1353
+
1354
+ sage: G = GrowthDiagram(RuleRSK, {i:{pi[i]-1:1} for i in range(len(pi))}) # indirect doctest
1355
+ sage: G._filling
1356
+ {(0, 1): 1, (1, 2): 1, (2, 0): 1, (3, 5): 1, (4, 3): 1, (5, 4): 1}
1357
+ sage: G.shape()
1358
+ [6, 6, 6, 6, 6, 6] / []
1359
+
1360
+ ``filling`` is a matrix::
1361
+
1362
+ sage: G = GrowthDiagram(RuleRSK, pi.to_matrix()) # indirect doctest # needs sage.modules
1363
+ sage: G._filling # needs sage.modules
1364
+ {(0, 1): 1, (1, 2): 1, (2, 0): 1, (3, 5): 1, (4, 3): 1, (5, 4): 1}
1365
+ sage: G.shape() # needs sage.modules
1366
+ [6, 6, 6, 6, 6, 6] / []
1367
+
1368
+ ``filling`` is a permutation::
1369
+
1370
+ sage: G = GrowthDiagram(RuleRSK, pi) # indirect doctest
1371
+ sage: G._filling
1372
+ {(0, 1): 1, (1, 2): 1, (2, 0): 1, (3, 5): 1, (4, 3): 1, (5, 4): 1}
1373
+ sage: G.shape()
1374
+ [6, 6, 6, 6, 6, 6] / []
1375
+
1376
+ ``filling`` is a list::
1377
+
1378
+ sage: G = GrowthDiagram(RuleRSK, [3,1,4,1,5]) # indirect doctest
1379
+ sage: G._filling
1380
+ {(0, 2): 1, (1, 0): 1, (2, 3): 1, (3, 0): 1, (4, 4): 1}
1381
+ sage: G.shape()
1382
+ [5, 5, 5, 5, 5] / []
1383
+
1384
+ ``filling`` is a list of lists::
1385
+
1386
+ sage: G = GrowthDiagram(RuleRSK, [[1,0,1],[0,1]]) # indirect doctest
1387
+ sage: G._filling
1388
+ {(0, 0): 1, (1, 1): 1, (2, 0): 1}
1389
+ sage: G.shape()
1390
+ [3, 2] / []
1391
+
1392
+ ``filling`` is a list of lists and shape is given::
1393
+
1394
+ sage: G = GrowthDiagram(RuleRSK, [[1,0,1],[0,1]], # indirect doctest
1395
+ ....: shape=SkewPartition([[3,2],[1]]))
1396
+ sage: G._filling
1397
+ {(0, 0): 1, (1, 1): 1, (2, 0): 1}
1398
+ sage: G.shape()
1399
+ [3, 2] / [1]
1400
+
1401
+ ``filling`` is empty and shape is ``None``::
1402
+
1403
+ sage: G = GrowthDiagram(RuleRSK, {})
1404
+ sage: (G.filling(), G.shape())
1405
+ ({}, [] / [])
1406
+ """
1407
+ if isinstance(filling, dict):
1408
+ try:
1409
+ v = next(iter(filling.values()))
1410
+ if isinstance(v, dict):
1411
+ # it is a dict of dicts
1412
+ F = dict()
1413
+ for (i, row) in filling.items():
1414
+ for (j, v) in row.items():
1415
+ if v != 0:
1416
+ F[(i,j)] = int(v)
1417
+ else:
1418
+ # it is dict of coordinates
1419
+ F = {(i,j): v for ((i,j), v) in filling.items()
1420
+ if v != 0}
1421
+ except StopIteration:
1422
+ # it is an empty dict of coordinates
1423
+ F = filling
1424
+
1425
+ else:
1426
+ # it is a sequence
1427
+ F = dict()
1428
+ try:
1429
+ # it is a sequence of sequences
1430
+ for i, row in enumerate(filling):
1431
+ for j, v in enumerate(row):
1432
+ if v != 0:
1433
+ F[j,i] = int(v)
1434
+ if shape is None:
1435
+ shape = [len(row) for row in filling]
1436
+
1437
+ except TypeError:
1438
+ # it is a word - for convenience we allow signed words
1439
+ for i, l in enumerate(filling):
1440
+ if l > 0:
1441
+ F[i, l-1] = 1
1442
+ else:
1443
+ F[i, -l-1] = -1
1444
+
1445
+ if shape is None:
1446
+ if F == {}:
1447
+ shape = []
1448
+ else:
1449
+ # find bounding rectangle of ``filling``
1450
+ max_row = max(i for i, _ in F)+1
1451
+ max_col = max(j for _, j in F)+1
1452
+ shape = [max_row] * max_col
1453
+
1454
+ return (F, self._process_shape(shape))
1455
+
1456
+ def _grow(self):
1457
+ r"""
1458
+ Compute the labels on the boundary opposite of the origin, given
1459
+ the filling.
1460
+
1461
+ TESTS::
1462
+
1463
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
1464
+ sage: pi = Permutation([1])
1465
+ sage: G = GrowthDiagram(RuleRSK, pi) # indirect doctest
1466
+ sage: G._out_labels
1467
+ [[], [1], []]
1468
+
1469
+ sage: pi = Permutation([1,2])
1470
+ sage: G = GrowthDiagram(RuleRSK, pi) # indirect doctest
1471
+ sage: G._out_labels
1472
+ [[], [1], [2], [1], []]
1473
+
1474
+ sage: pi = Permutation([2,1])
1475
+ sage: G = GrowthDiagram(RuleRSK, pi) # indirect doctest
1476
+ sage: G._out_labels
1477
+ [[], [1], [1, 1], [1], []]
1478
+
1479
+ sage: G = GrowthDiagram(RuleRSK, {(0,1):1, (1,0):1}, SkewPartition([[2,1],[1]])) # indirect doctest
1480
+ sage: G._out_labels
1481
+ [[], [1], [], [1], []]
1482
+
1483
+ sage: G = GrowthDiagram(RuleRSK, {(1,1):1}, SkewPartition([[2,2],[1]]), labels=[[],[],[1],[],[]]) # indirect doctest
1484
+ sage: G._out_labels
1485
+ [[], [1], [2], [1], []]
1486
+
1487
+ sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
1488
+ sage: G = GrowthDiagram(BinaryWord, {(1,1):1}, SkewPartition([[2,2],[1]]), labels=[[],[],[1],[],[]]) # indirect doctest
1489
+ sage: G._out_labels
1490
+ [word: , word: 1, word: 11, word: 1, word: ]
1491
+ """
1492
+ labels = list(self._in_labels)
1493
+ l = len(self._lambda)
1494
+ rule = self.rule
1495
+ if rule.has_multiple_edges:
1496
+ for r in range(l):
1497
+ for c in range(self._mu[r]+l-r, self._lambda[r]+l-r):
1498
+ j = r
1499
+ i = c-l+r
1500
+ (labels[2*c-1],
1501
+ labels[2*c],
1502
+ labels[2*c+1]) = rule.forward_rule(labels[2*c-2],
1503
+ labels[2*c-1],
1504
+ labels[2*c],
1505
+ labels[2*c+1],
1506
+ labels[2*c+2],
1507
+ self._filling.get((i,j), 0))
1508
+ else:
1509
+ for r in range(l):
1510
+ for c in range(self._mu[r]+l-r, self._lambda[r]+l-r):
1511
+ j = r
1512
+ i = c-l+r
1513
+ labels[c] = rule.forward_rule(labels[c-1],
1514
+ labels[c],
1515
+ labels[c+1],
1516
+ self._filling.get((i,j), 0))
1517
+
1518
+ self._out_labels = labels
1519
+
1520
+ def _shrink(self):
1521
+ r"""
1522
+ Compute the labels on the boundary near the origin, and the filling.
1523
+
1524
+ TESTS::
1525
+
1526
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
1527
+ sage: filling = [[0,0,1,0,0,0,0], [0,1,0,0,0,0,0], [1,0,0,0,0,0,0],
1528
+ ....: [0,0,0,1,0,0,0], [0,0,0,0,0,0,1],
1529
+ ....: [0,0,0,0,0,1,0], [0,0,0,0,1,0,0]]
1530
+ sage: G = GrowthDiagram(RuleRSK, filling)
1531
+ sage: list(GrowthDiagram(RuleRSK, labels=G._out_labels)) == filling
1532
+ True
1533
+
1534
+ sage: labels = [[], [1], []]
1535
+ sage: G = GrowthDiagram(RuleRSK, labels=labels) # indirect doctest
1536
+ sage: G._filling
1537
+ {(0, 0): 1}
1538
+ sage: G._in_labels
1539
+ [[], [], []]
1540
+
1541
+ sage: labels = [[], [1], [2], [2,1], [1,1], [1], []]
1542
+ sage: G = GrowthDiagram(RuleRSK, labels=labels) # indirect doctest
1543
+ sage: G._filling
1544
+ {(0, 1): 1, (1, 2): 1, (2, 0): 1}
1545
+ sage: G._in_labels
1546
+ [[], [], [], [], [], [], []]
1547
+
1548
+ sage: labels = [[], [1], [2], [3], [3, 1], [3, 2], [4, 2], [4, 1], [3, 1], [2, 1], [1, 1], [1], []]
1549
+ sage: G = GrowthDiagram(RuleRSK, labels=labels) # indirect doctest
1550
+ sage: G._filling
1551
+ {(0, 1): 1, (1, 2): 1, (2, 5): 1, (3, 0): 1, (4, 3): 1, (5, 4): 1}
1552
+
1553
+ sage: labels = [[],[1],[1],[2],[2],[2,1],[2]]
1554
+ sage: G = GrowthDiagram(RuleRSK, labels=labels)
1555
+ Traceback (most recent call last):
1556
+ ...
1557
+ ValueError: can only determine the shape of the growth diagram
1558
+ if ranks of successive labels differ
1559
+ sage: G = GrowthDiagram(RuleRSK, shape=[3,2,1], labels=labels) # indirect doctest
1560
+ sage: G._filling
1561
+ {(1, 0): 1}
1562
+ sage: G._in_labels
1563
+ [[], [], [], [], [1], [1], [2]]
1564
+
1565
+ sage: labels = [[], [1],[1],[2],[2],[2,1],[2],[2,1],[1,1],[2,1],[1,1]]
1566
+ sage: G = GrowthDiagram(RuleRSK, shape=[5,4,3,2,1], labels=labels) # indirect doctest
1567
+ sage: G._filling
1568
+ {(1, 2): 1, (2, 1): 1, (4, 0): 1}
1569
+ sage: G._in_labels
1570
+ [[], [], [], [], [], [], [1], [1], [1], [1, 1], [1, 1]]
1571
+
1572
+ sage: labels = [[], [1],[1],[2],[2],[2,1],[2],[2,1],[1,1],[2,1],[1,1]]
1573
+ sage: G = GrowthDiagram(RuleRSK, shape=SkewPartition([[5,4,3,2,1],[3,2,1]]), labels=labels) # indirect doctest
1574
+ sage: G._filling
1575
+ {(1, 2): 1, (2, 1): 1, (4, 0): 1}
1576
+ sage: G._in_labels
1577
+ [[], [], [], [1], [1], [1], [1], [1], [1], [1, 1], [1, 1]]
1578
+ """
1579
+ F = dict()
1580
+ labels = list(self._out_labels)
1581
+ l = len(self._lambda)
1582
+ rule = self.rule
1583
+ if rule.has_multiple_edges:
1584
+ for r in range(l):
1585
+ for c in range(self._lambda[l-r-1]+r, self._mu[l-r-1]+r, -1):
1586
+ j = l-r-1
1587
+ i = c-r-1
1588
+ (labels[2*c-1],
1589
+ labels[2*c],
1590
+ labels[2*c+1], v) = rule.backward_rule(labels[2*c-2],
1591
+ labels[2*c-1],
1592
+ labels[2*c],
1593
+ labels[2*c+1],
1594
+ labels[2*c+2])
1595
+ if v != 0:
1596
+ F[(i,j)] = v
1597
+
1598
+ else:
1599
+ for r in range(l):
1600
+ for c in range(self._lambda[l-r-1]+r, self._mu[l-r-1]+r, -1):
1601
+ j = l-r-1
1602
+ i = c-r-1
1603
+ labels[c], v = rule.backward_rule(labels[c-1],
1604
+ labels[c],
1605
+ labels[c+1])
1606
+ if v != 0:
1607
+ F[(i,j)] = v
1608
+
1609
+ self._in_labels = labels
1610
+ self._filling = F
1611
+
1612
+ ######################################################################
1613
+ # ABC for rules of growth diagrams
1614
+ ######################################################################
1615
+
1616
+
1617
+ class Rule(UniqueRepresentation):
1618
+ r"""
1619
+ Generic base class for a rule for a growth diagram.
1620
+
1621
+ Subclasses may provide the following attributes:
1622
+
1623
+ - ``zero`` -- the zero element of the vertices of the graphs
1624
+
1625
+ - ``r`` -- (default: 1) the parameter in the equation `DU - UD = rI`
1626
+
1627
+ - ``has_multiple_edges`` -- boolean (default: ``False``); if the dual
1628
+ graded graph has multiple edges and therefore edges are
1629
+ triples consisting of two vertices and a label
1630
+
1631
+ - ``zero_edge`` -- (default: 0) the zero label of the
1632
+ edges of the graphs used for degenerate edges. It is
1633
+ allowed to use this label also for other edges.
1634
+
1635
+ Subclasses may provide the following methods:
1636
+
1637
+ - ``normalize_vertex`` -- a function that converts its input to a
1638
+ vertex
1639
+
1640
+ - ``vertices`` -- a function that takes a nonnegative integer
1641
+ as input and returns the list of vertices on this rank
1642
+
1643
+ - ``rank`` -- the rank function of the dual graded graphs
1644
+
1645
+ - ``forward_rule`` -- a function with input ``(y, t, x,
1646
+ content)`` or ``(y, e, t, f, x, content)`` if
1647
+ ``has_multiple_edges`` is ``True``. ``(y, e, t)`` is an
1648
+ edge in the graph `P`, ``(t, f, x)`` an edge in the graph
1649
+ ``Q``. It should return the fourth vertex ``z``, or, if
1650
+ ``has_multiple_edges`` is ``True``, the path ``(g, z, h)``
1651
+ from ``y`` to ``x``.
1652
+
1653
+ - ``backward_rule`` -- a function with input ``(y, z, x)`` or
1654
+ ``(y, g, z, h, x)`` if ``has_multiple_edges`` is ``True``.
1655
+ ``(y, g, z)`` is an edge in the graph `Q`, ``(z, h, x)`` an
1656
+ edge in the graph ``P``. It should return the fourth
1657
+ vertex and the content ``(t, content)``, or, if
1658
+ ``has_multiple_edges`` is ``True``, the path from ``y`` to
1659
+ ``x`` and the content as ``(e, t, f, content)``.
1660
+
1661
+ - ``is_P_edge``, ``is_Q_edge`` -- functions that take two
1662
+ vertices as arguments and return ``True`` or ``False``, or,
1663
+ if multiple edges are allowed, the list of edge labels of
1664
+ the edges from the first vertex to the second in the
1665
+ respective graded graph. These are only used for checking
1666
+ user input and providing the dual graded graph, and are
1667
+ therefore not mandatory.
1668
+
1669
+ Note that the class :class:`GrowthDiagram` is able to use
1670
+ partially implemented subclasses just fine. Suppose that
1671
+ ``MyRule`` is such a subclass. Then:
1672
+
1673
+ - ``GrowthDiagram(MyRule, my_filling)`` requires only an
1674
+ implementation of ``forward_rule``, ``zero`` and possibly
1675
+ ``has_multiple_edges``.
1676
+
1677
+ - ``GrowthDiagram(MyRule, labels=my_labels, shape=my_shape)``
1678
+ requires only an implementation of ``backward_rule`` and
1679
+ possibly ``has_multiple_edges``, provided that the labels
1680
+ ``my_labels`` are given as needed by ``backward_rule``.
1681
+
1682
+ - ``GrowthDiagram(MyRule, labels=my_labels)`` additionally needs
1683
+ an implementation of ``rank`` to deduce the shape.
1684
+
1685
+ In particular, this allows to implement rules which do not quite
1686
+ fit Fomin's notion of dual graded graphs. An example would be
1687
+ Bloom and Saracino's variant of the RSK correspondence [BS2012]_,
1688
+ where a backward rule is not available.
1689
+
1690
+ Similarly:
1691
+
1692
+ - ``MyRule.P_graph`` only requires an implementation of
1693
+ ``vertices``, ``is_P_edge`` and possibly ``has_multiple_edges``
1694
+ is required, mutatis mutandis for ``MyRule.Q_graph``.
1695
+
1696
+ - ``MyRule._check_duality`` requires ``P_graph`` and ``Q_graph``.
1697
+
1698
+ In particular, this allows to work with dual graded graphs
1699
+ without local rules.
1700
+ """
1701
+ has_multiple_edges = False # override when necessary
1702
+ zero_edge = 0 # override when necessary
1703
+ r = 1 # override when necessary
1704
+
1705
+ def normalize_vertex(self, v): # override when necessary
1706
+ r"""
1707
+ Return ``v`` as a vertex of the dual graded graph.
1708
+
1709
+ This is a default implementation, returning its argument.
1710
+
1711
+ EXAMPLES::
1712
+
1713
+ sage: from sage.combinat.growth import Rule
1714
+ sage: Rule().normalize_vertex("hello") == "hello"
1715
+ True
1716
+ """
1717
+ return v
1718
+
1719
+ def __call__(self, *args, **kwds):
1720
+ r"""
1721
+ Return the growth diagram corresponding to the parameters.
1722
+
1723
+ This provides a shorthand for calling :class:`GrowthDiagram`
1724
+ directly.
1725
+
1726
+ EXAMPLES::
1727
+
1728
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
1729
+ sage: RuleRSK([2,3,1], shape=[3,2,2])
1730
+ 0 0 1
1731
+ 1 0
1732
+ 0 1
1733
+
1734
+ sage: RuleRSK(labels=[[], [1], [2], [1], [], [1], []])
1735
+ 0 0 1
1736
+ 1 0
1737
+ 0 1
1738
+ """
1739
+ return GrowthDiagram(self, *args, **kwds)
1740
+
1741
+ def _check_duality(self, n):
1742
+ r"""
1743
+ Raise an error if the graphs are not `r`-dual at level ``n``.
1744
+
1745
+ `P` and `Q` are `r`-dual if `DU = UD + rI` on the free
1746
+ `\ZZ`-module `\ZZ[V]`, where `D` is the down operator of `Q`,
1747
+ assigning to each vertex the formal sum of its predecessors,
1748
+ `U` is the up operator of `P`, assigning to each vertex the
1749
+ formal sum of its successors, and `I` is the identity
1750
+ operator.
1751
+
1752
+ INPUT:
1753
+
1754
+ - ``n`` -- positive integer specifying which rank of
1755
+ the graph to test
1756
+
1757
+ EXAMPLES:
1758
+
1759
+ For binary words, we have indeed provided dual graded graphs::
1760
+
1761
+ sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
1762
+ sage: BinaryWord._check_duality(3)
1763
+
1764
+ The following two graphs are not `1`-dual::
1765
+
1766
+ sage: from sage.combinat.growth import Rule
1767
+ sage: class RuleWrong(Rule):
1768
+ ....: def vertices(self, n): return Partitions(n)
1769
+ ....: def is_Q_edge(self, v, w):
1770
+ ....: return (v, w) in [([1],[2]), ([2],[3])]
1771
+ ....: def is_P_edge(self, v, w):
1772
+ ....: return (v, w) in [([1],[2]), ([1],[1,1]), ([2],[3])]
1773
+
1774
+ sage: RuleWrong()._check_duality(2)
1775
+ Traceback (most recent call last):
1776
+ ...
1777
+ ValueError: D U - U D differs from 1 I for vertex [2]:
1778
+ D U = [[2]]
1779
+ U D + 1 I = [[1, 1], [2], [2]]
1780
+ """
1781
+ if self.has_multiple_edges:
1782
+ def check_vertex(w, P, Q):
1783
+ DUw = [v[0] for uw in P.outgoing_edges(w) for v in Q.incoming_edges(uw[1])]
1784
+ UDw = [v[1] for lw in Q.incoming_edges(w) for v in P.outgoing_edges(lw[0])]
1785
+ UDw.extend([w]*self.r)
1786
+ if sorted(DUw) != sorted(UDw):
1787
+ raise ValueError("D U - U D differs from %s I for vertex %s:\n"
1788
+ "D U = %s\n"
1789
+ "U D + %s I = %s"
1790
+ % (self.r, w, DUw, self.r, UDw))
1791
+ else:
1792
+ def check_vertex(w, P, Q):
1793
+ DUw = [v for uw in P.upper_covers(w) for v in Q.lower_covers(uw)]
1794
+ UDw = [v for lw in Q.lower_covers(w) for v in P.upper_covers(lw)]
1795
+ UDw.extend([w]*self.r)
1796
+ if sorted(DUw) != sorted(UDw):
1797
+ raise ValueError("D U - U D differs from %s I for vertex %s:\n"
1798
+ "D U = %s\n"
1799
+ "U D + %s I = %s"
1800
+ % (self.r, w, DUw, self.r, UDw))
1801
+
1802
+ P = self.P_graph(n + 2)
1803
+ Q = self.Q_graph(n + 2)
1804
+ for w in self.vertices(n):
1805
+ check_vertex(w, P, Q)
1806
+
1807
+ def P_graph(self, n):
1808
+ r"""
1809
+ Return the first ``n`` levels of the first dual graded graph.
1810
+
1811
+ The non-degenerate edges in the vertical direction come from
1812
+ this graph.
1813
+
1814
+ EXAMPLES::
1815
+
1816
+ sage: Domino = GrowthDiagram.rules.Domino()
1817
+ sage: Domino.P_graph(3)
1818
+ Finite poset containing 8 elements
1819
+ """
1820
+ if self.has_multiple_edges:
1821
+ D = DiGraph([(x,y,e) for k in range(n-1)
1822
+ for x in self.vertices(k)
1823
+ for y in self.vertices(k+1)
1824
+ for e in self.is_P_edge(x, y)], multiedges=True)
1825
+ # unfortunately, layout_acyclic will not show multiple edges
1826
+ # D.layout_default = D.layout_acyclic
1827
+ return D
1828
+ else:
1829
+ return Poset(([w for k in range(n) for w in self.vertices(k)],
1830
+ self.is_P_edge), cover_relations=True)
1831
+
1832
+ def Q_graph(self, n):
1833
+ r"""
1834
+ Return the first ``n`` levels of the second dual graded graph.
1835
+
1836
+ The non-degenerate edges in the horizontal direction come
1837
+ from this graph.
1838
+
1839
+ EXAMPLES::
1840
+
1841
+ sage: Domino = GrowthDiagram.rules.Domino()
1842
+ sage: Q = Domino.Q_graph(3); Q
1843
+ Finite poset containing 8 elements
1844
+
1845
+ sage: Q.upper_covers(Partition([1,1]))
1846
+ [[1, 1, 1, 1], [3, 1], [2, 2]]
1847
+ """
1848
+ if self.has_multiple_edges:
1849
+ D = DiGraph([(x, y, e) for k in range(n - 1)
1850
+ for x in self.vertices(k)
1851
+ for y in self.vertices(k + 1)
1852
+ for e in self.is_Q_edge(x, y)], multiedges=True)
1853
+ # unfortunately, layout_acyclic will not show multiple edges
1854
+ # D.layout_default = D.layout_acyclic
1855
+ return D
1856
+ else:
1857
+ return Poset(([w for k in range(n) for w in self.vertices(k)],
1858
+ self.is_Q_edge), cover_relations=True)
1859
+
1860
+ ######################################################################
1861
+ # Specific rules of growth diagrams
1862
+ ######################################################################
1863
+
1864
+
1865
+ class RuleShiftedShapes(Rule):
1866
+ r"""
1867
+ A class modelling the Schensted correspondence for shifted
1868
+ shapes.
1869
+
1870
+ This agrees with Sagan [Sag1987]_ and Worley's [Wor1984]_, and
1871
+ Haiman's [Hai1989]_ insertion algorithms, see Proposition 4.5.2
1872
+ of [Fom1995]_.
1873
+
1874
+ EXAMPLES::
1875
+
1876
+ sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
1877
+ sage: GrowthDiagram(Shifted, [3,1,2])
1878
+ 0 1 0
1879
+ 0 0 1
1880
+ 1 0 0
1881
+
1882
+ The vertices of the dual graded graph are shifted shapes::
1883
+
1884
+ sage: Shifted.vertices(3)
1885
+ Partitions of the integer 3 satisfying constraints max_slope=-1
1886
+
1887
+ Let us check the example just before Corollary 3.2 in [Sag1987]_.
1888
+ Note that, instead of passing the rule to :class:`GrowthDiagram`,
1889
+ we can also call the rule to create growth diagrams::
1890
+
1891
+ sage: G = Shifted([2,6,5,1,7,4,3])
1892
+ sage: G.P_chain()
1893
+ [[], 0, [1], 0, [2], 0, [3], 0, [3, 1], 0, [3, 2], 0, [4, 2], 0, [5, 2]]
1894
+ sage: G.Q_chain()
1895
+ [[], 1, [1], 2, [2], 1, [2, 1], 3, [3, 1], 2, [4, 1], 3, [4, 2], 3, [5, 2]]
1896
+
1897
+ TESTS::
1898
+
1899
+ sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
1900
+ sage: Shifted.zero
1901
+ []
1902
+
1903
+ sage: Shifted._check_duality(4)
1904
+
1905
+ Check that the rules are bijective::
1906
+
1907
+ sage: all(Shifted(labels=Shifted(pi).out_labels()).to_word() == pi
1908
+ ....: for pi in Permutations(5))
1909
+ True
1910
+ sage: pi = Permutations(10).random_element()
1911
+ sage: G = Shifted(pi)
1912
+ sage: list(Shifted(labels=G.out_labels())) == list(G)
1913
+ True
1914
+ """
1915
+ zero = _make_partition([])
1916
+ has_multiple_edges = True
1917
+
1918
+ def normalize_vertex(self, v):
1919
+ r"""
1920
+ Return ``v`` as a partition.
1921
+
1922
+ EXAMPLES::
1923
+
1924
+ sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
1925
+ sage: Shifted.normalize_vertex([3,1]).parent()
1926
+ Partitions
1927
+ """
1928
+ return _make_partition(v)
1929
+
1930
+ def vertices(self, n):
1931
+ r"""
1932
+ Return the vertices of the dual graded graph on level ``n``.
1933
+
1934
+ EXAMPLES::
1935
+
1936
+ sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
1937
+ sage: Shifted.vertices(3)
1938
+ Partitions of the integer 3 satisfying constraints max_slope=-1
1939
+ """
1940
+ if n == 0:
1941
+ return [self.zero]
1942
+ else:
1943
+ return Partitions(n, max_slope=-1)
1944
+
1945
+ def rank(self, v):
1946
+ r"""
1947
+ Return the rank of ``v``: the size of the shifted partition.
1948
+
1949
+ EXAMPLES::
1950
+
1951
+ sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
1952
+ sage: Shifted.rank(Shifted.vertices(3)[0])
1953
+ 3
1954
+ """
1955
+ return v.size()
1956
+
1957
+ def is_Q_edge(self, v, w):
1958
+ r"""
1959
+ Return whether ``(v, w)`` is a `Q`-edge of ``self``.
1960
+
1961
+ ``(v, w)`` is an edge if ``w`` is obtained from ``v`` by adding a
1962
+ cell. It is a black (color 1) edge, if the cell is on the
1963
+ diagonal, otherwise it can be blue or red (color 2 or 3).
1964
+
1965
+ EXAMPLES::
1966
+
1967
+ sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
1968
+ sage: v = Shifted.vertices(2)[0]; v
1969
+ [2]
1970
+ sage: [(w, Shifted.is_Q_edge(v, w)) for w in Shifted.vertices(3)]
1971
+ [([3], [2, 3]), ([2, 1], [1])]
1972
+ sage: all(Shifted.is_Q_edge(v, w) == [] for w in Shifted.vertices(4))
1973
+ True
1974
+ """
1975
+ if self.rank(v) + 1 != self.rank(w):
1976
+ return []
1977
+ try:
1978
+ l = SkewPartition([w, v]).cells()
1979
+ except ValueError:
1980
+ return []
1981
+ else:
1982
+ if l[0][1] == 0:
1983
+ return [1] # black
1984
+ else:
1985
+ return [2,3] # blue, red
1986
+
1987
+ def is_P_edge(self, v, w):
1988
+ r"""
1989
+ Return whether ``(v, w)`` is a `P`-edge of ``self``.
1990
+
1991
+ ``(v, w)`` is an edge if ``w`` contains ``v``.
1992
+
1993
+ EXAMPLES::
1994
+
1995
+ sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
1996
+ sage: v = Shifted.vertices(2)[0]; v
1997
+ [2]
1998
+ sage: [w for w in Shifted.vertices(3) if Shifted.is_P_edge(v, w)]
1999
+ [[3], [2, 1]]
2000
+ """
2001
+ if self.rank(v) + 1 != self.rank(w):
2002
+ return []
2003
+ return [0] if w.contains(v) else []
2004
+
2005
+ def P_symbol(self, P_chain):
2006
+ r"""
2007
+ Return the labels along the vertical boundary of a rectangular
2008
+ growth diagram as a shifted tableau.
2009
+
2010
+ EXAMPLES:
2011
+
2012
+ Check the example just before Corollary 3.2 in [Sag1987]_::
2013
+
2014
+ sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
2015
+ sage: G = Shifted([2,6,5,1,7,4,3])
2016
+ sage: G.P_symbol().pp()
2017
+ 1 2 3 6 7
2018
+ 4 5
2019
+
2020
+ Check the example just before Corollary 8.2 in [SS1990]_::
2021
+
2022
+ sage: T = ShiftedPrimedTableau([[4],[1],[5]], skew=[3,1])
2023
+ sage: T.pp()
2024
+ . . . 4
2025
+ . 1
2026
+ 5
2027
+ sage: U = ShiftedPrimedTableau([[1],[3.5],[5]], skew=[3,1])
2028
+ sage: U.pp()
2029
+ . . . 1
2030
+ . 4'
2031
+ 5
2032
+ sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
2033
+ sage: labels = [mu if is_even(i) else 0
2034
+ ....: for i, mu in enumerate(T.to_chain()[::-1])] + U.to_chain()[1:]
2035
+ sage: G = Shifted({(1,2):1, (2,1):1}, shape=[5,5,5,5,5], labels=labels)
2036
+ sage: G.P_symbol().pp()
2037
+ . . . . 2
2038
+ . . 1 3
2039
+ . 4 5
2040
+ """
2041
+ chain = P_chain[::2]
2042
+ shape = chain[-1]
2043
+ T = [[None for _ in range(r)] for r in shape]
2044
+ for i in range(1,len(chain)):
2045
+ la = chain[i]
2046
+ mu = chain[i-1]
2047
+ mu += [0]*(len(la) - len(mu))
2048
+
2049
+ for r in range(len(la)):
2050
+ for c in range(mu[r], la[r]):
2051
+ T[r][c] = i
2052
+
2053
+ skew = _make_partition([row.count(None) for row in T])
2054
+ T = [[e for e in row if e is not None] for row in T]
2055
+ return ShiftedPrimedTableau(T, skew=skew)
2056
+
2057
+ def Q_symbol(self, Q_chain):
2058
+ r"""
2059
+ Return the labels along the horizontal boundary of a rectangular
2060
+ growth diagram as a skew tableau.
2061
+
2062
+ EXAMPLES:
2063
+
2064
+ Check the example just before Corollary 3.2 in [Sag1987]_::
2065
+
2066
+ sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
2067
+ sage: G = Shifted([2,6,5,1,7,4,3])
2068
+ sage: G.Q_symbol().pp()
2069
+ 1 2 4' 5 7'
2070
+ 3 6'
2071
+
2072
+ Check the example just before Corollary 8.2 in [SS1990]_::
2073
+
2074
+ sage: T = ShiftedPrimedTableau([[4],[1],[5]], skew=[3,1])
2075
+ sage: T.pp()
2076
+ . . . 4
2077
+ . 1
2078
+ 5
2079
+ sage: U = ShiftedPrimedTableau([[1],[3.5],[5]], skew=[3,1])
2080
+ sage: U.pp()
2081
+ . . . 1
2082
+ . 4'
2083
+ 5
2084
+ sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
2085
+ sage: labels = [mu if is_even(i) else 0
2086
+ ....: for i, mu in enumerate(T.to_chain()[::-1])] + U.to_chain()[1:]
2087
+ sage: G = Shifted({(1,2):1, (2,1):1}, shape=[5,5,5,5,5], labels=labels)
2088
+ sage: G.Q_symbol().pp()
2089
+ . . . . 2
2090
+ . . 1 4'
2091
+ . 3' 5'
2092
+ """
2093
+ chain = Q_chain
2094
+ shape = chain[-1]
2095
+ T = [[None for _ in range(r)] for r in shape]
2096
+ for i in range(1,(len(chain)+1)//2):
2097
+ la = chain[2*i]
2098
+ if chain[2*i-1] == 3:
2099
+ prime = 0.5
2100
+ else:
2101
+ prime = 0
2102
+ mu = chain[2*(i-1)]
2103
+ mu += [0]*(len(la) - len(mu))
2104
+
2105
+ for r in range(len(la)):
2106
+ for c in range(mu[r], la[r]):
2107
+ T[r][c] = i - prime
2108
+
2109
+ skew = _make_partition([row.count(None) for row in T])
2110
+ T = [[e for e in row if e is not None] for row in T]
2111
+ return ShiftedPrimedTableau(T, skew=skew)
2112
+
2113
+ def forward_rule(self, y, e, t, f, x, content):
2114
+ r"""
2115
+ Return the output path given two incident edges and the content.
2116
+
2117
+ See [Fom1995]_ Lemma 4.5.1, page 38.
2118
+
2119
+ INPUT:
2120
+
2121
+ - ``y``, ``e``, ``t``, ``f``, ``x`` -- a path of three partitions and
2122
+ two colors from a cell in a growth diagram, labelled as::
2123
+
2124
+ t f x
2125
+ e
2126
+ y
2127
+
2128
+ - ``content`` -- `0` or `1`; the content of the cell
2129
+
2130
+ OUTPUT:
2131
+
2132
+ The two colors and the fourth partition ``g``, ``z``, ``h``
2133
+ according to Sagan-Worley insertion.
2134
+
2135
+ EXAMPLES::
2136
+
2137
+ sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
2138
+ sage: Shifted.forward_rule([], 0, [], 0, [], 1)
2139
+ (1, [1], 0)
2140
+
2141
+ sage: Shifted.forward_rule([1], 0, [1], 0, [1], 1)
2142
+ (2, [2], 0)
2143
+
2144
+ if ``x != y``::
2145
+
2146
+ sage: Shifted.forward_rule([3], 0, [2], 1, [2,1], 0)
2147
+ (1, [3, 1], 0)
2148
+
2149
+ sage: Shifted.forward_rule([2,1], 0, [2], 2, [3], 0)
2150
+ (2, [3, 1], 0)
2151
+
2152
+ if ``x == y != t``::
2153
+
2154
+ sage: Shifted.forward_rule([3], 0, [2], 2, [3], 0)
2155
+ (1, [3, 1], 0)
2156
+
2157
+ sage: Shifted.forward_rule([3,1], 0, [2,1], 2, [3,1], 0)
2158
+ (2, [3, 2], 0)
2159
+
2160
+ sage: Shifted.forward_rule([2,1], 0, [2], 1, [2,1], 0)
2161
+ (3, [3, 1], 0)
2162
+
2163
+ sage: Shifted.forward_rule([3], 0, [2], 3, [3], 0)
2164
+ (3, [4], 0)
2165
+ """
2166
+ if e != 0:
2167
+ raise ValueError("the P-graph should not be colored")
2168
+ h = 0
2169
+ if x == t == y:
2170
+ if f != 0:
2171
+ raise ValueError("degenerate edge f should have color 0")
2172
+ if content == 0:
2173
+ g, z = 0, x
2174
+ elif content == 1:
2175
+ if not x:
2176
+ g, z = 1, _Partitions(x).add_cell(0) # black
2177
+ else:
2178
+ g, z = 2, _make_partition(x).add_cell(0) # blue
2179
+ else:
2180
+ raise NotImplementedError
2181
+ elif content != 0:
2182
+ raise ValueError("for y=%s, t=%s, x=%s, the content should be 0 but is %s"
2183
+ % (y, t, x, content))
2184
+ elif x != t == y:
2185
+ g, z = f, x
2186
+ elif x == t != y:
2187
+ if f != 0:
2188
+ raise ValueError("degenerate edge f should have color 0")
2189
+ g, z = f, y
2190
+ else:
2191
+ if x != y:
2192
+ row = SkewPartition([x, t]).cells()[0][0]
2193
+ g, z = f, _make_partition(y).add_cell(row)
2194
+ elif x == y != t and f == 2: # blue
2195
+ row = 1+SkewPartition([x, t]).cells()[0][0]
2196
+ if row == len(y):
2197
+ g, z = 1, _make_partition(y).add_cell(row) # black
2198
+ else:
2199
+ g, z = 2, _make_partition(y).add_cell(row) # blue
2200
+ elif x == y != t and f in [1, 3]: # black or red
2201
+ c = SkewPartition([x, t]).cells()[0]
2202
+ col = c[0] + c[1] + 1
2203
+ for i in range(len(y)):
2204
+ if i + y[i] == col:
2205
+ z = y[:i] + [y[i] + 1] + y[i + 1:]
2206
+ break
2207
+ g = 3
2208
+ else:
2209
+ raise NotImplementedError
2210
+ return g, _make_partition(z), h
2211
+
2212
+ def backward_rule(self, y, g, z, h, x):
2213
+ r"""
2214
+ Return the input path and the content given two incident edges.
2215
+
2216
+ See [Fom1995]_ Lemma 4.5.1, page 38.
2217
+
2218
+ INPUT:
2219
+
2220
+ - ``y``, ``g``, ``z``, ``h``, ``x`` -- a path of three partitions and
2221
+ two colors from a cell in a growth diagram, labelled as::
2222
+
2223
+ x
2224
+ h
2225
+ y g z
2226
+
2227
+ OUTPUT:
2228
+
2229
+ A tuple ``(e, t, f, content)`` consisting of the shape ``t``
2230
+ of the fourth word, the colours of the incident edges and the
2231
+ content of the cell according to Sagan - Worley insertion.
2232
+
2233
+ EXAMPLES::
2234
+
2235
+ sage: Shifted = GrowthDiagram.rules.ShiftedShapes()
2236
+ sage: Shifted.backward_rule([], 1, [1], 0, [])
2237
+ (0, [], 0, 1)
2238
+
2239
+ sage: Shifted.backward_rule([1], 2, [2], 0, [1])
2240
+ (0, [1], 0, 1)
2241
+
2242
+ if ``x != y``::
2243
+
2244
+ sage: Shifted.backward_rule([3], 1, [3, 1], 0, [2,1])
2245
+ (0, [2], 1, 0)
2246
+
2247
+ sage: Shifted.backward_rule([2,1], 2, [3, 1], 0, [3])
2248
+ (0, [2], 2, 0)
2249
+
2250
+ if ``x == y != t``::
2251
+
2252
+ sage: Shifted.backward_rule([3], 1, [3, 1], 0, [3])
2253
+ (0, [2], 2, 0)
2254
+
2255
+ sage: Shifted.backward_rule([3,1], 2, [3, 2], 0, [3,1])
2256
+ (0, [2, 1], 2, 0)
2257
+
2258
+ sage: Shifted.backward_rule([2,1], 3, [3, 1], 0, [2,1])
2259
+ (0, [2], 1, 0)
2260
+
2261
+ sage: Shifted.backward_rule([3], 3, [4], 0, [3])
2262
+ (0, [2], 3, 0)
2263
+ """
2264
+ if h != 0:
2265
+ raise ValueError("the P-graph should not be colored")
2266
+
2267
+ if x == y == z:
2268
+ if g != 0:
2269
+ raise ValueError("degenerate edge g should have color 0")
2270
+ return (0, x, 0, 0)
2271
+ elif x == z != y:
2272
+ return (0, y, g, 0)
2273
+ elif x != z == y:
2274
+ if g != 0:
2275
+ raise ValueError("degenerate edge g should have color 0")
2276
+ return (0, x, 0, 0)
2277
+ else:
2278
+ if x != y:
2279
+ row = SkewPartition([z, x]).cells()[0][0]
2280
+ return (0, _make_partition(y).remove_cell(row), g, 0)
2281
+ else:
2282
+ row, col = SkewPartition([z, x]).cells()[0]
2283
+ if row > 0 and g in [1, 2]: # black or blue
2284
+ return (0, _make_partition(y).remove_cell(row-1), 2, 0)
2285
+ elif row == 0 and g in [1, 2]: # black or blue
2286
+ return (0, y, 0, 1)
2287
+ else:
2288
+ # find last cell in column col-1
2289
+ for i in range(len(y)-1,-1,-1):
2290
+ if i + y[i] == col + row:
2291
+ if y[i] == 1:
2292
+ t = y[:i]
2293
+ return (0, t, 1, 0)
2294
+ else:
2295
+ t = y[:i] + [y[i]-1] + y[i+1:]
2296
+ return (0, t, 3, 0)
2297
+ raise ValueError("this should not happen")
2298
+
2299
+
2300
+ class RuleLLMS(Rule):
2301
+ r"""
2302
+ A rule modelling the Schensted correspondence for affine
2303
+ permutations.
2304
+
2305
+ EXAMPLES::
2306
+
2307
+ sage: LLMS3 = GrowthDiagram.rules.LLMS(3)
2308
+ sage: GrowthDiagram(LLMS3, [3,1,2])
2309
+ 0 1 0
2310
+ 0 0 1
2311
+ 1 0 0
2312
+
2313
+ The vertices of the dual graded graph are
2314
+ :class:`~sage.combinat.core.Cores`::
2315
+
2316
+ sage: LLMS3.vertices(4)
2317
+ 3-Cores of length 4
2318
+
2319
+ Let us check example of Figure 1 in [LS2007]_. Note that,
2320
+ instead of passing the rule to :class:`GrowthDiagram`, we can
2321
+ also call the rule to create growth diagrams::
2322
+
2323
+ sage: G = LLMS3([4,1,2,6,3,5]); G
2324
+ 0 1 0 0 0 0
2325
+ 0 0 1 0 0 0
2326
+ 0 0 0 0 1 0
2327
+ 1 0 0 0 0 0
2328
+ 0 0 0 0 0 1
2329
+ 0 0 0 1 0 0
2330
+
2331
+ The :meth:`P_symbol` is a
2332
+ :class:`~sage.combinat.k_tableau.StrongTableau`::
2333
+
2334
+ sage: G.P_symbol().pp()
2335
+ -1 -2 -3 -5
2336
+ 3 5
2337
+ -4 -6
2338
+ 5
2339
+ 6
2340
+
2341
+ The :meth:`Q_symbol` is a
2342
+ :class:`~sage.combinat.k_tableau.WeakTableau`::
2343
+
2344
+ sage: G.Q_symbol().pp()
2345
+ 1 3 4 5
2346
+ 2 5
2347
+ 3 6
2348
+ 5
2349
+ 6
2350
+
2351
+ Let us also check Example 6.2 in [LLMSSZ2013]_::
2352
+
2353
+ sage: G = LLMS3([4,1,3,2])
2354
+ sage: G.P_symbol().pp()
2355
+ -1 -2 3
2356
+ -3
2357
+ -4
2358
+
2359
+ sage: G.Q_symbol().pp()
2360
+ 1 3 4
2361
+ 2
2362
+ 3
2363
+
2364
+ TESTS::
2365
+
2366
+ sage: LLMS3 = GrowthDiagram.rules.LLMS(3)
2367
+ sage: LLMS3.zero
2368
+ []
2369
+ """
2370
+ zero_edge = None # to prevent confusion with the edge labelled with content 0
2371
+ has_multiple_edges = True
2372
+
2373
+ def __init__(self, k):
2374
+ r"""
2375
+ Initialize ``self``.
2376
+
2377
+ TESTS::
2378
+
2379
+ sage: LLMS3 = GrowthDiagram.rules.LLMS(3)
2380
+ sage: TestSuite(LLMS3).run()
2381
+ """
2382
+ self.k = k
2383
+ self.zero = Core([], k)
2384
+
2385
+ def normalize_vertex(self, v):
2386
+ r"""
2387
+ Convert ``v`` to a `k`-core.
2388
+
2389
+ EXAMPLES::
2390
+
2391
+ sage: LLMS3 = GrowthDiagram.rules.LLMS(3)
2392
+ sage: LLMS3.normalize_vertex([3,1]).parent()
2393
+ 3-Cores of length 3
2394
+ """
2395
+ return Core(v, self.k)
2396
+
2397
+ def rank(self, v):
2398
+ r"""
2399
+ Return the rank of ``v``: the length of the core.
2400
+
2401
+ EXAMPLES::
2402
+
2403
+ sage: LLMS3 = GrowthDiagram.rules.LLMS(3)
2404
+ sage: LLMS3.rank(LLMS3.vertices(3)[0])
2405
+ 3
2406
+ """
2407
+ return v.length()
2408
+
2409
+ def vertices(self, n):
2410
+ r"""
2411
+ Return the vertices of the dual graded graph on level ``n``.
2412
+
2413
+ EXAMPLES::
2414
+
2415
+ sage: LLMS3 = GrowthDiagram.rules.LLMS(3)
2416
+ sage: LLMS3.vertices(2)
2417
+ 3-Cores of length 2
2418
+ """
2419
+ return Cores(self.k, length=n)
2420
+
2421
+ def is_Q_edge(self, v, w):
2422
+ r"""
2423
+ Return whether ``(v, w)`` is a `Q`-edge of ``self``.
2424
+
2425
+ ``(v, w)`` is an edge if ``w`` is a weak cover of ``v``, see
2426
+ :meth:`~sage.combinat.core.Core.weak_covers()`.
2427
+
2428
+ EXAMPLES::
2429
+
2430
+ sage: # needs sage.modules
2431
+ sage: LLMS4 = GrowthDiagram.rules.LLMS(4)
2432
+ sage: v = LLMS4.vertices(3)[1]; v
2433
+ [2, 1]
2434
+ sage: [w for w in LLMS4.vertices(4) if len(LLMS4.is_Q_edge(v, w)) > 0]
2435
+ [[2, 2], [3, 1, 1]]
2436
+ sage: all(LLMS4.is_Q_edge(v, w) == [] for w in LLMS4.vertices(5))
2437
+ True
2438
+ """
2439
+ return [None] if w in v.weak_covers() else []
2440
+
2441
+ def is_P_edge(self, v, w):
2442
+ r"""
2443
+ Return whether ``(v, w)`` is a `P`-edge of ``self``.
2444
+
2445
+ For two k-cores v and w containing v, there are as many edges as
2446
+ there are components in the skew partition w/v. These
2447
+ components are ribbons, and therefore contain a unique cell
2448
+ with maximal content. We index the edge with this content.
2449
+
2450
+ EXAMPLES::
2451
+
2452
+ sage: LLMS4 = GrowthDiagram.rules.LLMS(4)
2453
+ sage: v = LLMS4.vertices(2)[0]; v
2454
+ [2]
2455
+ sage: [(w, LLMS4.is_P_edge(v, w)) for w in LLMS4.vertices(3)]
2456
+ [([3], [2]), ([2, 1], [-1]), ([1, 1, 1], [])]
2457
+ sage: all(LLMS4.is_P_edge(v, w) == [] for w in LLMS4.vertices(4))
2458
+ True
2459
+ """
2460
+ if w in v.strong_covers():
2461
+ T = SkewPartition([w.to_partition(), v.to_partition()])
2462
+ return [max([j-i for i,j in c]) for c in T.cell_poset().connected_components()]
2463
+ else:
2464
+ return []
2465
+
2466
+ def P_symbol(self, P_chain):
2467
+ r"""
2468
+ Return the labels along the vertical boundary of a
2469
+ rectangular growth diagram as a skew
2470
+ :class:`~sage.combinat.k_tableau.StrongTableau`.
2471
+
2472
+ EXAMPLES::
2473
+
2474
+ sage: LLMS4 = GrowthDiagram.rules.LLMS(4)
2475
+ sage: G = LLMS4([3,4,1,2])
2476
+ sage: G.P_symbol().pp()
2477
+ -1 -2
2478
+ -3 -4
2479
+ """
2480
+ C = P_chain
2481
+ T = SkewTableau(chain=C[::2])
2482
+ S = T.to_list()
2483
+ for entry, content in enumerate(C[1::2], 1):
2484
+ for i,j in T.cells_containing(entry):
2485
+ if j-i == content:
2486
+ S[i][j] = -S[i][j]
2487
+ break
2488
+ return StrongTableau(S, self.k-1)
2489
+
2490
+ def Q_symbol(self, Q_chain):
2491
+ r"""
2492
+ Return the labels along the horizontal boundary of a
2493
+ rectangular growth diagram as a skew
2494
+ :class:`~sage.combinat.k_tableau.WeakTableau`.
2495
+
2496
+ EXAMPLES::
2497
+
2498
+ sage: LLMS4 = GrowthDiagram.rules.LLMS(4)
2499
+ sage: G = LLMS4([3,4,1,2])
2500
+ sage: G.Q_symbol().pp()
2501
+ 1 2
2502
+ 3 4
2503
+ """
2504
+ return WeakTableau(SkewTableau(chain=Q_chain[::2]), self.k-1)
2505
+
2506
+ def forward_rule(self, y, e, t, f, x, content):
2507
+ r"""
2508
+ Return the output path given two incident edges and the content.
2509
+
2510
+ See [LS2007]_ Section 3.4 and [LLMSSZ2013]_ Section 6.3.
2511
+
2512
+ INPUT:
2513
+
2514
+ - ``y``, ``e``, ``t``, ``f``, ``x`` -- a path of three partitions and
2515
+ two colors from a cell in a growth diagram, labelled as::
2516
+
2517
+ t f x
2518
+ e
2519
+ y
2520
+
2521
+ - ``content`` -- `0` or `1`; the content of the cell
2522
+
2523
+ OUTPUT:
2524
+
2525
+ The two colors and the fourth partition g, z, h according to
2526
+ LLMS insertion.
2527
+
2528
+ EXAMPLES::
2529
+
2530
+ sage: LLMS3 = GrowthDiagram.rules.LLMS(3)
2531
+ sage: LLMS4 = GrowthDiagram.rules.LLMS(4)
2532
+
2533
+ sage: Z = LLMS3.zero
2534
+ sage: LLMS3.forward_rule(Z, None, Z, None, Z, 0)
2535
+ (None, [], None)
2536
+
2537
+ sage: LLMS3.forward_rule(Z, None, Z, None, Z, 1)
2538
+ (None, [1], 0)
2539
+
2540
+ sage: Y = Core([3,1,1], 3)
2541
+ sage: LLMS3.forward_rule(Y, None, Y, None, Y, 1)
2542
+ (None, [4, 2, 1, 1], 3)
2543
+
2544
+ if ``x != y``::
2545
+
2546
+ sage: Y = Core([1,1], 3); T = Core([1], 3); X = Core([2], 3)
2547
+ sage: LLMS3.forward_rule(Y, -1, T, None, X, 0)
2548
+ (None, [2, 1, 1], -1)
2549
+
2550
+ sage: Y = Core([2], 4); T = Core([1], 4); X = Core([1,1], 4)
2551
+ sage: LLMS4.forward_rule(Y, 1, T, None, X, 0)
2552
+ (None, [2, 1], 1)
2553
+
2554
+ sage: Y = Core([2,1,1], 3); T = Core([2], 3); X = Core([3,1], 3)
2555
+ sage: LLMS3.forward_rule(Y, -1, T, None, X, 0)
2556
+ (None, [3, 1, 1], -2)
2557
+
2558
+ if ``x == y != t``::
2559
+
2560
+ sage: Y = Core([1], 3); T = Core([], 3); X = Core([1], 3)
2561
+ sage: LLMS3.forward_rule(Y, 0, T, None, X, 0)
2562
+ (None, [1, 1], -1)
2563
+
2564
+ sage: Y = Core([1], 4); T = Core([], 4); X = Core([1], 4)
2565
+ sage: LLMS4.forward_rule(Y, 0, T, None, X, 0)
2566
+ (None, [1, 1], -1)
2567
+
2568
+ sage: Y = Core([2,1], 4); T = Core([1,1], 4); X = Core([2,1], 4)
2569
+ sage: LLMS4.forward_rule(Y, 1, T, None, X, 0)
2570
+ (None, [2, 2], 0)
2571
+ """
2572
+ if f is not None:
2573
+ raise ValueError("the Q-graph should not be colored")
2574
+ g = None
2575
+ if x == t == y:
2576
+ if e is not None:
2577
+ raise ValueError("degenerate edge e should have color None")
2578
+ if content == 0:
2579
+ z, h = x, None
2580
+ elif content == 1:
2581
+ if t.size() == 0:
2582
+ z = t.affine_symmetric_group_simple_action(0)
2583
+ else:
2584
+ z = t.affine_symmetric_group_simple_action(t[0] % self.k)
2585
+ h = z[0] - 1
2586
+ else:
2587
+ assert False, "BUG in RuleLLMS"
2588
+ elif content != 0:
2589
+ raise ValueError("for y=%s, t=%s, x=%s, the content should be 0 but is %s"
2590
+ % (y, t, x, content))
2591
+ elif x != t == y:
2592
+ if e is not None:
2593
+ raise ValueError("degenerate edge e should have color None")
2594
+ z, h = x, e
2595
+ elif x == t != y:
2596
+ z, h = y, e
2597
+ else: # x != t and y != t
2598
+ qx = SkewPartition([x.to_partition(), t.to_partition()])
2599
+ qy = SkewPartition([y.to_partition(), t.to_partition()])
2600
+ if not all(c in qx.cells() for c in qy.cells()):
2601
+ res = [(j-i) % self.k for i, j in qx.cells()]
2602
+ assert len(set(res)) == 1
2603
+ r = res[0]
2604
+ z = y.affine_symmetric_group_simple_action(r)
2605
+ if e % self.k == r:
2606
+ h = e-1
2607
+ else:
2608
+ h = e
2609
+ elif x == y != t:
2610
+ # the addable cell with largest content at most e
2611
+ cprime = sorted([c for c in y.to_partition().addable_cells()
2612
+ if c[1]-c[0] <= e],
2613
+ key=lambda c: -(c[1]-c[0]))[0]
2614
+ h = cprime[1] - cprime[0]
2615
+ z = y.affine_symmetric_group_simple_action(h % self.k)
2616
+
2617
+ return g, z, h
2618
+
2619
+
2620
+ class RuleBinaryWord(Rule):
2621
+ r"""
2622
+ A rule modelling a Schensted-like correspondence for binary words.
2623
+
2624
+ EXAMPLES::
2625
+
2626
+ sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
2627
+ sage: GrowthDiagram(BinaryWord, [3,1,2])
2628
+ 0 1 0
2629
+ 0 0 1
2630
+ 1 0 0
2631
+
2632
+ The vertices of the dual graded graph are binary words::
2633
+
2634
+ sage: BinaryWord.vertices(3)
2635
+ [word: 100, word: 101, word: 110, word: 111]
2636
+
2637
+ Note that, instead of passing the rule to :class:`GrowthDiagram`,
2638
+ we can also use call the rule to create growth diagrams. For
2639
+ example::
2640
+
2641
+ sage: BinaryWord([2,4,1,3]).P_chain()
2642
+ [word: , word: 1, word: 10, word: 101, word: 1101]
2643
+ sage: BinaryWord([2,4,1,3]).Q_chain()
2644
+ [word: , word: 1, word: 11, word: 110, word: 1101]
2645
+
2646
+ The Kleitman Greene invariant is the descent word, encoded by the
2647
+ positions of the zeros::
2648
+
2649
+ sage: pi = Permutation([4,1,8,3,6,5,2,7,9])
2650
+ sage: G = BinaryWord(pi); G
2651
+ 0 1 0 0 0 0 0 0 0
2652
+ 0 0 0 0 0 0 1 0 0
2653
+ 0 0 0 1 0 0 0 0 0
2654
+ 1 0 0 0 0 0 0 0 0
2655
+ 0 0 0 0 0 1 0 0 0
2656
+ 0 0 0 0 1 0 0 0 0
2657
+ 0 0 0 0 0 0 0 1 0
2658
+ 0 0 1 0 0 0 0 0 0
2659
+ 0 0 0 0 0 0 0 0 1
2660
+ sage: pi.descents()
2661
+ [1, 3, 5, 6]
2662
+
2663
+ TESTS::
2664
+
2665
+ sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
2666
+ sage: BinaryWord.zero
2667
+ word:
2668
+ sage: G = BinaryWord(labels=[[1,1],[1,1,0],[0,1]])
2669
+ Traceback (most recent call last):
2670
+ ...
2671
+ ValueError: 01 has smaller rank than 110 but is not covered by it in P
2672
+
2673
+ sage: G = BinaryWord(labels=[[1,1],[1,0,1],[0,1]])
2674
+ Traceback (most recent call last):
2675
+ ...
2676
+ ValueError: 11 has smaller rank than 101 but is not covered by it in Q
2677
+
2678
+ Check duality::
2679
+
2680
+ sage: BinaryWord._check_duality(4)
2681
+
2682
+ Check that the rules are bijective::
2683
+
2684
+ sage: all(BinaryWord(labels=BinaryWord(pi).out_labels()).to_word()
2685
+ ....: == pi for pi in Permutations(4))
2686
+ True
2687
+ sage: pi = Permutations(10).random_element()
2688
+ sage: G = BinaryWord(pi)
2689
+ sage: list(BinaryWord(labels=G.out_labels())) == list(G)
2690
+ True
2691
+
2692
+ Test that the Kleitman Greene invariant is indeed the descent word::
2693
+
2694
+ sage: r = 4
2695
+ sage: all(Word([0 if i in w.descents() else 1 for i in range(r)])
2696
+ ....: == BinaryWord(w).out_labels()[r]
2697
+ ....: for w in Permutations(r))
2698
+ True
2699
+ """
2700
+ zero = Word([], alphabet=[0,1])
2701
+
2702
+ def normalize_vertex(self, v):
2703
+ r"""
2704
+ Return ``v`` as a binary word.
2705
+
2706
+ EXAMPLES::
2707
+
2708
+ sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
2709
+ sage: BinaryWord.normalize_vertex([0,1]).parent()
2710
+ Finite words over {0, 1}
2711
+ """
2712
+ return Word(v, alphabet=[0,1])
2713
+
2714
+ def vertices(self, n):
2715
+ r"""
2716
+ Return the vertices of the dual graded graph on level ``n``.
2717
+
2718
+ EXAMPLES::
2719
+
2720
+ sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
2721
+ sage: BinaryWord.vertices(3)
2722
+ [word: 100, word: 101, word: 110, word: 111]
2723
+ """
2724
+ if n == 0:
2725
+ return [self.zero]
2726
+ else:
2727
+ w1 = Word([1], [0,1])
2728
+ return [w1 + w for w in Words([0,1], n-1)]
2729
+
2730
+ def rank(self, v):
2731
+ r"""
2732
+ Return the rank of ``v``: number of letters of the word.
2733
+
2734
+ EXAMPLES::
2735
+
2736
+ sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
2737
+ sage: BinaryWord.rank(BinaryWord.vertices(3)[0])
2738
+ 3
2739
+ """
2740
+ return len(v)
2741
+
2742
+ def is_Q_edge(self, v, w):
2743
+ r"""
2744
+ Return whether ``(v, w)`` is a `Q`-edge of ``self``.
2745
+
2746
+ ``(w, v)`` is an edge if ``w`` is obtained from ``v`` by
2747
+ appending a letter.
2748
+
2749
+ EXAMPLES::
2750
+
2751
+ sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
2752
+ sage: v = BinaryWord.vertices(2)[0]; v
2753
+ word: 10
2754
+ sage: [w for w in BinaryWord.vertices(3) if BinaryWord.is_Q_edge(v, w)]
2755
+ [word: 100, word: 101]
2756
+ sage: [w for w in BinaryWord.vertices(4) if BinaryWord.is_Q_edge(v, w)]
2757
+ []
2758
+ """
2759
+ return w[:-1] == v
2760
+
2761
+ def is_P_edge(self, v, w):
2762
+ r"""
2763
+ Return whether ``(v, w)`` is a `P`-edge of ``self``.
2764
+
2765
+ ``(v, w)`` is an edge if ``v`` is obtained from ``w`` by
2766
+ deleting a letter.
2767
+
2768
+ EXAMPLES::
2769
+
2770
+ sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
2771
+ sage: v = BinaryWord.vertices(2)[1]; v
2772
+ word: 11
2773
+ sage: [w for w in BinaryWord.vertices(3) if BinaryWord.is_P_edge(v, w)]
2774
+ [word: 101, word: 110, word: 111]
2775
+ sage: [w for w in BinaryWord.vertices(4) if BinaryWord.is_P_edge(v, w)]
2776
+ []
2777
+ """
2778
+ return len(w) == len(v) + 1 and v.is_subword_of(w)
2779
+
2780
+ def forward_rule(self, y, t, x, content):
2781
+ r"""
2782
+ Return the output shape given three shapes and the content.
2783
+
2784
+ See [Fom1995]_ Lemma 4.6.1, page 40.
2785
+
2786
+ INPUT:
2787
+
2788
+ - ``y``, ``t``, ``x`` -- three binary words from a cell in a growth
2789
+ diagram, labelled as::
2790
+
2791
+ t x
2792
+ y
2793
+
2794
+ - ``content`` -- `0` or `1`; the content of the cell
2795
+
2796
+ OUTPUT:
2797
+
2798
+ The fourth binary word ``z`` according to Viennot's
2799
+ bijection [Vie1983]_.
2800
+
2801
+ EXAMPLES::
2802
+
2803
+ sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
2804
+
2805
+ sage: BinaryWord.forward_rule([], [], [], 1)
2806
+ word: 1
2807
+
2808
+ sage: BinaryWord.forward_rule([1], [1], [1], 1)
2809
+ word: 11
2810
+
2811
+ if ``x != y`` append last letter of ``x`` to ``y``::
2812
+
2813
+ sage: BinaryWord.forward_rule([1,0], [1], [1,1], 0)
2814
+ word: 101
2815
+
2816
+ if ``x == y != t`` append ``0`` to ``y``::
2817
+
2818
+ sage: BinaryWord.forward_rule([1,1], [1], [1,1], 0)
2819
+ word: 110
2820
+ """
2821
+ if x == t == y:
2822
+ if content == 0:
2823
+ z = x
2824
+ elif content == 1:
2825
+ z = Word(list(y) + [1], alphabet=[0,1])
2826
+ else:
2827
+ raise NotImplementedError
2828
+ elif content != 0:
2829
+ raise ValueError("for y=%s, t=%s, x=%s, the content should be 0 but is %s"
2830
+ % (y, t, x, content))
2831
+ elif x != t == y:
2832
+ z = x
2833
+ elif x == t != y:
2834
+ z = y
2835
+ else:
2836
+ if x != y:
2837
+ z = Word(list(y) + [x[-1]], alphabet=[0,1])
2838
+ elif x == y != t:
2839
+ z = Word(list(y) + [0], alphabet=[0,1])
2840
+ else:
2841
+ raise NotImplementedError
2842
+ return z
2843
+
2844
+ def backward_rule(self, y, z, x):
2845
+ r"""
2846
+ Return the content and the input shape.
2847
+
2848
+ See [Fom1995]_ Lemma 4.6.1, page 40.
2849
+
2850
+ - ``y``, ``z``, ``x`` -- three binary words from a cell in a growth diagram,
2851
+ labelled as::
2852
+
2853
+ x
2854
+ y z
2855
+
2856
+ OUTPUT:
2857
+
2858
+ A pair ``(t, content)`` consisting of the shape of the fourth
2859
+ word and the content of the cell according to Viennot's
2860
+ bijection [Vie1983]_.
2861
+
2862
+ TESTS::
2863
+
2864
+ sage: BinaryWord = GrowthDiagram.rules.BinaryWord()
2865
+ sage: w = [4,1,8,3,6,5,2,7,9]; G = GrowthDiagram(BinaryWord, w)
2866
+ sage: BinaryWord(labels=G.out_labels()).to_word() == w # indirect doctest
2867
+ True
2868
+ """
2869
+ if x == y == z:
2870
+ return (x, 0)
2871
+ elif x == z != y:
2872
+ return (y, 0)
2873
+ elif x != z == y:
2874
+ return (x, 0)
2875
+ else:
2876
+ if x == y and len(z) > 0 and z[-1] == 1:
2877
+ return (x, 1)
2878
+ else:
2879
+ return (x[:-1], 0)
2880
+
2881
+
2882
+ class RuleSylvester(Rule):
2883
+ r"""
2884
+ A rule modelling a Schensted-like correspondence for binary trees.
2885
+
2886
+ EXAMPLES::
2887
+
2888
+ sage: Sylvester = GrowthDiagram.rules.Sylvester()
2889
+ sage: GrowthDiagram(Sylvester, [3,1,2])
2890
+ 0 1 0
2891
+ 0 0 1
2892
+ 1 0 0
2893
+
2894
+ The vertices of the dual graded graph are
2895
+ :class:`~sage.combinat.binary_tree.BinaryTrees`::
2896
+
2897
+ sage: Sylvester.vertices(3)
2898
+ Binary trees of size 3
2899
+
2900
+ The :meth:`~sage.combinat.growth.Rule.P_graph` is also known as
2901
+ the bracket tree, the :meth:`~sage.combinat.growth.Rule.Q_graph`
2902
+ is the lattice of finite order ideals of the infinite binary
2903
+ tree, see Example 2.4.6 in [Fom1994]_.
2904
+
2905
+ For a permutation, the :meth:`P_symbol` is the binary search
2906
+ tree, the :meth:`Q_symbol` is the increasing tree corresponding
2907
+ to the inverse permutation. Note that, instead of passing the
2908
+ rule to :class:`GrowthDiagram`, we can also call the rule to
2909
+ create growth diagrams. From [Nze2007]_::
2910
+
2911
+ sage: pi = Permutation([3,5,1,4,2,6]); G = Sylvester(pi); G
2912
+ 0 0 1 0 0 0
2913
+ 0 0 0 0 1 0
2914
+ 1 0 0 0 0 0
2915
+ 0 0 0 1 0 0
2916
+ 0 1 0 0 0 0
2917
+ 0 0 0 0 0 1
2918
+ sage: ascii_art(G.P_symbol())
2919
+ __3__
2920
+ / \
2921
+ 1 5
2922
+ \ / \
2923
+ 2 4 6
2924
+ sage: ascii_art(G.Q_symbol())
2925
+ __1__
2926
+ / \
2927
+ 3 2
2928
+ \ / \
2929
+ 5 4 6
2930
+
2931
+ sage: all(Sylvester(pi).P_symbol() == pi.binary_search_tree()
2932
+ ....: for pi in Permutations(5))
2933
+ True
2934
+
2935
+ sage: all(Sylvester(pi).Q_symbol() == pi.inverse().increasing_tree()
2936
+ ....: for pi in Permutations(5))
2937
+ True
2938
+
2939
+ TESTS::
2940
+
2941
+ sage: Sylvester.zero
2942
+ .
2943
+
2944
+ sage: B = BinaryTree; R = B([None,[]]); L = B([[],None])
2945
+ sage: T = B([[],[]]); S = B([L,None])
2946
+ sage: G = Sylvester(labels=[R, T, R])
2947
+ Traceback (most recent call last):
2948
+ ...
2949
+ ValueError: [., [., .]] has smaller rank than [[., .], [., .]]
2950
+ but is not covered by it in P
2951
+
2952
+ sage: G = Sylvester(labels=[R, S, R])
2953
+ Traceback (most recent call last):
2954
+ ...
2955
+ ValueError: [., [., .]] has smaller rank than [[[., .], .], .]
2956
+ but is not covered by it in Q
2957
+
2958
+ Check duality::
2959
+
2960
+ sage: Sylvester._check_duality(4)
2961
+
2962
+ Check that the rules are bijective::
2963
+
2964
+ sage: all(Sylvester(labels=GrowthDiagram(Sylvester, pi).out_labels()).to_word()
2965
+ ....: == pi for pi in Permutations(4))
2966
+ True
2967
+ sage: pi = Permutations(10).random_element()
2968
+ sage: G = GrowthDiagram(Sylvester, pi)
2969
+ sage: list(Sylvester(labels=G.out_labels())) == list(G)
2970
+ True
2971
+ """
2972
+ zero = BinaryTree() # type:ignore
2973
+
2974
+ def normalize_vertex(self, v):
2975
+ r"""
2976
+ Return ``v`` as a binary tree.
2977
+
2978
+ EXAMPLES::
2979
+
2980
+ sage: Sylvester = GrowthDiagram.rules.Sylvester()
2981
+ sage: Sylvester.normalize_vertex([[],[]]).parent()
2982
+ Binary trees
2983
+ """
2984
+ return BinaryTree(v)
2985
+
2986
+ def vertices(self, n):
2987
+ r"""
2988
+ Return the vertices of the dual graded graph on level ``n``.
2989
+
2990
+ EXAMPLES::
2991
+
2992
+ sage: Sylvester = GrowthDiagram.rules.Sylvester()
2993
+ sage: Sylvester.vertices(3)
2994
+ Binary trees of size 3
2995
+ """
2996
+ return BinaryTrees(n)
2997
+
2998
+ def rank(self, v):
2999
+ r"""
3000
+ Return the rank of ``v``: the number of nodes of the tree.
3001
+
3002
+ EXAMPLES::
3003
+
3004
+ sage: Sylvester = GrowthDiagram.rules.Sylvester()
3005
+ sage: Sylvester.rank(Sylvester.vertices(3)[0])
3006
+ 3
3007
+ """
3008
+ return v.node_number()
3009
+
3010
+ def is_Q_edge(self, v, w):
3011
+ r"""
3012
+ Return whether ``(v, w)`` is a `Q`-edge of ``self``.
3013
+
3014
+ ``(v, w)`` is an edge if ``v`` is a sub-tree of ``w`` with one
3015
+ node less.
3016
+
3017
+ EXAMPLES::
3018
+
3019
+ sage: Sylvester = GrowthDiagram.rules.Sylvester()
3020
+ sage: v = Sylvester.vertices(2)[1]; ascii_art(v)
3021
+ o
3022
+ /
3023
+ o
3024
+ sage: ascii_art([w for w in Sylvester.vertices(3) if Sylvester.is_Q_edge(v, w)])
3025
+ [ o , o, o ]
3026
+ [ / \ / / ]
3027
+ [ o o o o ]
3028
+ [ \ / ]
3029
+ [ o o ]
3030
+ sage: [w for w in Sylvester.vertices(4) if Sylvester.is_Q_edge(v, w)]
3031
+ []
3032
+ """
3033
+ def is_subtree(T1, T2):
3034
+ if T2.is_empty():
3035
+ return False
3036
+ elif T2[0].is_empty() and T2[1].is_empty():
3037
+ return T1.is_empty()
3038
+ elif T1.is_empty():
3039
+ return False
3040
+ else:
3041
+ return ((T1[0] == T2[0] and is_subtree(T1[1], T2[1])) or
3042
+ (T1[1] == T2[1] and is_subtree(T1[0], T2[0])))
3043
+ return is_subtree(v, w)
3044
+
3045
+ def is_P_edge(self, v, w):
3046
+ r"""
3047
+ Return whether ``(v, w)`` is a `P`-edge of ``self``.
3048
+
3049
+ ``(v, w)`` is an edge if ``v`` is obtained from ``w`` by deleting
3050
+ its right-most node.
3051
+
3052
+ EXAMPLES::
3053
+
3054
+ sage: Sylvester = GrowthDiagram.rules.Sylvester()
3055
+ sage: v = Sylvester.vertices(2)[1]; ascii_art(v)
3056
+ o
3057
+ /
3058
+ o
3059
+
3060
+ sage: ascii_art([w for w in Sylvester.vertices(3) if Sylvester.is_P_edge(v, w)])
3061
+ [ o , o ]
3062
+ [ / \ / ]
3063
+ [ o o o ]
3064
+ [ / ]
3065
+ [ o ]
3066
+
3067
+ sage: [w for w in Sylvester.vertices(4) if Sylvester.is_P_edge(v, w)]
3068
+ []
3069
+ """
3070
+ if w.is_empty():
3071
+ return False
3072
+ else:
3073
+ return v == RuleSylvester._delete_right_most_node(w)
3074
+
3075
+ def P_symbol(self, P_chain):
3076
+ r"""
3077
+ Return the labels along the vertical boundary of a rectangular
3078
+ growth diagram as a labelled binary tree.
3079
+
3080
+ For permutations, this coincides with the binary search tree.
3081
+
3082
+ EXAMPLES::
3083
+
3084
+ sage: Sylvester = GrowthDiagram.rules.Sylvester()
3085
+ sage: pi = Permutation([2,4,3,1])
3086
+ sage: ascii_art(Sylvester(pi).P_symbol())
3087
+ _2_
3088
+ / \
3089
+ 1 4
3090
+ /
3091
+ 3
3092
+ sage: Sylvester(pi).P_symbol() == pi.binary_search_tree()
3093
+ True
3094
+
3095
+ We can also do the skew version::
3096
+
3097
+ sage: B = BinaryTree; E = B(); N = B([])
3098
+ sage: ascii_art(Sylvester([3,2], shape=[3,3,3], labels=[N,N,N,E,E,E,N]).P_symbol())
3099
+ __1___
3100
+ / \
3101
+ None 3
3102
+ /
3103
+ 2
3104
+ """
3105
+ def add_label(L, S, T, m):
3106
+ if T[0] == S:
3107
+ L = LabelledBinaryTree([L, None], m)
3108
+ else:
3109
+ assert T[0] == S[0]
3110
+ l = L.label()
3111
+ L = LabelledBinaryTree([L[0], add_label(L[1], S[1], T[1], m)], l)
3112
+ return L
3113
+
3114
+ L = LabelledBinaryTree(P_chain[0])
3115
+ for i in range(1, len(P_chain)):
3116
+ S, T = P_chain[i-1], P_chain[i]
3117
+ L = add_label(L, S, T, i)
3118
+ return L
3119
+
3120
+ def Q_symbol(self, Q_chain):
3121
+ r"""
3122
+ Return the labels along the vertical boundary of a rectangular
3123
+ growth diagram as a labelled binary tree.
3124
+
3125
+ For permutations, this coincides with the increasing tree.
3126
+
3127
+ EXAMPLES::
3128
+
3129
+ sage: Sylvester = GrowthDiagram.rules.Sylvester()
3130
+ sage: pi = Permutation([2,4,3,1])
3131
+ sage: ascii_art(Sylvester(pi).Q_symbol())
3132
+ _1_
3133
+ / \
3134
+ 4 2
3135
+ /
3136
+ 3
3137
+ sage: Sylvester(pi).Q_symbol() == pi.inverse().increasing_tree()
3138
+ True
3139
+
3140
+ We can also do the skew version::
3141
+
3142
+ sage: B = BinaryTree; E = B(); N = B([])
3143
+ sage: ascii_art(Sylvester([3,2], shape=[3,3,3], labels=[N,N,N,E,E,E,N]).Q_symbol())
3144
+ _None_
3145
+ / \
3146
+ 3 1
3147
+ /
3148
+ 2
3149
+ """
3150
+ def add_label(L, S, T, m):
3151
+ if L.is_empty():
3152
+ assert T.node_number() == 1
3153
+ return LabelledBinaryTree([], m)
3154
+ l = L.label()
3155
+ if T[0] == S[0]:
3156
+ return LabelledBinaryTree([L[0], add_label(L[1], S[1], T[1], m)], l)
3157
+ else:
3158
+ return LabelledBinaryTree([add_label(L[0], S[0], T[0], m), L[1]], l)
3159
+
3160
+ L = LabelledBinaryTree(Q_chain[0])
3161
+ for i in range(1, len(Q_chain)):
3162
+ S, T = Q_chain[i-1], Q_chain[i]
3163
+ L = add_label(L, S, T, i)
3164
+ return L
3165
+
3166
+ @staticmethod
3167
+ def _delete_right_most_node(b):
3168
+ r"""
3169
+ Return the tree obtained by deleting the right most node from ``b``.
3170
+
3171
+ TESTS::
3172
+
3173
+ sage: Sylvester = GrowthDiagram.rules.Sylvester()
3174
+ sage: b = BinaryTree([]); b
3175
+ [., .]
3176
+ sage: Sylvester._delete_right_most_node(b)
3177
+ .
3178
+ sage: b = BinaryTree([[[], []], None]); ascii_art(b)
3179
+ o
3180
+ /
3181
+ o
3182
+ / \
3183
+ o o
3184
+ sage: ascii_art(Sylvester._delete_right_most_node(b))
3185
+ o
3186
+ / \
3187
+ o o
3188
+ """
3189
+ if b.is_empty():
3190
+ raise ValueError("cannot delete right most node from empty tree")
3191
+ elif b[1].is_empty():
3192
+ return b[0]
3193
+ else:
3194
+ return BinaryTree([b[0], RuleSylvester._delete_right_most_node(b[1])])
3195
+
3196
+ def forward_rule(self, y, t, x, content):
3197
+ r"""
3198
+ Return the output shape given three shapes and the content.
3199
+
3200
+ See [Nze2007]_, page 9.
3201
+
3202
+ INPUT:
3203
+
3204
+ - ``y``, ``t``, ``x`` -- three binary trees from a cell in a growth
3205
+ diagram, labelled as::
3206
+
3207
+ t x
3208
+ y
3209
+
3210
+ - ``content`` -- `0` or `1`; the content of the cell
3211
+
3212
+ OUTPUT: the fourth binary tree ``z``
3213
+
3214
+ EXAMPLES::
3215
+
3216
+ sage: Sylvester = GrowthDiagram.rules.Sylvester()
3217
+ sage: B = BinaryTree; E = B(); N = B([]); L = B([[],None])
3218
+ sage: R = B([None,[]]); T = B([[],[]])
3219
+
3220
+ sage: ascii_art(Sylvester.forward_rule(E, E, E, 1))
3221
+ o
3222
+ sage: ascii_art(Sylvester.forward_rule(N, N, N, 1))
3223
+ o
3224
+ \
3225
+ o
3226
+ sage: ascii_art(Sylvester.forward_rule(L, L, L, 1))
3227
+ o
3228
+ / \
3229
+ o o
3230
+ sage: ascii_art(Sylvester.forward_rule(R, R, R, 1))
3231
+ o
3232
+ \
3233
+ o
3234
+ \
3235
+ o
3236
+
3237
+ If ``y != x``, obtain ``z`` from ``y`` adding a node such
3238
+ that deleting the right most gives ``x``::
3239
+
3240
+ sage: ascii_art(Sylvester.forward_rule(R, N, L, 0))
3241
+ o
3242
+ / \
3243
+ o o
3244
+
3245
+ sage: ascii_art(Sylvester.forward_rule(L, N, R, 0))
3246
+ o
3247
+ /
3248
+ o
3249
+ \
3250
+ o
3251
+
3252
+ If ``y == x != t``, obtain ``z`` from ``x`` by adding a node
3253
+ as left child to the right most node::
3254
+
3255
+ sage: ascii_art(Sylvester.forward_rule(N, E, N, 0))
3256
+ o
3257
+ /
3258
+ o
3259
+ sage: ascii_art(Sylvester.forward_rule(T, L, T, 0))
3260
+ _o_
3261
+ / \
3262
+ o o
3263
+ /
3264
+ o
3265
+ sage: ascii_art(Sylvester.forward_rule(L, N, L, 0))
3266
+ o
3267
+ /
3268
+ o
3269
+ /
3270
+ o
3271
+ sage: ascii_art(Sylvester.forward_rule(R, N, R, 0))
3272
+ o
3273
+ \
3274
+ o
3275
+ /
3276
+ o
3277
+ """
3278
+ def successors(b):
3279
+ r"""
3280
+ Return all trees obtained from ``b`` by adding a node.
3281
+ """
3282
+ if b.is_empty():
3283
+ yield BinaryTree([])
3284
+ else:
3285
+ for t in successors(b[0]):
3286
+ yield BinaryTree([t, b[1]])
3287
+ for t in successors(b[1]):
3288
+ yield BinaryTree([b[0], t])
3289
+
3290
+ def union(y, x):
3291
+ r"""
3292
+ Return the unique tree obtained by adding a node to ``y`` such
3293
+ that deleting the right most node gives ``x``.
3294
+ """
3295
+ for t in successors(y):
3296
+ if RuleSylvester._delete_right_most_node(t) == x:
3297
+ return t
3298
+ raise ValueError("could not find union of %s and %s" % (y,x))
3299
+
3300
+ if y == t == x:
3301
+ if content == 0:
3302
+ z = y
3303
+ elif content == 1:
3304
+ z = t.over(BinaryTree([]))
3305
+ else:
3306
+ raise NotImplementedError
3307
+ elif content != 0:
3308
+ raise ValueError("for y=%s, t=%s, x=%s, the content should be 0 but is %s" % (y, t, x, content))
3309
+ elif y != t == x:
3310
+ z = y
3311
+ elif y == t != x:
3312
+ z = x
3313
+ else:
3314
+ z = union(y, x)
3315
+ return z
3316
+
3317
+ def backward_rule(self, y, z, x):
3318
+ r"""
3319
+ Return the output shape given three shapes and the content.
3320
+
3321
+ See [Nze2007]_, page 9.
3322
+
3323
+ INPUT:
3324
+
3325
+ - ``y``, ``z``, ``x`` -- three binary trees from a cell in a growth
3326
+ diagram, labelled as::
3327
+
3328
+ x
3329
+ y z
3330
+
3331
+ OUTPUT:
3332
+
3333
+ A pair ``(t, content)`` consisting of the shape of the fourth
3334
+ binary tree t and the content of the cell.
3335
+
3336
+ EXAMPLES::
3337
+
3338
+ sage: Sylvester = GrowthDiagram.rules.Sylvester()
3339
+ sage: B = BinaryTree; E = B(); N = B([]); L = B([[],None])
3340
+ sage: R = B([None,[]]); T = B([[],[]])
3341
+
3342
+ sage: ascii_art(Sylvester.backward_rule(E, E, E))
3343
+ ( , 0 )
3344
+ sage: ascii_art(Sylvester.backward_rule(N, N, N))
3345
+ ( o, 0 )
3346
+ """
3347
+ if x == y == z:
3348
+ return (x, 0)
3349
+ elif x == z != y:
3350
+ return (y, 0)
3351
+ elif x != z == y:
3352
+ return (x, 0)
3353
+ else:
3354
+ if x == y and z == x.over(BinaryTree([])):
3355
+ return (x, 1)
3356
+ else:
3357
+ t = RuleSylvester._delete_right_most_node(y)
3358
+ return (t, 0)
3359
+
3360
+
3361
+ class RuleYoungFibonacci(Rule):
3362
+ r"""
3363
+ A rule modelling a Schensted-like correspondence for
3364
+ Young-Fibonacci-tableaux.
3365
+
3366
+ EXAMPLES::
3367
+
3368
+ sage: YF = GrowthDiagram.rules.YoungFibonacci()
3369
+ sage: GrowthDiagram(YF, [3,1,2])
3370
+ 0 1 0
3371
+ 0 0 1
3372
+ 1 0 0
3373
+
3374
+ The vertices of the dual graded graph are Fibonacci words -
3375
+ compositions into parts of size at most two::
3376
+
3377
+ sage: YF.vertices(4)
3378
+ [word: 22, word: 211, word: 121, word: 112, word: 1111]
3379
+
3380
+ Note that, instead of passing the rule to :class:`GrowthDiagram`,
3381
+ we can also use call the rule to create growth diagrams. For
3382
+ example::
3383
+
3384
+ sage: G = YF([2, 3, 7, 4, 1, 6, 5]); G
3385
+ 0 0 0 0 1 0 0
3386
+ 1 0 0 0 0 0 0
3387
+ 0 1 0 0 0 0 0
3388
+ 0 0 0 1 0 0 0
3389
+ 0 0 0 0 0 0 1
3390
+ 0 0 0 0 0 1 0
3391
+ 0 0 1 0 0 0 0
3392
+
3393
+ The Kleitman Greene invariant is: take the last letter and the
3394
+ largest letter of the permutation and remove them. If they
3395
+ coincide write 1, otherwise write 2::
3396
+
3397
+ sage: G.P_chain()[-1]
3398
+ word: 21211
3399
+
3400
+ TESTS::
3401
+
3402
+ sage: YF = GrowthDiagram.rules.YoungFibonacci()
3403
+ sage: YF.zero
3404
+ word:
3405
+
3406
+ Check duality::
3407
+
3408
+ sage: YF._check_duality(4)
3409
+
3410
+ sage: G = YF(labels=[[1],[1,0],[1]])
3411
+ Traceback (most recent call last):
3412
+ ...
3413
+ ValueError: 0 not in alphabet
3414
+
3415
+ sage: G = YF(labels=[[1,1],[1,2]])
3416
+ Traceback (most recent call last):
3417
+ ...
3418
+ ValueError: 11 has smaller rank than 12 but is not covered by it in Q
3419
+
3420
+ sage: G = YF(labels=[[1,2],[1,1]])
3421
+ Traceback (most recent call last):
3422
+ ...
3423
+ ValueError: 11 has smaller rank than 12 but is not covered by it in P
3424
+
3425
+ sage: all(YF(labels=YF(pi).out_labels()).to_word()
3426
+ ....: == pi for pi in Permutations(4))
3427
+ True
3428
+ sage: pi = Permutations(10).random_element()
3429
+ sage: G = YF(pi)
3430
+ sage: list(YF(labels=G.out_labels())) == list(G)
3431
+ True
3432
+ """
3433
+ zero = Word([], alphabet=[1,2])
3434
+
3435
+ def normalize_vertex(self, v):
3436
+ r"""
3437
+ Return ``v`` as a word with letters 1 and 2.
3438
+
3439
+ EXAMPLES::
3440
+
3441
+ sage: YF = GrowthDiagram.rules.YoungFibonacci()
3442
+ sage: YF.normalize_vertex([1,2,1]).parent()
3443
+ Finite words over {1, 2}
3444
+ """
3445
+ return Word(v, alphabet=[1,2])
3446
+
3447
+ def vertices(self, n):
3448
+ r"""
3449
+ Return the vertices of the dual graded graph on level ``n``.
3450
+
3451
+ EXAMPLES::
3452
+
3453
+ sage: YF = GrowthDiagram.rules.YoungFibonacci()
3454
+ sage: YF.vertices(3)
3455
+ [word: 21, word: 12, word: 111]
3456
+ """
3457
+ if n == 0:
3458
+ return [self.zero]
3459
+ else:
3460
+ return [Word(list(w), [1,2]) for w in Compositions(n, max_part=2)]
3461
+
3462
+ def rank(self, v):
3463
+ r"""
3464
+ Return the rank of ``v``: the size of the corresponding composition.
3465
+
3466
+ EXAMPLES::
3467
+
3468
+ sage: YF = GrowthDiagram.rules.YoungFibonacci()
3469
+ sage: YF.rank(YF.vertices(3)[0])
3470
+ 3
3471
+ """
3472
+ return sum(v)
3473
+
3474
+ def is_P_edge(self, v, w):
3475
+ r"""
3476
+ Return whether ``(v, w)`` is a `P`-edge of ``self``.
3477
+
3478
+ ``(v, w)`` is an edge if ``v`` is obtained from ``w`` by deleting
3479
+ a ``1`` or replacing the left-most ``2`` by a ``1``.
3480
+
3481
+ EXAMPLES::
3482
+
3483
+ sage: YF = GrowthDiagram.rules.YoungFibonacci()
3484
+ sage: v = YF.vertices(5)[5]; v
3485
+ word: 1121
3486
+ sage: [w for w in YF.vertices(6) if YF.is_P_edge(v, w)]
3487
+ [word: 2121, word: 11121]
3488
+ sage: [w for w in YF.vertices(7) if YF.is_P_edge(v, w)]
3489
+ []
3490
+ """
3491
+ if sum(w) != sum(v) + 1:
3492
+ return False
3493
+ ell = len(v)
3494
+ w = list(w)
3495
+ for i in range(ell+1):
3496
+ d = list(v)
3497
+ d.insert(i, 1)
3498
+ if w == d:
3499
+ return True
3500
+ if i < ell and v[i] == 1:
3501
+ d = list(v)
3502
+ d[i] = 2
3503
+ if w == d:
3504
+ return True
3505
+ break
3506
+ return False
3507
+
3508
+ is_Q_edge = is_P_edge
3509
+
3510
+ def forward_rule(self, y, t, x, content):
3511
+ r"""
3512
+ Return the output shape given three shapes and the content.
3513
+
3514
+ See [Fom1995]_ Lemma 4.4.1, page 35.
3515
+
3516
+ INPUT:
3517
+
3518
+ - ``y``, ``t``, ``x`` -- three Fibonacci words from a
3519
+ cell in a growth diagram, labelled as::
3520
+
3521
+ t x
3522
+ y
3523
+
3524
+ - ``content`` -- `0` or `1`; the content of the cell
3525
+
3526
+ OUTPUT: the fourth Fibonacci word
3527
+
3528
+ EXAMPLES::
3529
+
3530
+ sage: YF = GrowthDiagram.rules.YoungFibonacci()
3531
+
3532
+ sage: YF.forward_rule([], [], [], 1)
3533
+ word: 1
3534
+
3535
+ sage: YF.forward_rule([1], [1], [1], 1)
3536
+ word: 11
3537
+
3538
+ sage: YF.forward_rule([1,2], [1], [1,1], 0)
3539
+ word: 21
3540
+
3541
+ sage: YF.forward_rule([1,1], [1], [1,1], 0)
3542
+ word: 21
3543
+ """
3544
+ if x == t == y:
3545
+ if content == 0:
3546
+ r = x
3547
+ elif content == 1:
3548
+ r = Word([1] + list(y), alphabet=[1,2])
3549
+ else:
3550
+ raise NotImplementedError
3551
+ elif content != 0:
3552
+ raise ValueError("for y=%s, t=%s, x=%s, the content should be 0 but is %s"
3553
+ % (y, t, x, content))
3554
+ elif x == t:
3555
+ r = y
3556
+ elif y == t:
3557
+ r = x
3558
+ else:
3559
+ if x != t != y:
3560
+ r = Word([2] + list(t), alphabet=[1,2])
3561
+ else:
3562
+ raise NotImplementedError("for y=%s, t=%s, x=%s, content %s we have no rule"
3563
+ % (y, t, x, content))
3564
+ return r
3565
+
3566
+ def backward_rule(self, y, z, x):
3567
+ r"""
3568
+ Return the content and the input shape.
3569
+
3570
+ See [Fom1995]_ Lemma 4.4.1, page 35.
3571
+
3572
+ - ``y``, ``z``, ``x`` -- three Fibonacci words from a cell in a
3573
+ growth diagram, labelled as::
3574
+
3575
+ x
3576
+ y z
3577
+
3578
+ OUTPUT:
3579
+
3580
+ A pair ``(t, content)`` consisting of the shape of the fourth
3581
+ word and the content of the cell.
3582
+
3583
+ TESTS::
3584
+
3585
+ sage: YF = GrowthDiagram.rules.YoungFibonacci()
3586
+ sage: w = [4,1,8,3,6,5,2,7,9]; G = YF(w)
3587
+ sage: GrowthDiagram(YF, labels=G.out_labels()).to_word() == w # indirect doctest
3588
+ True
3589
+ """
3590
+ if x == y == z:
3591
+ return (x, 0)
3592
+ elif x == z != y:
3593
+ return (y, 0)
3594
+ elif x != z == y:
3595
+ return (x, 0)
3596
+ else:
3597
+ if z[0] == 1:
3598
+ return (z[1:], 1)
3599
+ elif z[0] == 2:
3600
+ return (z[1:], 0)
3601
+
3602
+
3603
+ class RulePartitions(Rule):
3604
+ r"""
3605
+ A rule for growth diagrams on Young's lattice on integer
3606
+ partitions graded by size.
3607
+
3608
+ TESTS::
3609
+
3610
+ sage: Burge = GrowthDiagram.rules.Burge()
3611
+ sage: Burge.zero
3612
+ []
3613
+ sage: G = GrowthDiagram(Burge, labels=[[1],[1]])
3614
+ Traceback (most recent call last):
3615
+ ...
3616
+ ValueError: can only determine the shape of the growth diagram
3617
+ if ranks of successive labels differ
3618
+ """
3619
+ zero = _make_partition([])
3620
+
3621
+ def vertices(self, n):
3622
+ r"""
3623
+ Return the vertices of the dual graded graph on level ``n``.
3624
+
3625
+ EXAMPLES::
3626
+
3627
+ sage: RSK = GrowthDiagram.rules.RSK()
3628
+ sage: RSK.vertices(3)
3629
+ Partitions of the integer 3
3630
+ """
3631
+ return Partitions(n)
3632
+
3633
+ def normalize_vertex(self, v):
3634
+ r"""
3635
+ Return ``v`` as a partition.
3636
+
3637
+ EXAMPLES::
3638
+
3639
+ sage: RSK = GrowthDiagram.rules.RSK()
3640
+ sage: RSK.normalize_vertex([3,1]).parent()
3641
+ Partitions
3642
+ """
3643
+ return _make_partition(v)
3644
+
3645
+ def rank(self, v):
3646
+ r"""
3647
+ Return the rank of ``v``: the size of the partition.
3648
+
3649
+ EXAMPLES::
3650
+
3651
+ sage: RSK = GrowthDiagram.rules.RSK()
3652
+ sage: RSK.rank(RSK.vertices(3)[0])
3653
+ 3
3654
+ """
3655
+ return v.size()
3656
+
3657
+ def P_symbol(self, P_chain):
3658
+ r"""
3659
+ Return the labels along the vertical boundary of a rectangular
3660
+ growth diagram as a (skew) tableau.
3661
+
3662
+ EXAMPLES::
3663
+
3664
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
3665
+ sage: G = RuleRSK([[0,1,0], [1,0,2]])
3666
+ sage: G.P_symbol().pp()
3667
+ 1 2 2
3668
+ 2
3669
+ """
3670
+ return SkewTableau(chain=P_chain)
3671
+
3672
+ def Q_symbol(self, Q_chain):
3673
+ r"""
3674
+ Return the labels along the horizontal boundary of a rectangular
3675
+ growth diagram as a skew tableau.
3676
+
3677
+ EXAMPLES::
3678
+
3679
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
3680
+ sage: G = RuleRSK([[0,1,0], [1,0,2]])
3681
+ sage: G.Q_symbol().pp()
3682
+ 1 3 3
3683
+ 2
3684
+ """
3685
+ return SkewTableau(chain=Q_chain)
3686
+
3687
+
3688
+ class RuleRSK(RulePartitions):
3689
+ r"""
3690
+ A rule modelling Robinson-Schensted-Knuth insertion.
3691
+
3692
+ EXAMPLES::
3693
+
3694
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
3695
+ sage: GrowthDiagram(RuleRSK, [3,2,1,2,3])
3696
+ 0 0 1 0 0
3697
+ 0 1 0 1 0
3698
+ 1 0 0 0 1
3699
+
3700
+ The vertices of the dual graded graph are integer partitions::
3701
+
3702
+ sage: RuleRSK.vertices(3)
3703
+ Partitions of the integer 3
3704
+
3705
+ The local rules implemented provide the RSK correspondence
3706
+ between matrices with nonnegative integer entries and pairs of
3707
+ semistandard tableaux, the
3708
+ :meth:`~sage.combinat.growth.RulePartitions.P_symbol` and the
3709
+ :meth:`~sage.combinat.growth.RulePartitions.Q_symbol`. For
3710
+ permutations, it reduces to classical Schensted insertion.
3711
+
3712
+ Instead of passing the rule to :class:`GrowthDiagram`, we can
3713
+ also call the rule to create growth diagrams. For example::
3714
+
3715
+ sage: # needs sage.modules
3716
+ sage: m = matrix([[0,0,0,0,1],[1,1,0,2,0], [0,3,0,0,0]])
3717
+ sage: G = RuleRSK(m); G
3718
+ 0 0 0 0 1
3719
+ 1 1 0 2 0
3720
+ 0 3 0 0 0
3721
+ sage: ascii_art([G.P_symbol(), G.Q_symbol()])
3722
+ [ 1 2 2 2 3 1 2 2 2 2 ]
3723
+ [ 2 3 4 4 ]
3724
+ [ 3 , 5 ]
3725
+
3726
+ For rectangular fillings, the Kleitman-Greene invariant is the
3727
+ shape of the :meth:`P_symbol` (or the :meth:`Q_symbol`). Put
3728
+ differently, it is the partition labelling the lower right corner
3729
+ of the filling (recall that we are using matrix coordinates). It
3730
+ can be computed alternatively as the partition
3731
+ `(\mu_1,\dots,\mu_n)`, where `\mu_1 + \dots + \mu_i` is the
3732
+ maximal sum of entries in a collection of `i` pairwise disjoint
3733
+ sequences of cells with weakly increasing coordinates.
3734
+
3735
+ For rectangular fillings, we could also use the (faster)
3736
+ implementation provided via :func:`~sage.combinat.rsk.RSK`.
3737
+ Because the of the coordinate conventions in
3738
+ :func:`~sage.combinat.rsk.RSK`, we have to transpose matrices::
3739
+
3740
+ sage: # needs sage.modules
3741
+ sage: [G.P_symbol(), G.Q_symbol()] == RSK(m.transpose())
3742
+ True
3743
+ sage: n = 5; l = [(pi, RuleRSK(pi)) for pi in Permutations(n)]
3744
+ sage: all([G.P_symbol(), G.Q_symbol()] == RSK(pi) for pi, G in l)
3745
+ True
3746
+ sage: n = 5; l = [(w, RuleRSK(w)) for w in Words([1,2,3], 5)]
3747
+ sage: all([G.P_symbol(), G.Q_symbol()] == RSK(pi) for pi, G in l)
3748
+ True
3749
+ """
3750
+
3751
+ def forward_rule(self, y, t, x, content):
3752
+ r"""
3753
+ Return the output shape given three shapes and the content.
3754
+
3755
+ See [Kra2006]_ `(F^1 0)-(F^1 2)`.
3756
+
3757
+ INPUT:
3758
+
3759
+ - ``y``, ``t``, ``x`` -- three partitions from a cell in a
3760
+ growth diagram, labelled as::
3761
+
3762
+ t x
3763
+ y
3764
+
3765
+ - ``content`` -- nonnegative integer; the content of the cell
3766
+
3767
+ OUTPUT:
3768
+
3769
+ The fourth partition according to the Robinson-Schensted-Knuth
3770
+ correspondence.
3771
+
3772
+ EXAMPLES::
3773
+
3774
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
3775
+ sage: RuleRSK.forward_rule([2,1],[2,1],[2,1],1)
3776
+ [3, 1]
3777
+
3778
+ sage: RuleRSK.forward_rule([1],[],[2],2)
3779
+ [4, 1]
3780
+ """
3781
+ carry = content
3782
+ z = []
3783
+ while True:
3784
+ if x == []:
3785
+ row1 = 0
3786
+ else:
3787
+ row1 = x[0]
3788
+ if y == []:
3789
+ row3 = 0
3790
+ else:
3791
+ row3 = y[0]
3792
+ newPart = max(row1, row3) + carry
3793
+ if newPart == 0:
3794
+ # returning this as a Partition costs a lot of time
3795
+ return z[::-1]
3796
+ else:
3797
+ z = [newPart] + z
3798
+ if t == []:
3799
+ carry = min(row1, row3)
3800
+ else:
3801
+ carry = min(row1, row3) - t[0]
3802
+ x = x[1:]
3803
+ t = t[1:]
3804
+ y = y[1:]
3805
+
3806
+ def backward_rule(self, y, z, x):
3807
+ r"""
3808
+ Return the content and the input shape.
3809
+
3810
+ See [Kra2006]_ `(B^1 0)-(B^1 2)`.
3811
+
3812
+ INPUT:
3813
+
3814
+ - ``y``, ``z``, ``x`` -- three partitions from a cell in a
3815
+ growth diagram, labelled as::
3816
+
3817
+ x
3818
+ y z
3819
+
3820
+ OUTPUT:
3821
+
3822
+ A pair ``(t, content)`` consisting of the shape of the fourth
3823
+ word according to the Robinson-Schensted-Knuth correspondence
3824
+ and the content of the cell.
3825
+
3826
+ TESTS::
3827
+
3828
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
3829
+ sage: w = [4,1,8,3,6,5,2,7,9]; G = RuleRSK(w)
3830
+ sage: GrowthDiagram(RuleRSK, labels=G._out_labels).to_word() == w # indirect doctest
3831
+ True
3832
+ """
3833
+ carry = 0
3834
+ i = len(z)
3835
+ t = []
3836
+ while i > 0:
3837
+ if len(x) < i:
3838
+ row1 = 0
3839
+ else:
3840
+ row1 = x[i-1]
3841
+ if len(y) < i:
3842
+ row3 = 0
3843
+ else:
3844
+ row3 = y[i-1]
3845
+ t = [min(row1, row3) - carry] + t
3846
+ carry = z[i-1] - max(row1, row3)
3847
+ i = i-1
3848
+ return (_make_partition(t), carry)
3849
+
3850
+
3851
+ class RuleBurge(RulePartitions):
3852
+ r"""
3853
+ A rule modelling Burge insertion.
3854
+
3855
+ EXAMPLES::
3856
+
3857
+ sage: Burge = GrowthDiagram.rules.Burge()
3858
+ sage: GrowthDiagram(Burge, labels=[[],[1,1,1],[2,1,1,1],[2,1,1],[2,1],[1,1],[]])
3859
+ 1 1
3860
+ 0 1
3861
+ 1 0
3862
+ 1 0
3863
+
3864
+ The vertices of the dual graded graph are integer partitions::
3865
+
3866
+ sage: Burge.vertices(3)
3867
+ Partitions of the integer 3
3868
+
3869
+ The local rules implemented provide Burge's correspondence
3870
+ between matrices with nonnegative integer entries and pairs of
3871
+ semistandard tableaux, the
3872
+ :meth:`~sage.combinat.growth.RulePartitions.P_symbol` and the
3873
+ :meth:`~sage.combinat.growth.RulePartitions.Q_symbol`. For
3874
+ permutations, it reduces to classical Schensted insertion.
3875
+
3876
+ Instead of passing the rule to :class:`GrowthDiagram`, we can
3877
+ also call the rule to create growth diagrams. For example::
3878
+
3879
+ sage: # needs sage.modules
3880
+ sage: m = matrix([[2,0,0,1,0],[1,1,0,0,0], [0,0,0,0,3]])
3881
+ sage: G = Burge(m); G
3882
+ 2 0 0 1 0
3883
+ 1 1 0 0 0
3884
+ 0 0 0 0 3
3885
+ sage: ascii_art([G.P_symbol(), G.Q_symbol()])
3886
+ [ 1 2 3 1 2 5 ]
3887
+ [ 1 3 1 5 ]
3888
+ [ 1 3 1 5 ]
3889
+ [ 2 , 4 ]
3890
+
3891
+ For rectangular fillings, the Kleitman-Greene invariant is the
3892
+ shape of the
3893
+ :meth:`~sage.combinat.growth.RulePartitions.P_symbol`. Put
3894
+ differently, it is the partition labelling the lower right corner
3895
+ of the filling (recall that we are using matrix coordinates). It
3896
+ can be computed alternatively as the transpose of the partition
3897
+ `(\mu_1, \ldots, \mu_n)`, where `\mu_1 + \cdots + \mu_i` is the
3898
+ maximal sum of entries in a collection of `i` pairwise disjoint
3899
+ sequences of cells with weakly decreasing row indices and weakly
3900
+ increasing column indices.
3901
+ """
3902
+
3903
+ def forward_rule(self, y, t, x, content):
3904
+ r"""
3905
+ Return the output shape given three shapes and the content.
3906
+
3907
+ See [Kra2006]_ `(F^4 0)-(F^4 2)`.
3908
+
3909
+ INPUT:
3910
+
3911
+ - ``y``, ``t``, ``x`` -- three from a cell in a growth diagram,
3912
+ labelled as::
3913
+
3914
+ t x
3915
+ y
3916
+
3917
+ - ``content`` -- nonnegative integer; the content of the cell
3918
+
3919
+ OUTPUT: the fourth partition according to the Burge correspondence
3920
+
3921
+ EXAMPLES::
3922
+
3923
+ sage: Burge = GrowthDiagram.rules.Burge()
3924
+ sage: Burge.forward_rule([2,1],[2,1],[2,1],1)
3925
+ [3, 1]
3926
+
3927
+ sage: Burge.forward_rule([1],[],[2],2)
3928
+ [2, 1, 1, 1]
3929
+ """
3930
+ # n is the maximal length of longest decreasing chain by
3931
+ # Kleitman-Greene's theorem
3932
+ n = content + len(x) + len(y)
3933
+ x += [0]*(n-len(x))
3934
+ y += [0]*(n-len(y))
3935
+ t += [0]*(n-len(t))
3936
+ z = [0]*n
3937
+ carry = content
3938
+ for i, (row1, row2, row3) in enumerate(zip(x, t, y)):
3939
+ s = min(int(row1 == row2 == row3), carry)
3940
+ new_part = max(row1, row3) + s
3941
+ if new_part:
3942
+ z[i] = new_part
3943
+ carry += -s + min(row1, row3) - row2
3944
+ else:
3945
+ break
3946
+ return _make_partition(z)
3947
+
3948
+ def backward_rule(self, y, z, x):
3949
+ r"""
3950
+ Return the content and the input shape.
3951
+
3952
+ See [Kra2006]_ `(B^4 0)-(B^4 2)`. (In the arXiv version of
3953
+ the article there is a typo: in the computation of carry in
3954
+ `(B^4 2)` , `\rho` must be replaced by `\lambda`).
3955
+
3956
+ INPUT:
3957
+
3958
+ - ``y``, ``z``, ``x`` -- three partitions from a cell in a
3959
+ growth diagram, labelled as::
3960
+
3961
+ x
3962
+ y z
3963
+
3964
+ OUTPUT:
3965
+
3966
+ A pair ``(t, content)`` consisting of the shape of the fourth
3967
+ partition according to the Burge correspondence and the content of
3968
+ the cell.
3969
+
3970
+ EXAMPLES::
3971
+
3972
+ sage: Burge = GrowthDiagram.rules.Burge()
3973
+ sage: Burge.backward_rule([1,1,1],[2,1,1,1],[2,1,1])
3974
+ ([1, 1], 0)
3975
+
3976
+ TESTS::
3977
+
3978
+ sage: w = [4,1,8,3,6,5,2,7,9]; G = Burge(w)
3979
+ sage: GrowthDiagram(Burge, labels=G._out_labels).to_word() == w # indirect doctest
3980
+ True
3981
+ """
3982
+ t = [0]*len(z) # z must be the longest partition
3983
+ mu = [0]*(len(z)-len(x)) + x[::-1]
3984
+ nu = [0]*(len(z)-len(y)) + y[::-1]
3985
+ la = z[::-1]
3986
+ carry = 0
3987
+ for i, (mu_i, la_i, nu_i) in enumerate(zip(mu, la, nu)):
3988
+ s = min(int(mu_i == nu_i == la_i), carry)
3989
+ t[i] = min(mu_i, nu_i) - s
3990
+ carry += -s + la_i - max(mu_i, nu_i)
3991
+ t.reverse()
3992
+ return (_make_partition(t), carry)
3993
+
3994
+
3995
+ class RuleDomino(Rule):
3996
+ r"""
3997
+ A rule modelling domino insertion.
3998
+
3999
+ EXAMPLES::
4000
+
4001
+ sage: Domino = GrowthDiagram.rules.Domino()
4002
+ sage: GrowthDiagram(Domino, [[1,0,0],[0,0,1],[0,-1,0]])
4003
+ 1 0 0
4004
+ 0 0 1
4005
+ 0 -1 0
4006
+
4007
+ The vertices of the dual graded graph are integer partitions
4008
+ whose Ferrers diagram can be tiled with dominoes::
4009
+
4010
+ sage: Domino.vertices(2)
4011
+ [[4], [3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]]
4012
+
4013
+ Instead of passing the rule to :class:`GrowthDiagram`, we can
4014
+ also call the rule to create growth diagrams. For example, let
4015
+ us check Figure 3 in [Lam2004]_::
4016
+
4017
+ sage: G = Domino([[0,0,0,-1],[0,0,1,0],[-1,0,0,0],[0,1,0,0]]); G
4018
+ 0 0 0 -1
4019
+ 0 0 1 0
4020
+ -1 0 0 0
4021
+ 0 1 0 0
4022
+
4023
+ sage: ascii_art([G.P_symbol(), G.Q_symbol()])
4024
+ [ 1 2 4 1 2 2 ]
4025
+ [ 1 2 4 1 3 3 ]
4026
+ [ 3 3 , 4 4 ]
4027
+
4028
+ The spin of a domino tableau is half the number of vertical dominoes::
4029
+
4030
+ sage: def spin(T):
4031
+ ....: return sum(2*len(set(row)) - len(row) for row in T)/4
4032
+
4033
+ According to [Lam2004]_, the number of negative entries in the
4034
+ signed permutation equals the sum of the spins of the two
4035
+ associated tableaux::
4036
+
4037
+ sage: pi = [3,-1,2,4,-5]
4038
+ sage: G = Domino(pi)
4039
+ sage: list(G.filling().values()).count(-1) == spin(G.P_symbol()) + spin(G.Q_symbol())
4040
+ True
4041
+
4042
+ Negating all signs transposes all the partitions::
4043
+
4044
+ sage: G.P_symbol() == Domino([-e for e in pi]).P_symbol().conjugate()
4045
+ True
4046
+
4047
+ TESTS:
4048
+
4049
+ Check duality::
4050
+
4051
+ sage: Domino = GrowthDiagram.rules.Domino()
4052
+ sage: Domino._check_duality(3)
4053
+
4054
+ sage: G = Domino([[0,1,0],[0,0,-1],[1,0,0]]); G
4055
+ 0 1 0
4056
+ 0 0 -1
4057
+ 1 0 0
4058
+
4059
+ sage: ascii_art([G.P_symbol(), G.Q_symbol()])
4060
+ [ 1 1 1 1 ]
4061
+ [ 2 3 2 2 ]
4062
+ [ 2 3, 3 3 ]
4063
+
4064
+ sage: l = {pi: Domino(pi) for pi in SignedPermutations(4)}
4065
+ sage: S = Set([(G.P_symbol(), G.Q_symbol()) for G in l.values()])
4066
+ sage: S.cardinality()
4067
+ 384
4068
+
4069
+ Check the color-to-spin property for all permutations of size 4::
4070
+
4071
+ sage: all(list(G.filling().values()).count(-1) == spin(G.P_symbol()) + spin(G.Q_symbol())
4072
+ ....: for G in l.values())
4073
+ True
4074
+
4075
+ Negating all signs transposes all the partitions::
4076
+
4077
+ sage: W = SignedPermutations(4)
4078
+ sage: all(l[pi].P_symbol() == l[W([-e for e in pi])].P_symbol().conjugate()
4079
+ ....: for pi in l)
4080
+ True
4081
+
4082
+ Check part of Theorem 4.2.3 in [Lee1996]_::
4083
+
4084
+ sage: def to_permutation(pi):
4085
+ ....: pi1 = list(pi)
4086
+ ....: n = len(pi1)
4087
+ ....: pi2 = [-e for e in pi][::-1] + pi1
4088
+ ....: return Permutation([e+n+1 if e<0 else e+n for e in pi2])
4089
+ sage: RuleRSK = GrowthDiagram.rules.RSK()
4090
+ sage: def good(pi):
4091
+ ....: return Domino(pi).P_chain()[-1] == RuleRSK(to_permutation(pi)).P_chain()[-1]
4092
+ sage: all(good(pi) for pi in SignedPermutations(4))
4093
+ True
4094
+
4095
+ sage: G = Domino(labels=[[1],[2,1]])
4096
+ Traceback (most recent call last):
4097
+ ...
4098
+ ValueError: [1] has smaller rank than [2, 1] but is not covered by it in Q
4099
+
4100
+ sage: G = Domino(labels=[[2,1],[1]])
4101
+ Traceback (most recent call last):
4102
+ ...
4103
+ ValueError: [1] has smaller rank than [2, 1] but is not covered by it in P
4104
+ """
4105
+ r = 2
4106
+ zero = _make_partition([])
4107
+
4108
+ def normalize_vertex(self, v):
4109
+ """
4110
+ Return ``v`` as a partition.
4111
+
4112
+ EXAMPLES::
4113
+
4114
+ sage: Domino = GrowthDiagram.rules.Domino()
4115
+ sage: Domino.normalize_vertex([3,1]).parent()
4116
+ Partitions
4117
+ """
4118
+ return _make_partition(v)
4119
+
4120
+ def vertices(self, n):
4121
+ r"""
4122
+ Return the vertices of the dual graded graph on level ``n``.
4123
+
4124
+ EXAMPLES::
4125
+
4126
+ sage: Domino = GrowthDiagram.rules.Domino()
4127
+ sage: Domino.vertices(2)
4128
+ [[4], [3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]]
4129
+ """
4130
+ return [la for la in Partitions(2*n) if len(la.core(2)) == 0]
4131
+
4132
+ def rank(self, v):
4133
+ r"""
4134
+ Return the rank of ``v``.
4135
+
4136
+ The rank of a vertex is half the size of the partition,
4137
+ which equals the number of dominoes in any filling.
4138
+
4139
+ EXAMPLES::
4140
+
4141
+ sage: Domino = GrowthDiagram.rules.Domino()
4142
+ sage: Domino.rank(Domino.vertices(3)[0])
4143
+ 3
4144
+ """
4145
+ return v.size() // 2
4146
+
4147
+ def is_P_edge(self, v, w):
4148
+ r"""
4149
+ Return whether ``(v, w)`` is a `P`-edge of ``self``.
4150
+
4151
+ ``(v, w)`` is an edge if ``v`` is obtained from ``w`` by deleting
4152
+ a domino.
4153
+
4154
+ EXAMPLES::
4155
+
4156
+ sage: Domino = GrowthDiagram.rules.Domino()
4157
+ sage: v = Domino.vertices(2)[1]; ascii_art(v)
4158
+ ***
4159
+ *
4160
+ sage: ascii_art([w for w in Domino.vertices(3) if Domino.is_P_edge(v, w)])
4161
+ [ *** ]
4162
+ [ * ]
4163
+ [ ***** *** * ]
4164
+ [ * , ***, * ]
4165
+ sage: [w for w in Domino.vertices(4) if Domino.is_P_edge(v, w)]
4166
+ []
4167
+ """
4168
+ try:
4169
+ (row_1, col_1), (row_2, col_2) = SkewPartition([w, v]).cells()
4170
+ except ValueError:
4171
+ return False
4172
+ return row_1 == row_2 or col_1 == col_2
4173
+
4174
+ is_Q_edge = is_P_edge
4175
+
4176
+ def P_symbol(self, P_chain):
4177
+ r"""
4178
+ Return the labels along the vertical boundary of a rectangular
4179
+ growth diagram as a (skew) domino tableau.
4180
+
4181
+ EXAMPLES::
4182
+
4183
+ sage: Domino = GrowthDiagram.rules.Domino()
4184
+ sage: G = Domino([[0,1,0],[0,0,-1],[1,0,0]])
4185
+ sage: G.P_symbol().pp()
4186
+ 1 1
4187
+ 2 3
4188
+ 2 3
4189
+ """
4190
+ return SkewTableau(chain=P_chain)
4191
+
4192
+ Q_symbol = P_symbol
4193
+
4194
+ def forward_rule(self, y, t, x, content):
4195
+ r"""
4196
+ Return the output shape given three shapes and the content.
4197
+
4198
+ See [Lam2004]_ Section 3.1.
4199
+
4200
+ INPUT:
4201
+
4202
+ - ``y``, ``t``, ``x`` -- three partitions from a cell in a
4203
+ growth diagram, labelled as::
4204
+
4205
+ t x
4206
+ y
4207
+
4208
+ - ``content`` -- `-1`, `0` or `1`; the content of the cell
4209
+
4210
+ OUTPUT: the fourth partition according to domino insertion
4211
+
4212
+ EXAMPLES::
4213
+
4214
+ sage: Domino = GrowthDiagram.rules.Domino()
4215
+
4216
+ Rule 1::
4217
+
4218
+ sage: Domino.forward_rule([], [], [], 1)
4219
+ [2]
4220
+
4221
+ sage: Domino.forward_rule([1,1], [1,1], [1,1], 1)
4222
+ [3, 1]
4223
+
4224
+ Rule 2::
4225
+
4226
+ sage: Domino.forward_rule([1,1], [1,1], [1,1], -1)
4227
+ [1, 1, 1, 1]
4228
+
4229
+ Rule 3::
4230
+
4231
+ sage: Domino.forward_rule([1,1], [1,1], [2,2], 0)
4232
+ [2, 2]
4233
+
4234
+ Rule 4::
4235
+
4236
+ sage: Domino.forward_rule([2,2,2], [2,2], [3,3], 0)
4237
+ [3, 3, 2]
4238
+
4239
+ sage: Domino.forward_rule([2], [], [1,1], 0)
4240
+ [2, 2]
4241
+
4242
+ sage: Domino.forward_rule([1,1], [], [2], 0)
4243
+ [2, 2]
4244
+
4245
+ sage: Domino.forward_rule([2], [], [2], 0)
4246
+ [2, 2]
4247
+
4248
+ sage: Domino.forward_rule([4], [2], [4], 0)
4249
+ [4, 2]
4250
+
4251
+ sage: Domino.forward_rule([1,1,1,1], [1,1], [1,1,1,1], 0)
4252
+ [2, 2, 1, 1]
4253
+
4254
+ sage: Domino.forward_rule([2,1,1], [2], [4], 0)
4255
+ [4, 1, 1]
4256
+ """
4257
+ def union(la, mu):
4258
+ r"""
4259
+ Return the union of the two partitions.
4260
+ """
4261
+ return [max(p,q) for (p,q) in zip_longest(la, mu, fillvalue=0)]
4262
+
4263
+ if content not in [0,1,-1]:
4264
+ raise ValueError("domino: the content of the filling must be in {-1,0,1}")
4265
+
4266
+ if content == 1:
4267
+ if not (x == t == y):
4268
+ raise ValueError("all shapes must be equal")
4269
+ if t == []:
4270
+ z = [2]
4271
+ else:
4272
+ z = [t[0] + 2] + t[1:]
4273
+
4274
+ elif content == -1:
4275
+ if not (x == t == y):
4276
+ raise ValueError("all shapes must be equal")
4277
+ z = t + [1,1]
4278
+
4279
+ elif content == 0 and (t == x or t == y):
4280
+ z = union(x, y)
4281
+
4282
+ else:
4283
+ # content == 0 and t differs from x and y by
4284
+ # domino's gamma1 and gamma3
4285
+
4286
+ # the following is certainly very slow
4287
+ gamma3 = set(SkewPartition([y, t]).cells())
4288
+ gamma1 = set(SkewPartition([x, t]).cells())
4289
+ diff = gamma1.intersection(gamma3)
4290
+ cell1, cell2 = gamma3
4291
+ if len(diff) == 0:
4292
+ z = union(x, y)
4293
+
4294
+ elif len(diff) == 1:
4295
+ z = copy(x)
4296
+ # diff is a single cell
4297
+ (k,l) = diff.pop()
4298
+ # add (k+1, l+1) to x
4299
+ # either (k, l+1) or (k+1, l) must also be added
4300
+ if z[k] <= l + 1:
4301
+ z[k] += 1
4302
+ z[k+1] += 1
4303
+ else:
4304
+ if len(z) <= k + 1:
4305
+ z += [2]
4306
+ else:
4307
+ z[k+1] += 2
4308
+
4309
+ # diff has size 2, that is x == y
4310
+ elif cell1[0] == cell2[0]:
4311
+ z = copy(x)
4312
+ # a horizontal domino - add 2 to row below of gamma
4313
+ if len(z) <= cell1[0] + 1:
4314
+ z += [2]
4315
+ else:
4316
+ z[cell1[0]+1] += 2
4317
+
4318
+ else:
4319
+ z = copy(x)
4320
+ # a vertical domino - add 2 to column right of gamma
4321
+ # find first row shorter than cell1[1]+1
4322
+ for r, p in enumerate(z):
4323
+ if p <= cell1[1] + 1:
4324
+ z[r] += 1
4325
+ z[r+1] += 1
4326
+ break
4327
+ else:
4328
+ raise NotImplementedError("domino: cannot call forward rule with shapes %s and content %s"
4329
+ % ((y, t, x), content))
4330
+
4331
+ return z
4332
+
4333
+ #####################################################################
4334
+ ## Set the rules available from GrowthDiagram.rules.<tab>
4335
+ #####################################################################
4336
+
4337
+
4338
+ class Rules:
4339
+ """
4340
+ Catalog of rules for growth diagrams.
4341
+ """
4342
+ ShiftedShapes = RuleShiftedShapes
4343
+ LLMS = RuleLLMS
4344
+ BinaryWord = RuleBinaryWord
4345
+ Sylvester = RuleSylvester
4346
+ YoungFibonacci = RuleYoungFibonacci
4347
+ RSK = RuleRSK
4348
+ Burge = RuleBurge
4349
+ Domino = RuleDomino
4350
+
4351
+
4352
+ GrowthDiagram.rules = Rules