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,2912 @@
1
+ # sage_setup: distribution = sagemath-combinat
2
+ # sage.doctest: needs sage.combinat sage.groups sage.modules
3
+ r"""
4
+ Latin squares
5
+
6
+ A *latin square* of order `n` is an `n \times n` array such that
7
+ each symbol `s \in \{ 0, 1, \dots, n-1\}` appears precisely once in each
8
+ row, and precisely once in each column. A *partial latin square* of
9
+ order `n` is an `n \times n` array such that
10
+ each symbol `s \in \{ 0, 1, \dots, n-1\}` appears at most once in each
11
+ row, and at most once in each column. Empty cells are denoted by `-1`.
12
+ A latin square `L` is a
13
+ *completion* of a partial latin square `P` if `P \subseteq L`. If
14
+ `P` completes to just `L` then `P` *has unique completion*.
15
+
16
+ A *latin bitrade* `(T_1,\, T_2)` is a pair of partial
17
+ latin squares such that:
18
+
19
+ #. `\{ (i,\,j) \mid (i,\,j,\,k) \in T_1 \text{ for some symbol }k \}
20
+ = \{ (i,\,j) \mid (i,\,j,\,k') \in T_2 \text{ for some symbol }k' \};`
21
+
22
+ #. for each `(i,\,j,\,k) \in T_1` and `(i,\,j,\,k') \in T_2`,
23
+ `k \neq k'`;
24
+
25
+ #. the symbols appearing in row `i` of `T_1` are the same as those of
26
+ row `i` of `T_2`; the symbols appearing in column `j` of `T_1` are
27
+ the same as those of column `j` of `T_2`.
28
+
29
+
30
+ Intuitively speaking, a bitrade gives the difference between two latin
31
+ squares, so if `(T_1,\, T_2)` is a bitrade
32
+ for the pair of latin squares `(L_1,\, L_2)`, then
33
+ `L1 = (L2 \setminus T_1) \cup T_2`
34
+ and
35
+ `L2 = (L1 \setminus T_2) \cup T_1`.
36
+
37
+ This file contains
38
+
39
+
40
+ #. LatinSquare class definition;
41
+
42
+ #. some named latin squares (back circulant, forward circulant, abelian
43
+ `2`-group);
44
+
45
+ #. methods :meth:`is_partial_latin_square` and :meth:`is_latin_square` to test
46
+ if a :class:`LatinSquare` object satisfies the definition of a latin square
47
+ or partial latin square, respectively;
48
+
49
+ #. tests for completion and unique completion (these use the C++
50
+ implementation of Knuth's dancing links algorithm to solve the
51
+ problem as a instance of `0-1` matrix exact cover);
52
+
53
+ #. functions for calculating the `\tau_i` representation of a bitrade
54
+ and the genus of the associated hypermap embedding;
55
+
56
+ #. Markov chain of Jacobson and Matthews (1996) for generating latin
57
+ squares uniformly at random (provides a generator interface);
58
+
59
+ #. a few examples of `\tau_i` representations of bitrades constructed
60
+ from the action of a group on itself by right multiplication,
61
+ functions for converting to a pair of :class:`LatinSquare` objects.
62
+
63
+ EXAMPLES::
64
+
65
+ sage: from sage.combinat.matrices.latin import *
66
+ sage: B = back_circulant(5)
67
+ sage: B
68
+ [0 1 2 3 4]
69
+ [1 2 3 4 0]
70
+ [2 3 4 0 1]
71
+ [3 4 0 1 2]
72
+ [4 0 1 2 3]
73
+ sage: B.is_latin_square()
74
+ True
75
+ sage: B[0, 1] = 0
76
+ sage: B.is_latin_square()
77
+ False
78
+
79
+ sage: (a, b, c, G) = alternating_group_bitrade_generators(1)
80
+ sage: (T1, T2) = bitrade_from_group(a, b, c, G)
81
+ sage: T1
82
+ [ 0 -1 3 1]
83
+ [-1 1 0 2]
84
+ [ 1 3 2 -1]
85
+ [ 2 0 -1 3]
86
+ sage: T2
87
+ [ 1 -1 0 3]
88
+ [-1 0 2 1]
89
+ [ 2 1 3 -1]
90
+ [ 0 3 -1 2]
91
+ sage: T1.nr_filled_cells()
92
+ 12
93
+ sage: genus(T1, T2)
94
+ 1
95
+
96
+ .. TODO::
97
+
98
+ #. Latin squares with symbols from a ring instead of the integers
99
+ `\{ 0, 1, \dots, n-1 \}`.
100
+
101
+ #. Isotopism testing of latin squares and bitrades via graph
102
+ isomorphism (nauty?).
103
+
104
+ #. Combinatorial constructions for bitrades.
105
+
106
+
107
+ AUTHORS:
108
+
109
+ - Carlo Hamalainen (2008-03-23): initial version
110
+
111
+
112
+ TESTS::
113
+
114
+ sage: L = elementary_abelian_2group(3)
115
+ sage: L == loads(dumps(L))
116
+ True
117
+ """
118
+ # ****************************************************************************
119
+ # Copyright (C) 2008 Carlo Hamalainen <carlo.hamalainen@gmail.com>,
120
+ #
121
+ # Distributed under the terms of the GNU General Public License (GPL)
122
+ #
123
+ # This code is distributed in the hope that it will be useful,
124
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
125
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
126
+ # General Public License for more details.
127
+ #
128
+ # The full text of the GPL is available at:
129
+ #
130
+ # https://www.gnu.org/licenses/
131
+ # ****************************************************************************
132
+
133
+ from sage.matrix.constructor import matrix
134
+ from sage.rings.integer_ring import ZZ
135
+ from sage.rings.integer import Integer
136
+ from sage.matrix.matrix_integer_dense import Matrix_integer_dense
137
+ from sage.groups.perm_gps.permgroup_element import PermutationGroupElement
138
+ from sage.groups.perm_gps.constructor import PermutationGroupElement as PermutationConstructor
139
+ from sage.libs.gap.libgap import libgap
140
+ from sage.libs.gap.element import GapElement
141
+ from sage.combinat.permutation import Permutation
142
+ from sage.groups.perm_gps.permgroup import PermutationGroup
143
+ from sage.arith.misc import is_prime
144
+ from sage.rings.finite_rings.finite_field_constructor import FiniteField
145
+ from sage.misc.flatten import flatten
146
+
147
+ from .dlxcpp import DLXCPP
148
+ from functools import reduce
149
+
150
+
151
+ class LatinSquare:
152
+ def __init__(self, *args):
153
+ """
154
+ Latin squares.
155
+
156
+ This class implements a latin square of order n with rows and
157
+ columns indexed by the set 0, 1, ..., n-1 and symbols from the same
158
+ set. The underlying latin square is a matrix(ZZ, n, n). If L is a
159
+ latin square, then the cell at row r, column c is empty if and only
160
+ if L[r, c] < 0. In this way we allow partial latin squares and can
161
+ speak of completions to latin squares, etc.
162
+
163
+ There are two ways to declare a latin square:
164
+
165
+ Empty latin square of order n::
166
+
167
+ sage: n = 3
168
+ sage: L = LatinSquare(n)
169
+ sage: L
170
+ [-1 -1 -1]
171
+ [-1 -1 -1]
172
+ [-1 -1 -1]
173
+
174
+ Latin square from a matrix::
175
+
176
+ sage: M = matrix(ZZ, [[0, 1], [2, 3]])
177
+ sage: LatinSquare(M)
178
+ [0 1]
179
+ [2 3]
180
+ """
181
+ if len(args) == 1 and isinstance(args[0], (Integer, int)):
182
+ self.square = matrix(ZZ, args[0], args[0])
183
+ self.clear_cells()
184
+ elif len(args) == 2 and all(isinstance(a, (Integer, int))
185
+ for a in args):
186
+ self.square = matrix(ZZ, args[0], args[1])
187
+ self.clear_cells()
188
+ elif len(args) == 1 and isinstance(args[0], Matrix_integer_dense):
189
+ self.square = args[0]
190
+ else:
191
+ raise TypeError("bad input for latin square")
192
+
193
+ def dumps(self):
194
+ """
195
+ Since the latin square class does not hold any other private
196
+ variables we just call dumps on self.square:
197
+
198
+ EXAMPLES::
199
+
200
+ sage: from sage.combinat.matrices.latin import *
201
+ sage: back_circulant(2) == loads(dumps(back_circulant(2)))
202
+ True
203
+ """
204
+ from sage.misc.persist import dumps
205
+ return dumps(self.square)
206
+
207
+ def __str__(self):
208
+ """
209
+ The string representation of a latin square is the same as the
210
+ underlying matrix.
211
+
212
+ EXAMPLES::
213
+
214
+ sage: print(LatinSquare(matrix(ZZ, [[0, 1], [2, 3]])).__str__())
215
+ [0 1]
216
+ [2 3]
217
+ """
218
+ return str(self.square)
219
+
220
+ def __repr__(self):
221
+ """
222
+ The representation of a latin square is the same as the underlying
223
+ matrix.
224
+
225
+ EXAMPLES::
226
+
227
+ sage: print(LatinSquare(matrix(ZZ, [[0, 1], [2, 3]])).__repr__())
228
+ [0 1]
229
+ [2 3]
230
+ """
231
+ return repr(self.square)
232
+
233
+ def __getitem__(self, rc):
234
+ """
235
+ If L is a LatinSquare then this method allows us to evaluate L[r,
236
+ c].
237
+
238
+ EXAMPLES::
239
+
240
+ sage: from sage.combinat.matrices.latin import *
241
+ sage: B = back_circulant(3)
242
+ sage: B[1, 1]
243
+ 2
244
+ """
245
+
246
+ r = rc[0]
247
+ c = rc[1]
248
+
249
+ return self.square[r, c]
250
+
251
+ def __setitem__(self, rc, val):
252
+ """
253
+ If L is a LatinSquare then this method allows us to set L[r, c].
254
+
255
+ EXAMPLES::
256
+
257
+ sage: from sage.combinat.matrices.latin import *
258
+ sage: B = back_circulant(3)
259
+ sage: B[1, 1] = 10
260
+ sage: B[1, 1]
261
+ 10
262
+ """
263
+
264
+ r = rc[0]
265
+ c = rc[1]
266
+
267
+ self.square[r, c] = val
268
+
269
+ def set_immutable(self):
270
+ """
271
+ A latin square is immutable if the underlying matrix is immutable.
272
+
273
+ EXAMPLES::
274
+
275
+ sage: L = LatinSquare(matrix(ZZ, [[0, 1], [2, 3]]))
276
+ sage: L.set_immutable()
277
+ sage: {L : 0} # this would fail without set_immutable()
278
+ {[0 1]
279
+ [2 3]: 0}
280
+ """
281
+
282
+ self.square.set_immutable()
283
+
284
+ def __hash__(self):
285
+ """
286
+ The hash of a latin square is precisely the hash of the underlying
287
+ matrix.
288
+
289
+ EXAMPLES::
290
+
291
+ sage: L = LatinSquare(matrix(ZZ, [[0, 1], [2, 3]]))
292
+ sage: L.set_immutable()
293
+ sage: L.__hash__()
294
+ 1677951251422179082 # 64-bit
295
+ -479138038 # 32-bit
296
+ """
297
+ return hash(self.square)
298
+
299
+ def __eq__(self, Q):
300
+ """
301
+ Two latin squares are equal if the underlying matrices are equal.
302
+
303
+ EXAMPLES::
304
+
305
+ sage: A = LatinSquare(matrix(ZZ, [[0, 1], [2, 3]]))
306
+ sage: B = LatinSquare(matrix(ZZ, [[0, 4], [2, 3]]))
307
+ sage: A == B
308
+ False
309
+ sage: B[0, 1] = 1
310
+ sage: A == B
311
+ True
312
+ """
313
+
314
+ return self.square == Q.square
315
+
316
+ def __copy__(self):
317
+ """
318
+ To copy a latin square we must copy the underlying matrix.
319
+
320
+ EXAMPLES::
321
+
322
+ sage: A = LatinSquare(matrix(ZZ, [[0, 1], [2, 3]]))
323
+ sage: B = copy(A)
324
+ sage: B
325
+ [0 1]
326
+ [2 3]
327
+ """
328
+ C = LatinSquare(self.square.nrows(), self.square.ncols())
329
+ from copy import copy
330
+ C.square = copy(self.square)
331
+ return C
332
+
333
+ def clear_cells(self):
334
+ """
335
+ Mark every cell in ``self`` as being empty.
336
+
337
+ EXAMPLES::
338
+
339
+ sage: A = LatinSquare(matrix(ZZ, [[0, 1], [2, 3]]))
340
+ sage: A.clear_cells()
341
+ sage: A
342
+ [-1 -1]
343
+ [-1 -1]
344
+ """
345
+ for r in range(self.square.nrows()):
346
+ for c in range(self.square.ncols()):
347
+ self.square[r, c] = -1
348
+
349
+ def nrows(self):
350
+ """
351
+ Number of rows in the latin square.
352
+
353
+ EXAMPLES::
354
+
355
+ sage: LatinSquare(3).nrows()
356
+ 3
357
+ """
358
+ return self.square.nrows()
359
+
360
+ def ncols(self):
361
+ """
362
+ Number of columns in the latin square.
363
+
364
+ EXAMPLES::
365
+
366
+ sage: LatinSquare(3).ncols()
367
+ 3
368
+ """
369
+ return self.square.ncols()
370
+
371
+ def row(self, x):
372
+ """
373
+ Return row x of the latin square.
374
+
375
+ EXAMPLES::
376
+
377
+ sage: from sage.combinat.matrices.latin import *
378
+ sage: back_circulant(3).row(0)
379
+ (0, 1, 2)
380
+ """
381
+ return self.square.row(x)
382
+
383
+ def column(self, x):
384
+ """
385
+ Return column x of the latin square.
386
+
387
+ EXAMPLES::
388
+
389
+ sage: from sage.combinat.matrices.latin import *
390
+ sage: back_circulant(3).column(0)
391
+ (0, 1, 2)
392
+ """
393
+ return self.square.column(x)
394
+
395
+ def list(self):
396
+ """
397
+ Convert the latin square into a list, in a row-wise manner.
398
+
399
+ EXAMPLES::
400
+
401
+ sage: from sage.combinat.matrices.latin import *
402
+ sage: back_circulant(3).list()
403
+ [0, 1, 2, 1, 2, 0, 2, 0, 1]
404
+ """
405
+ return self.square.list()
406
+
407
+ def nr_filled_cells(self):
408
+ """
409
+ Return the number of filled cells (i.e. cells with a positive
410
+ value) in the partial latin square ``self``.
411
+
412
+ EXAMPLES::
413
+
414
+ sage: from sage.combinat.matrices.latin import *
415
+ sage: LatinSquare(matrix([[0, -1], [-1, 0]])).nr_filled_cells()
416
+ 2
417
+ """
418
+ s = 0
419
+ for r in range(self.nrows()):
420
+ for c in range(self.ncols()):
421
+ if self[r, c] >= 0:
422
+ s += 1
423
+ return s
424
+
425
+ def actual_row_col_sym_sizes(self):
426
+ """
427
+ Bitrades sometimes end up in partial latin squares with unused
428
+ rows, columns, or symbols. This function works out the actual
429
+ number of used rows, columns, and symbols.
430
+
431
+ .. warning::
432
+
433
+ We assume that the unused rows/columns occur in the lower
434
+ right of self, and that the used symbols are in the range
435
+ {0, 1, ..., m} (no holes in that list).
436
+
437
+ EXAMPLES::
438
+
439
+ sage: from sage.combinat.matrices.latin import *
440
+ sage: B = back_circulant(3)
441
+ sage: B[0,2] = B[1,2] = B[2,2] = -1
442
+ sage: B[0,0] = B[2,1] = -1
443
+ sage: B
444
+ [-1 1 -1]
445
+ [ 1 2 -1]
446
+ [ 2 -1 -1]
447
+ sage: B.actual_row_col_sym_sizes()
448
+ (3, 2, 2)
449
+ """
450
+ row_max = self.nrows()
451
+ col_max = self.ncols()
452
+ sym_max = self.nr_distinct_symbols()
453
+
454
+ while self.is_empty_row(row_max-1):
455
+ row_max -= 1
456
+ while self.is_empty_column(col_max-1):
457
+ col_max -= 1
458
+
459
+ return row_max, col_max, sym_max
460
+
461
+ def is_empty_column(self, c):
462
+ """
463
+ Check if column c of the partial latin square ``self`` is empty.
464
+
465
+ EXAMPLES::
466
+
467
+ sage: from sage.combinat.matrices.latin import *
468
+ sage: L = back_circulant(4)
469
+ sage: L.is_empty_column(0)
470
+ False
471
+ sage: L[0,0] = L[1,0] = L[2,0] = L[3,0] = -1
472
+ sage: L.is_empty_column(0)
473
+ True
474
+ """
475
+ return list(set(self.column(c))) == [-1]
476
+
477
+ def is_empty_row(self, r):
478
+ """
479
+ Check if row r of the partial latin square ``self`` is empty.
480
+
481
+ EXAMPLES::
482
+
483
+ sage: from sage.combinat.matrices.latin import *
484
+ sage: L = back_circulant(4)
485
+ sage: L.is_empty_row(0)
486
+ False
487
+ sage: L[0,0] = L[0,1] = L[0,2] = L[0,3] = -1
488
+ sage: L.is_empty_row(0)
489
+ True
490
+ """
491
+ return list(set(self.row(r))) == [-1]
492
+
493
+ def nr_distinct_symbols(self):
494
+ """
495
+ Return the number of distinct symbols in the partial latin square
496
+ ``self``.
497
+
498
+ EXAMPLES::
499
+
500
+ sage: from sage.combinat.matrices.latin import *
501
+ sage: back_circulant(5).nr_distinct_symbols()
502
+ 5
503
+ sage: L = LatinSquare(10)
504
+ sage: L.nr_distinct_symbols()
505
+ 0
506
+ sage: L[0, 0] = 0
507
+ sage: L[0, 1] = 1
508
+ sage: L.nr_distinct_symbols()
509
+ 2
510
+ """
511
+ symbols = set(flatten([list(x) for x in list(self.square)]))
512
+ return sum(1 for x in symbols if x >= 0)
513
+
514
+ def apply_isotopism(self, row_perm, col_perm, sym_perm):
515
+ """
516
+ An isotopism is a permutation of the rows, columns, and symbols of
517
+ a partial latin square ``self``. Use isotopism() to convert a tuple
518
+ (indexed from 0) to a Permutation object.
519
+
520
+ EXAMPLES::
521
+
522
+ sage: from sage.combinat.matrices.latin import *
523
+ sage: B = back_circulant(5)
524
+ sage: B
525
+ [0 1 2 3 4]
526
+ [1 2 3 4 0]
527
+ [2 3 4 0 1]
528
+ [3 4 0 1 2]
529
+ [4 0 1 2 3]
530
+ sage: alpha = isotopism((0,1,2,3,4))
531
+ sage: beta = isotopism((1,0,2,3,4))
532
+ sage: gamma = isotopism((2,1,0,3,4))
533
+ sage: B.apply_isotopism(alpha, beta, gamma)
534
+ [3 4 2 0 1]
535
+ [0 2 3 1 4]
536
+ [1 3 0 4 2]
537
+ [4 0 1 2 3]
538
+ [2 1 4 3 0]
539
+ """
540
+ Q = LatinSquare(self.nrows(), self.ncols())
541
+
542
+ for r in range(self.nrows()):
543
+ for c in range(self.ncols()):
544
+ try:
545
+ if self[r, c] < 0:
546
+ s2 = -1
547
+ else:
548
+ s2 = sym_perm[self[r, c]] - 1
549
+ except IndexError:
550
+ s2 = self[r, c] # we must be leaving the symbol fixed?
551
+
552
+ Q[row_perm[r]-1, col_perm[c]-1] = s2
553
+
554
+ return Q
555
+
556
+ def filled_cells_map(self):
557
+ """
558
+ Number the filled cells of ``self`` with integers from {1, 2, 3, ...}.
559
+
560
+ INPUT:
561
+
562
+ - ``self`` -- partial latin square ``self`` (empty cells
563
+ have negative values)
564
+
565
+ OUTPUT:
566
+
567
+ A dictionary ``cells_map`` where ``cells_map[(i,j)] = m`` means that
568
+ ``(i,j)`` is the ``m``-th filled cell in ``P``,
569
+ while ``cells_map[m] = (i,j)``.
570
+
571
+ EXAMPLES::
572
+
573
+ sage: from sage.combinat.matrices.latin import *
574
+ sage: (a, b, c, G) = alternating_group_bitrade_generators(1)
575
+ sage: (T1, T2) = bitrade_from_group(a, b, c, G)
576
+ sage: D = T1.filled_cells_map()
577
+ sage: {i: v for i,v in D.items() if i in ZZ}
578
+ {1: (0, 0),
579
+ 2: (0, 2),
580
+ 3: (0, 3),
581
+ 4: (1, 1),
582
+ 5: (1, 2),
583
+ 6: (1, 3),
584
+ 7: (2, 0),
585
+ 8: (2, 1),
586
+ 9: (2, 2),
587
+ 10: (3, 0),
588
+ 11: (3, 1),
589
+ 12: (3, 3)}
590
+ sage: {i: v for i,v in D.items() if i not in ZZ}
591
+ {(0, 0): 1,
592
+ (0, 2): 2,
593
+ (0, 3): 3,
594
+ (1, 1): 4,
595
+ (1, 2): 5,
596
+ (1, 3): 6,
597
+ (2, 0): 7,
598
+ (2, 1): 8,
599
+ (2, 2): 9,
600
+ (3, 0): 10,
601
+ (3, 1): 11,
602
+ (3, 3): 12}
603
+ """
604
+ cells_map = {}
605
+ k = 1
606
+
607
+ for r in range(self.nrows()):
608
+ for c in range(self.ncols()):
609
+ e = self[r, c]
610
+
611
+ if e < 0:
612
+ continue
613
+
614
+ cells_map[(r, c)] = k
615
+ cells_map[k] = (r, c)
616
+
617
+ k += 1
618
+
619
+ return cells_map
620
+
621
+ def top_left_empty_cell(self):
622
+ """
623
+ Return the least ``[r, c]`` such that ``self[r, c]`` is an empty cell.
624
+ If all cells are filled then we return ``None``.
625
+
626
+ INPUT:
627
+
628
+ - ``self`` -- LatinSquare
629
+
630
+ EXAMPLES::
631
+
632
+ sage: from sage.combinat.matrices.latin import *
633
+ sage: B = back_circulant(5)
634
+ sage: B[3, 4] = -1
635
+ sage: B.top_left_empty_cell()
636
+ [3, 4]
637
+ """
638
+ for r in range(self.nrows()):
639
+ for c in range(self.ncols()):
640
+ if self[r, c] < 0:
641
+ return [r, c]
642
+
643
+ return None
644
+
645
+ def is_partial_latin_square(self):
646
+ """
647
+ ``self`` is a partial latin square if it is an n by n matrix, and each
648
+ symbol in [0, 1, ..., n-1] appears at most once in each row, and at
649
+ most once in each column.
650
+
651
+ EXAMPLES::
652
+
653
+ sage: from sage.combinat.matrices.latin import *
654
+ sage: LatinSquare(4).is_partial_latin_square()
655
+ True
656
+ sage: back_circulant(3).gcs().is_partial_latin_square()
657
+ True
658
+ sage: back_circulant(6).is_partial_latin_square()
659
+ True
660
+ """
661
+
662
+ assert self.nrows() == self.ncols()
663
+
664
+ n = self.nrows()
665
+
666
+ for r in range(n):
667
+ vals_in_row = {}
668
+
669
+ for c in range(n):
670
+ e = self[r, c]
671
+
672
+ if e < 0:
673
+ continue
674
+
675
+ # Entry out of range 0, 1, ..., n-1:
676
+ if e >= n:
677
+ return False
678
+
679
+ # Entry has already appeared in this row:
680
+ if e in vals_in_row:
681
+ return False
682
+
683
+ vals_in_row[e] = True
684
+
685
+ for c in range(n):
686
+ vals_in_col = {}
687
+
688
+ for r in range(n):
689
+ e = self[r, c]
690
+
691
+ if e < 0:
692
+ continue
693
+
694
+ # Entry out of range 0, 1, ..., n-1:
695
+ if e >= n:
696
+ return False
697
+
698
+ # Entry has already appeared in this column:
699
+ if e in vals_in_col:
700
+ return False
701
+
702
+ vals_in_col[e] = True
703
+
704
+ return True
705
+
706
+ def is_latin_square(self):
707
+ """
708
+ ``self`` is a latin square if it is an n by n matrix, and each symbol
709
+ in [0, 1, ..., n-1] appears exactly once in each row, and exactly
710
+ once in each column.
711
+
712
+ EXAMPLES::
713
+
714
+ sage: from sage.combinat.matrices.latin import *
715
+ sage: elementary_abelian_2group(4).is_latin_square()
716
+ True
717
+
718
+ ::
719
+
720
+ sage: forward_circulant(7).is_latin_square()
721
+ True
722
+ """
723
+ # We do not allow latin rectangles:
724
+ if self.nrows() != self.ncols():
725
+ return False
726
+
727
+ # Every cell must be filled:
728
+ if any(x < 0 for x in self.list()):
729
+ return False
730
+
731
+ # By necessity self must be a partial latin square:
732
+ if not self.is_partial_latin_square():
733
+ return False
734
+
735
+ return True
736
+
737
+ def permissable_values(self, r, c):
738
+ """
739
+ Find all values that do not appear in row r and column c of the
740
+ latin square ``self``. If ``self[r, c]`` is filled then we return the
741
+ empty list.
742
+
743
+ INPUT:
744
+
745
+ - ``self`` -- LatinSquare
746
+
747
+ - ``r`` -- integer; row of the latin square
748
+
749
+ - ``c`` -- integer; column of the latin square
750
+
751
+ EXAMPLES::
752
+
753
+ sage: from sage.combinat.matrices.latin import *
754
+ sage: L = back_circulant(5)
755
+ sage: L[0, 0] = -1
756
+ sage: L.permissable_values(0, 0)
757
+ [0]
758
+ """
759
+
760
+ if self[r, c] >= 0:
761
+ return []
762
+
763
+ assert self.nrows() == self.ncols()
764
+
765
+ n = self.nrows()
766
+
767
+ vals = {}
768
+ for e in range(n):
769
+ vals[e] = True
770
+
771
+ for i in range(n):
772
+ if self[i, c] >= 0:
773
+ del vals[self[i, c]]
774
+
775
+ for j in range(n):
776
+ if self[r, j] >= 0:
777
+ try:
778
+ del vals[self[r, j]]
779
+ except KeyError:
780
+ # We may have already removed a symbol
781
+ # in the previous for-loop.
782
+ pass
783
+
784
+ return list(vals)
785
+
786
+ def random_empty_cell(self):
787
+ """
788
+ Find an empty cell of self, uniformly at random.
789
+
790
+ INPUT:
791
+
792
+ - ``self`` -- LatinSquare
793
+
794
+ OUTPUT:
795
+
796
+ - ``[r, c]`` -- cell such that ``self[r, c]`` is empty, or returns
797
+ ``None`` if ``self`` is a (full) latin square
798
+
799
+ EXAMPLES::
800
+
801
+ sage: from sage.combinat.matrices.latin import *
802
+ sage: P = back_circulant(2)
803
+ sage: P[1,1] = -1
804
+ sage: P.random_empty_cell()
805
+ [1, 1]
806
+ """
807
+
808
+ cells = {}
809
+
810
+ for r in range(self.nrows()):
811
+ for c in range(self.ncols()):
812
+ if self[r, c] < 0:
813
+ cells[(r, c)] = True
814
+
815
+ cells = list(cells)
816
+
817
+ if not cells:
818
+ return None
819
+
820
+ rc = cells[ZZ.random_element(len(cells))]
821
+
822
+ return [rc[0], rc[1]]
823
+
824
+ def is_uniquely_completable(self):
825
+ """
826
+ Return ``True`` if the partial latin square ``self`` has exactly one
827
+ completion to a latin square. This is just a wrapper for the
828
+ current best-known algorithm, Dancing Links by Knuth. See
829
+ dancing_links.spyx
830
+
831
+ EXAMPLES::
832
+
833
+ sage: from sage.combinat.matrices.latin import *
834
+ sage: back_circulant(4).gcs().is_uniquely_completable()
835
+ True
836
+
837
+ ::
838
+
839
+ sage: G = elementary_abelian_2group(3).gcs()
840
+ sage: G.is_uniquely_completable()
841
+ True
842
+
843
+ ::
844
+
845
+ sage: G[0, 0] = -1
846
+ sage: G.is_uniquely_completable()
847
+ False
848
+ """
849
+
850
+ return self.dlxcpp_has_unique_completion()
851
+
852
+ def is_completable(self):
853
+ """
854
+ Return ``True`` if the partial latin square can be completed to a
855
+ latin square.
856
+
857
+ EXAMPLES:
858
+
859
+ The following partial latin square has no completion because there
860
+ is nowhere that we can place the symbol 0 in the third row::
861
+
862
+ sage: B = LatinSquare(3)
863
+
864
+ ::
865
+
866
+ sage: B[0, 0] = 0
867
+ sage: B[1, 1] = 0
868
+ sage: B[2, 2] = 1
869
+
870
+ ::
871
+
872
+ sage: B
873
+ [ 0 -1 -1]
874
+ [-1 0 -1]
875
+ [-1 -1 1]
876
+
877
+ ::
878
+
879
+ sage: B.is_completable()
880
+ False
881
+
882
+ ::
883
+
884
+ sage: B[2, 2] = 0
885
+ sage: B.is_completable()
886
+ True
887
+ """
888
+ return bool(dlxcpp_find_completions(self, nr_to_find=1))
889
+
890
+ def gcs(self):
891
+ """
892
+ A greedy critical set of a latin square ``self`` is found by
893
+ successively removing elements in a row-wise (bottom-up) manner,
894
+ checking for unique completion at each step.
895
+
896
+ EXAMPLES::
897
+
898
+ sage: from sage.combinat.matrices.latin import *
899
+ sage: A = elementary_abelian_2group(3)
900
+ sage: G = A.gcs()
901
+ sage: A
902
+ [0 1 2 3 4 5 6 7]
903
+ [1 0 3 2 5 4 7 6]
904
+ [2 3 0 1 6 7 4 5]
905
+ [3 2 1 0 7 6 5 4]
906
+ [4 5 6 7 0 1 2 3]
907
+ [5 4 7 6 1 0 3 2]
908
+ [6 7 4 5 2 3 0 1]
909
+ [7 6 5 4 3 2 1 0]
910
+ sage: G
911
+ [ 0 1 2 3 4 5 6 -1]
912
+ [ 1 0 3 2 5 4 -1 -1]
913
+ [ 2 3 0 1 6 -1 4 -1]
914
+ [ 3 2 1 0 -1 -1 -1 -1]
915
+ [ 4 5 6 -1 0 1 2 -1]
916
+ [ 5 4 -1 -1 1 0 -1 -1]
917
+ [ 6 -1 4 -1 2 -1 0 -1]
918
+ [-1 -1 -1 -1 -1 -1 -1 -1]
919
+ """
920
+
921
+ n = self.nrows()
922
+
923
+ from copy import copy
924
+ G = copy(self)
925
+
926
+ for r in range(n-1, -1, -1):
927
+ for c in range(n-1, -1, -1):
928
+ e = G[r, c]
929
+ G[r, c] = -1
930
+
931
+ if not G.dlxcpp_has_unique_completion():
932
+ G[r, c] = e
933
+
934
+ return G
935
+
936
+ def dlxcpp_has_unique_completion(self):
937
+ """
938
+ Check if the partial latin square ``self`` of order n can be embedded
939
+ in precisely one latin square of order n.
940
+
941
+ EXAMPLES::
942
+
943
+ sage: from sage.combinat.matrices.latin import *
944
+ sage: back_circulant(2).dlxcpp_has_unique_completion()
945
+ True
946
+ sage: P = LatinSquare(2)
947
+ sage: P.dlxcpp_has_unique_completion()
948
+ False
949
+ sage: P[0, 0] = 0
950
+ sage: P.dlxcpp_has_unique_completion()
951
+ True
952
+ """
953
+ return len(dlxcpp_find_completions(self, nr_to_find=2)) == 1
954
+
955
+ def vals_in_row(self, r):
956
+ """
957
+ Return a dictionary with key e if and only if row r of ``self`` has
958
+ the symbol e.
959
+
960
+ EXAMPLES::
961
+
962
+ sage: from sage.combinat.matrices.latin import *
963
+ sage: B = back_circulant(3)
964
+ sage: B[0, 0] = -1
965
+ sage: back_circulant(3).vals_in_row(0)
966
+ {0: True, 1: True, 2: True}
967
+ """
968
+
969
+ n = self.ncols()
970
+ vals_in_row = {}
971
+
972
+ for c in range(n):
973
+ e = self[r, c]
974
+ if e >= 0:
975
+ vals_in_row[e] = True
976
+
977
+ return vals_in_row
978
+
979
+ def vals_in_col(self, c):
980
+ """
981
+ Return a dictionary with key e if and only if column c of ``self`` has
982
+ the symbol e.
983
+
984
+ EXAMPLES::
985
+
986
+ sage: from sage.combinat.matrices.latin import *
987
+ sage: B = back_circulant(3)
988
+ sage: B[0, 0] = -1
989
+ sage: back_circulant(3).vals_in_col(0)
990
+ {0: True, 1: True, 2: True}
991
+ """
992
+ n = self.nrows()
993
+ vals_in_col = {}
994
+
995
+ for r in range(n):
996
+ e = self[r, c]
997
+ if e >= 0:
998
+ vals_in_col[e] = True
999
+
1000
+ return vals_in_col
1001
+
1002
+ def latex(self):
1003
+ r"""
1004
+ Return LaTeX code for the latin square.
1005
+
1006
+ EXAMPLES::
1007
+
1008
+ sage: from sage.combinat.matrices.latin import *
1009
+ sage: print(back_circulant(3).latex())
1010
+ \begin{array}{|c|c|c|}\hline 0 & 1 & 2\\\hline 1 & 2 & 0\\\hline 2 & 0 & 1\\\hline\end{array}
1011
+ """
1012
+
1013
+ a = ""
1014
+ a += r"\begin{array}{" + self.ncols()*"|c" + "|}"
1015
+ for r in range(self.nrows()):
1016
+ a += r"\hline "
1017
+ for c in range(self.ncols()):
1018
+ s = self[r, c]
1019
+ if s < 0:
1020
+ a += "~"
1021
+ else:
1022
+ a += str(s)
1023
+
1024
+ if c < self.ncols()-1:
1025
+ a += " & "
1026
+ else:
1027
+ a += "\\\\"
1028
+ a += r"\hline"
1029
+ a += r"\end{array}"
1030
+ return a
1031
+
1032
+ def disjoint_mate_dlxcpp_rows_and_map(self, allow_subtrade):
1033
+ """
1034
+ Internal function for find_disjoint_mates.
1035
+
1036
+ EXAMPLES::
1037
+
1038
+ sage: from sage.combinat.matrices.latin import *
1039
+ sage: B = back_circulant(4)
1040
+ sage: B.disjoint_mate_dlxcpp_rows_and_map(allow_subtrade = True)
1041
+ ([[0, 16, 32],
1042
+ [1, 17, 32],
1043
+ [2, 18, 32],
1044
+ [3, 19, 32],
1045
+ [4, 16, 33],
1046
+ [5, 17, 33],
1047
+ [6, 18, 33],
1048
+ [7, 19, 33],
1049
+ [8, 16, 34],
1050
+ [9, 17, 34],
1051
+ [10, 18, 34],
1052
+ [11, 19, 34],
1053
+ [12, 16, 35],
1054
+ [13, 17, 35],
1055
+ [14, 18, 35],
1056
+ [15, 19, 35],
1057
+ [0, 20, 36],
1058
+ [1, 21, 36],
1059
+ [2, 22, 36],
1060
+ [3, 23, 36],
1061
+ [4, 20, 37],
1062
+ [5, 21, 37],
1063
+ [6, 22, 37],
1064
+ [7, 23, 37],
1065
+ [8, 20, 38],
1066
+ [9, 21, 38],
1067
+ [10, 22, 38],
1068
+ [11, 23, 38],
1069
+ [12, 20, 39],
1070
+ [13, 21, 39],
1071
+ [14, 22, 39],
1072
+ [15, 23, 39],
1073
+ [0, 24, 40],
1074
+ [1, 25, 40],
1075
+ [2, 26, 40],
1076
+ [3, 27, 40],
1077
+ [4, 24, 41],
1078
+ [5, 25, 41],
1079
+ [6, 26, 41],
1080
+ [7, 27, 41],
1081
+ [8, 24, 42],
1082
+ [9, 25, 42],
1083
+ [10, 26, 42],
1084
+ [11, 27, 42],
1085
+ [12, 24, 43],
1086
+ [13, 25, 43],
1087
+ [14, 26, 43],
1088
+ [15, 27, 43],
1089
+ [0, 28, 44],
1090
+ [1, 29, 44],
1091
+ [2, 30, 44],
1092
+ [3, 31, 44],
1093
+ [4, 28, 45],
1094
+ [5, 29, 45],
1095
+ [6, 30, 45],
1096
+ [7, 31, 45],
1097
+ [8, 28, 46],
1098
+ [9, 29, 46],
1099
+ [10, 30, 46],
1100
+ [11, 31, 46],
1101
+ [12, 28, 47],
1102
+ [13, 29, 47],
1103
+ [14, 30, 47],
1104
+ [15, 31, 47]],
1105
+ {(0, 16, 32): (0, 0, 0),
1106
+ (0, 20, 36): (1, 0, 0),
1107
+ (0, 24, 40): (2, 0, 0),
1108
+ (0, 28, 44): (3, 0, 0),
1109
+ (1, 17, 32): (0, 0, 1),
1110
+ (1, 21, 36): (1, 0, 1),
1111
+ (1, 25, 40): (2, 0, 1),
1112
+ (1, 29, 44): (3, 0, 1),
1113
+ (2, 18, 32): (0, 0, 2),
1114
+ (2, 22, 36): (1, 0, 2),
1115
+ (2, 26, 40): (2, 0, 2),
1116
+ (2, 30, 44): (3, 0, 2),
1117
+ (3, 19, 32): (0, 0, 3),
1118
+ (3, 23, 36): (1, 0, 3),
1119
+ (3, 27, 40): (2, 0, 3),
1120
+ (3, 31, 44): (3, 0, 3),
1121
+ (4, 16, 33): (0, 1, 0),
1122
+ (4, 20, 37): (1, 1, 0),
1123
+ (4, 24, 41): (2, 1, 0),
1124
+ (4, 28, 45): (3, 1, 0),
1125
+ (5, 17, 33): (0, 1, 1),
1126
+ (5, 21, 37): (1, 1, 1),
1127
+ (5, 25, 41): (2, 1, 1),
1128
+ (5, 29, 45): (3, 1, 1),
1129
+ (6, 18, 33): (0, 1, 2),
1130
+ (6, 22, 37): (1, 1, 2),
1131
+ (6, 26, 41): (2, 1, 2),
1132
+ (6, 30, 45): (3, 1, 2),
1133
+ (7, 19, 33): (0, 1, 3),
1134
+ (7, 23, 37): (1, 1, 3),
1135
+ (7, 27, 41): (2, 1, 3),
1136
+ (7, 31, 45): (3, 1, 3),
1137
+ (8, 16, 34): (0, 2, 0),
1138
+ (8, 20, 38): (1, 2, 0),
1139
+ (8, 24, 42): (2, 2, 0),
1140
+ (8, 28, 46): (3, 2, 0),
1141
+ (9, 17, 34): (0, 2, 1),
1142
+ (9, 21, 38): (1, 2, 1),
1143
+ (9, 25, 42): (2, 2, 1),
1144
+ (9, 29, 46): (3, 2, 1),
1145
+ (10, 18, 34): (0, 2, 2),
1146
+ (10, 22, 38): (1, 2, 2),
1147
+ (10, 26, 42): (2, 2, 2),
1148
+ (10, 30, 46): (3, 2, 2),
1149
+ (11, 19, 34): (0, 2, 3),
1150
+ (11, 23, 38): (1, 2, 3),
1151
+ (11, 27, 42): (2, 2, 3),
1152
+ (11, 31, 46): (3, 2, 3),
1153
+ (12, 16, 35): (0, 3, 0),
1154
+ (12, 20, 39): (1, 3, 0),
1155
+ (12, 24, 43): (2, 3, 0),
1156
+ (12, 28, 47): (3, 3, 0),
1157
+ (13, 17, 35): (0, 3, 1),
1158
+ (13, 21, 39): (1, 3, 1),
1159
+ (13, 25, 43): (2, 3, 1),
1160
+ (13, 29, 47): (3, 3, 1),
1161
+ (14, 18, 35): (0, 3, 2),
1162
+ (14, 22, 39): (1, 3, 2),
1163
+ (14, 26, 43): (2, 3, 2),
1164
+ (14, 30, 47): (3, 3, 2),
1165
+ (15, 19, 35): (0, 3, 3),
1166
+ (15, 23, 39): (1, 3, 3),
1167
+ (15, 27, 43): (2, 3, 3),
1168
+ (15, 31, 47): (3, 3, 3)})
1169
+ """
1170
+
1171
+ assert self.nrows() == self.ncols()
1172
+
1173
+ n = self.nrows()
1174
+
1175
+ # We will need 3n^2 columns in total:
1176
+ #
1177
+ # n^2 for the xCy columns
1178
+ # n^2 for the xRy columns
1179
+ # n^2 for the xy columns
1180
+
1181
+ dlx_rows = []
1182
+ cmap = {}
1183
+
1184
+ max_column_nr = -1
1185
+
1186
+ for r in range(n):
1187
+ valsrow = self.vals_in_row(r)
1188
+
1189
+ for c in range(n):
1190
+ valscol = self.vals_in_col(c)
1191
+
1192
+ # If this is an empty cell of self then we do nothing.
1193
+ if self[r, c] < 0:
1194
+ continue
1195
+
1196
+ for e in sorted(set(list(valsrow) + list(valscol))):
1197
+ # These should be constants
1198
+ c_OFFSET = e + c*n
1199
+ r_OFFSET = e + r*n + n*n
1200
+ xy_OFFSET = 2*n*n + r*n + c
1201
+
1202
+ cmap[(c_OFFSET, r_OFFSET, xy_OFFSET)] = (r, c, e)
1203
+
1204
+ # The disjoint mate has to be disjoint.
1205
+ if (not allow_subtrade) and self[r, c] == e:
1206
+ continue
1207
+
1208
+ # The permissible symbols must come from this row/column.
1209
+ if e not in valsrow:
1210
+ continue
1211
+ if e not in valscol:
1212
+ continue
1213
+
1214
+ dlx_rows.append([c_OFFSET, r_OFFSET, xy_OFFSET])
1215
+
1216
+ max_column_nr = max(max_column_nr, c_OFFSET,
1217
+ r_OFFSET, xy_OFFSET)
1218
+
1219
+ # We will have missed some columns. We
1220
+ # have to add 'dummy' rows so that the C++ DLX solver will find
1221
+ # a solution.
1222
+ used_columns = flatten(dlx_rows)
1223
+ for i in range(max_column_nr + 1):
1224
+ if i not in used_columns:
1225
+ dlx_rows.append([i])
1226
+
1227
+ return dlx_rows, cmap
1228
+
1229
+ def find_disjoint_mates(self, nr_to_find=None, allow_subtrade=False):
1230
+ r"""
1231
+ .. warning::
1232
+
1233
+ If allow_subtrade is ``True`` then we may return a partial
1234
+ latin square that is *not* disjoint to ``self``. In that case,
1235
+ use bitrade(P, Q) to get an actual bitrade.
1236
+
1237
+ EXAMPLES::
1238
+
1239
+ sage: from sage.combinat.matrices.latin import *
1240
+ sage: B = back_circulant(4)
1241
+ sage: g = B.find_disjoint_mates(allow_subtrade = True)
1242
+ sage: B1 = next(g)
1243
+ sage: B0, B1 = bitrade(B, B1)
1244
+ sage: assert is_bitrade(B0, B1)
1245
+ sage: print(B0)
1246
+ [-1 1 2 -1]
1247
+ [-1 2 -1 0]
1248
+ [-1 -1 -1 -1]
1249
+ [-1 0 1 2]
1250
+ sage: print(B1)
1251
+ [-1 2 1 -1]
1252
+ [-1 0 -1 2]
1253
+ [-1 -1 -1 -1]
1254
+ [-1 1 2 0]
1255
+ """
1256
+
1257
+ assert self.nrows() == self.ncols()
1258
+
1259
+ dlx_rows, cmap = self.disjoint_mate_dlxcpp_rows_and_map(allow_subtrade)
1260
+
1261
+ nr_found = 0
1262
+
1263
+ for x in DLXCPP(dlx_rows):
1264
+ nr_found += 1
1265
+
1266
+ from copy import deepcopy
1267
+ Q = deepcopy(self)
1268
+
1269
+ for y in x:
1270
+ if len(dlx_rows[y]) == 1:
1271
+ continue # dummy row
1272
+ (r, c, e) = cmap[tuple(dlx_rows[y])]
1273
+ Q[r, c] = e
1274
+
1275
+ yield Q
1276
+
1277
+ if nr_to_find is not None and nr_found >= nr_to_find:
1278
+ return
1279
+
1280
+ def contained_in(self, Q):
1281
+ r"""
1282
+ Return ``True`` if ``self`` is a subset of `Q`.
1283
+
1284
+ EXAMPLES::
1285
+
1286
+ sage: from sage.combinat.matrices.latin import *
1287
+ sage: P = elementary_abelian_2group(2)
1288
+ sage: P[0, 0] = -1
1289
+ sage: P.contained_in(elementary_abelian_2group(2))
1290
+ True
1291
+ sage: back_circulant(4).contained_in(elementary_abelian_2group(2))
1292
+ False
1293
+ """
1294
+ for r in range(self.nrows()):
1295
+ for c in range(self.ncols()):
1296
+ if self[r, c] >= 0 and Q[r, c] < 0:
1297
+ return False
1298
+ if self[r, c] >= 0 and self[r, c] != Q[r, c]:
1299
+ return False
1300
+ return True
1301
+
1302
+
1303
+ def genus(T1, T2):
1304
+ """
1305
+ Return the genus of hypermap embedding associated with the bitrade
1306
+ (T1, T2).
1307
+
1308
+ Informally, we compute the [tau_1, tau_2, tau_3]
1309
+ permutation representation of the bitrade. Each cycle of tau_1,
1310
+ tau_2, and tau_3 gives a rotation scheme for a black, white, and
1311
+ star vertex (respectively). The genus then comes from Euler's
1312
+ formula.
1313
+
1314
+ For more details see Carlo Hamalainen: *Partitioning
1315
+ 3-homogeneous latin bitrades*. To appear in Geometriae Dedicata,
1316
+ available at :arxiv:`0710.0938`
1317
+
1318
+ EXAMPLES::
1319
+
1320
+ sage: from sage.combinat.matrices.latin import *
1321
+ sage: (a, b, c, G) = alternating_group_bitrade_generators(1)
1322
+ sage: (T1, T2) = bitrade_from_group(a, b, c, G)
1323
+ sage: genus(T1, T2)
1324
+ 1
1325
+ sage: (a, b, c, G) = pq_group_bitrade_generators(3, 7)
1326
+ sage: (T1, T2) = bitrade_from_group(a, b, c, G)
1327
+ sage: genus(T1, T2)
1328
+ 3
1329
+ """
1330
+ cells_map, t1, t2, t3 = tau123(T1, T2)
1331
+ return (len(t1.to_cycles()) + len(t2.to_cycles()) + len(t3.to_cycles()) - T1.nr_filled_cells() - 2) // (-2)
1332
+
1333
+
1334
+ def tau123(T1, T2):
1335
+ r"""
1336
+ Compute the tau_i representation for a bitrade (T1, T2).
1337
+
1338
+ See the
1339
+ functions tau1, tau2, and tau3 for the mathematical definitions.
1340
+
1341
+ OUTPUT:
1342
+
1343
+ - (cells_map, t1, t2, t3)
1344
+
1345
+ where cells_map is a map to/from the filled cells of T1, and t1,
1346
+ t2, t3 are the tau1, tau2, tau3 permutations.
1347
+
1348
+ EXAMPLES::
1349
+
1350
+ sage: from sage.combinat.matrices.latin import *
1351
+ sage: (a, b, c, G) = pq_group_bitrade_generators(3, 7)
1352
+ sage: (T1, T2) = bitrade_from_group(a, b, c, G)
1353
+ sage: T1
1354
+ [ 0 1 3 -1 -1 -1 -1]
1355
+ [ 1 2 4 -1 -1 -1 -1]
1356
+ [ 2 3 5 -1 -1 -1 -1]
1357
+ [ 3 4 6 -1 -1 -1 -1]
1358
+ [ 4 5 0 -1 -1 -1 -1]
1359
+ [ 5 6 1 -1 -1 -1 -1]
1360
+ [ 6 0 2 -1 -1 -1 -1]
1361
+ sage: T2
1362
+ [ 1 3 0 -1 -1 -1 -1]
1363
+ [ 2 4 1 -1 -1 -1 -1]
1364
+ [ 3 5 2 -1 -1 -1 -1]
1365
+ [ 4 6 3 -1 -1 -1 -1]
1366
+ [ 5 0 4 -1 -1 -1 -1]
1367
+ [ 6 1 5 -1 -1 -1 -1]
1368
+ [ 0 2 6 -1 -1 -1 -1]
1369
+ sage: (cells_map, t1, t2, t3) = tau123(T1, T2)
1370
+ sage: D = cells_map
1371
+ sage: {i: v for i,v in D.items() if i in ZZ}
1372
+ {1: (0, 0),
1373
+ 2: (0, 1),
1374
+ 3: (0, 2),
1375
+ 4: (1, 0),
1376
+ 5: (1, 1),
1377
+ 6: (1, 2),
1378
+ 7: (2, 0),
1379
+ 8: (2, 1),
1380
+ 9: (2, 2),
1381
+ 10: (3, 0),
1382
+ 11: (3, 1),
1383
+ 12: (3, 2),
1384
+ 13: (4, 0),
1385
+ 14: (4, 1),
1386
+ 15: (4, 2),
1387
+ 16: (5, 0),
1388
+ 17: (5, 1),
1389
+ 18: (5, 2),
1390
+ 19: (6, 0),
1391
+ 20: (6, 1),
1392
+ 21: (6, 2)}
1393
+ sage: {i: v for i,v in D.items() if i not in ZZ}
1394
+ {(0, 0): 1,
1395
+ (0, 1): 2,
1396
+ (0, 2): 3,
1397
+ (1, 0): 4,
1398
+ (1, 1): 5,
1399
+ (1, 2): 6,
1400
+ (2, 0): 7,
1401
+ (2, 1): 8,
1402
+ (2, 2): 9,
1403
+ (3, 0): 10,
1404
+ (3, 1): 11,
1405
+ (3, 2): 12,
1406
+ (4, 0): 13,
1407
+ (4, 1): 14,
1408
+ (4, 2): 15,
1409
+ (5, 0): 16,
1410
+ (5, 1): 17,
1411
+ (5, 2): 18,
1412
+ (6, 0): 19,
1413
+ (6, 1): 20,
1414
+ (6, 2): 21}
1415
+ sage: cells_map_as_square(cells_map, max(T1.nrows(), T1.ncols()))
1416
+ [ 1 2 3 -1 -1 -1 -1]
1417
+ [ 4 5 6 -1 -1 -1 -1]
1418
+ [ 7 8 9 -1 -1 -1 -1]
1419
+ [10 11 12 -1 -1 -1 -1]
1420
+ [13 14 15 -1 -1 -1 -1]
1421
+ [16 17 18 -1 -1 -1 -1]
1422
+ [19 20 21 -1 -1 -1 -1]
1423
+ sage: t1
1424
+ [3, 1, 2, 6, 4, 5, 9, 7, 8, 12, 10, 11, 15, 13, 14, 18, 16, 17, 21, 19, 20]
1425
+ sage: t2
1426
+ [4, 8, 15, 7, 11, 18, 10, 14, 21, 13, 17, 3, 16, 20, 6, 19, 2, 9, 1, 5, 12]
1427
+ sage: t3
1428
+ [20, 18, 10, 2, 21, 13, 5, 3, 16, 8, 6, 19, 11, 9, 1, 14, 12, 4, 17, 15, 7]
1429
+
1430
+ ::
1431
+
1432
+ sage: t1.to_cycles()
1433
+ [(1, 3, 2), (4, 6, 5), (7, 9, 8), (10, 12, 11), (13, 15, 14), (16, 18, 17), (19, 21, 20)]
1434
+ sage: t2.to_cycles()
1435
+ [(1, 4, 7, 10, 13, 16, 19), (2, 8, 14, 20, 5, 11, 17), (3, 15, 6, 18, 9, 21, 12)]
1436
+ sage: t3.to_cycles()
1437
+ [(1, 20, 15), (2, 18, 4), (3, 10, 8), (5, 21, 7), (6, 13, 11), (9, 16, 14), (12, 19, 17)]
1438
+
1439
+ The product t1\*t2\*t3 is the identity, i.e. it fixes every point::
1440
+
1441
+ sage: len((t1*t2*t3).fixed_points()) == T1.nr_filled_cells()
1442
+ True
1443
+ """
1444
+ assert is_bitrade(T1, T2)
1445
+
1446
+ cells_map = T1.filled_cells_map()
1447
+
1448
+ t1 = tau1(T1, T2, cells_map)
1449
+ t2 = tau2(T1, T2, cells_map)
1450
+ t3 = tau3(T1, T2, cells_map)
1451
+
1452
+ return (cells_map, t1, t2, t3)
1453
+
1454
+
1455
+ def isotopism(p):
1456
+ """
1457
+ Return a Permutation object that represents an isotopism (for rows,
1458
+ columns or symbols of a partial latin square).
1459
+
1460
+ Technically, all this function does is take as input a
1461
+ representation of a permutation of `0,...,n-1` and return a
1462
+ :class:`Permutation` object defined on `1,...,n`.
1463
+
1464
+ For a definition of isotopism, see the :wikipedia:`wikipedia section on
1465
+ isotopism <Latin_square#Equivalence_classes_of_Latin_squares>`.
1466
+
1467
+ INPUT:
1468
+
1469
+ According to the type of input (see examples below):
1470
+
1471
+ - an integer `n` -- the function returns the identity on `1,...,n`
1472
+
1473
+ - a string representing a permutation in disjoint cycles notation,
1474
+ e.g. `(0,1,2)(3,4,5)` -- the corresponding permutation is returned,
1475
+ shifted by 1 to act on `1,...,n`
1476
+
1477
+ - list/tuple of tuples -- assumes disjoint cycle notation, see previous
1478
+ entry
1479
+
1480
+ - a list of integers -- the function adds `1` to each member of the
1481
+ list, and returns the corresponding permutation
1482
+
1483
+ - a :class:`PermutationGroupElement` ``p`` -- returns a permutation
1484
+ describing ``p`` **without** any shift
1485
+
1486
+ EXAMPLES::
1487
+
1488
+ sage: from sage.combinat.matrices.latin import *
1489
+ sage: isotopism(5) # identity on 5 points
1490
+ [1, 2, 3, 4, 5]
1491
+
1492
+ ::
1493
+
1494
+ sage: G = PermutationGroup(['(1,2,3)(4,5)'])
1495
+ sage: g = G.gen(0)
1496
+ sage: isotopism(g)
1497
+ [2, 3, 1, 5, 4]
1498
+
1499
+ ::
1500
+
1501
+ sage: isotopism([0,3,2,1]) # 0 goes to 0, 1 goes to 3, etc.
1502
+ [1, 4, 3, 2]
1503
+
1504
+ ::
1505
+
1506
+ sage: isotopism( (0,1,2) ) # single cycle, presented as a tuple
1507
+ [2, 3, 1]
1508
+
1509
+ ::
1510
+
1511
+ sage: x = isotopism( ((0,1,2), (3,4)) ) # tuple of cycles
1512
+ sage: x
1513
+ [2, 3, 1, 5, 4]
1514
+ sage: x.to_cycles()
1515
+ [(1, 2, 3), (4, 5)]
1516
+ """
1517
+
1518
+ # Identity isotopism on p points:
1519
+ if isinstance(p, (Integer, int)):
1520
+ return Permutation(range(1, p + 1))
1521
+
1522
+ if isinstance(p, PermutationGroupElement):
1523
+ # fixme Ask the Sage mailing list about the tuple/list issue!
1524
+ return Permutation(list(p.tuple()))
1525
+
1526
+ if isinstance(p, list):
1527
+ # We expect a list like [0,3,2,1] which means
1528
+ # that 0 goes to 0, 1 goes to 3, etc.
1529
+ return Permutation([x+1 for x in p])
1530
+
1531
+ if isinstance(p, tuple):
1532
+ # We have a single cycle:
1533
+ if isinstance(p[0], Integer):
1534
+ return Permutation(tuple(x+1 for x in p))
1535
+
1536
+ # We have a tuple of cycles:
1537
+ if isinstance(p[0], tuple):
1538
+ x = isotopism(p[0])
1539
+
1540
+ for i in range(1, len(p)):
1541
+ x = x._left_to_right_multiply_on_left(isotopism(p[i]))
1542
+
1543
+ return x
1544
+
1545
+ # Not sure what we got!
1546
+ raise TypeError("unable to convert {!r} to isotopism".format(p))
1547
+
1548
+
1549
+ def cells_map_as_square(cells_map, n):
1550
+ """
1551
+ Return a LatinSquare with cells numbered from 1, 2, ... to given
1552
+ the dictionary cells_map.
1553
+
1554
+ .. NOTE::
1555
+
1556
+ The value n should be the maximum of the number of rows and
1557
+ columns of the original partial latin square
1558
+
1559
+ EXAMPLES::
1560
+
1561
+ sage: from sage.combinat.matrices.latin import *
1562
+ sage: (a, b, c, G) = alternating_group_bitrade_generators(1)
1563
+ sage: (T1, T2) = bitrade_from_group(a, b, c, G)
1564
+ sage: T1
1565
+ [ 0 -1 3 1]
1566
+ [-1 1 0 2]
1567
+ [ 1 3 2 -1]
1568
+ [ 2 0 -1 3]
1569
+
1570
+ There are 12 filled cells in T::
1571
+
1572
+ sage: cells_map_as_square(T1.filled_cells_map(), max(T1.nrows(), T1.ncols()))
1573
+ [ 1 -1 2 3]
1574
+ [-1 4 5 6]
1575
+ [ 7 8 9 -1]
1576
+ [10 11 -1 12]
1577
+ """
1578
+
1579
+ assert n > 1
1580
+
1581
+ L = LatinSquare(n, n)
1582
+
1583
+ for r in range(n):
1584
+ for c in range(n):
1585
+ try:
1586
+ L[r, c] = cells_map[(r, c)]
1587
+ except KeyError:
1588
+ # There is no cell (r,c) so skip it
1589
+ L[r, c] = -1
1590
+
1591
+ return L
1592
+
1593
+
1594
+ def beta1(rce, T1, T2):
1595
+ """
1596
+ Find the unique (x, c, e) in T2 such that (r, c, e) is in T1.
1597
+
1598
+ INPUT:
1599
+
1600
+ - ``rce`` -- tuple (or list) (r, c, e) in T1
1601
+
1602
+ - ``T1``, ``T2`` -- latin bitrade
1603
+
1604
+ OUTPUT: (x, c, e) in T2
1605
+
1606
+ EXAMPLES::
1607
+
1608
+ sage: from sage.combinat.matrices.latin import *
1609
+ sage: T1 = back_circulant(5)
1610
+ sage: x = isotopism( (0,1,2,3,4) )
1611
+ sage: y = isotopism(5) # identity
1612
+ sage: z = isotopism(5) # identity
1613
+ sage: T2 = T1.apply_isotopism(x, y, z)
1614
+ sage: is_bitrade(T1, T2)
1615
+ True
1616
+ sage: beta1([0, 0, 0], T1, T2)
1617
+ (1, 0, 0)
1618
+ """
1619
+ r = rce[0]
1620
+ c = rce[1]
1621
+ e = rce[2]
1622
+
1623
+ assert T1[r, c] == e
1624
+ assert e >= 0
1625
+
1626
+ for x in range(T1.nrows()):
1627
+ if T2[x, c] == e:
1628
+ return (x, c, e)
1629
+
1630
+ raise ValueError
1631
+
1632
+
1633
+ def beta2(rce, T1, T2):
1634
+ """
1635
+ Find the unique (r, x, e) in T2 such that (r, c, e) is in T1.
1636
+
1637
+ INPUT:
1638
+
1639
+ - ``rce`` -- tuple (or list) (r, c, e) in T1
1640
+
1641
+ - ``T1``, ``T2`` -- latin bitrade
1642
+
1643
+ OUTPUT: (r, x, e) in T2
1644
+
1645
+ EXAMPLES::
1646
+
1647
+ sage: from sage.combinat.matrices.latin import *
1648
+ sage: T1 = back_circulant(5)
1649
+ sage: x = isotopism( (0,1,2,3,4) )
1650
+ sage: y = isotopism(5) # identity
1651
+ sage: z = isotopism(5) # identity
1652
+ sage: T2 = T1.apply_isotopism(x, y, z)
1653
+ sage: is_bitrade(T1, T2)
1654
+ True
1655
+ sage: beta2([0, 0, 0], T1, T2)
1656
+ (0, 1, 0)
1657
+ """
1658
+ r = rce[0]
1659
+ c = rce[1]
1660
+ e = rce[2]
1661
+
1662
+ assert T1[r, c] == e
1663
+ assert e >= 0
1664
+
1665
+ for x in range(T1.ncols()):
1666
+ if T2[r, x] == e:
1667
+ return (r, x, e)
1668
+
1669
+ raise ValueError
1670
+
1671
+
1672
+ def beta3(rce, T1, T2):
1673
+ """
1674
+ Find the unique (r, c, x) in T2 such that (r, c, e) is in T1.
1675
+
1676
+ INPUT:
1677
+
1678
+ - ``rce`` -- tuple (or list) (r, c, e) in T1
1679
+
1680
+ - ``T1, T2`` -- latin bitrade
1681
+
1682
+ OUTPUT: (r, c, x) in T2.
1683
+
1684
+ EXAMPLES::
1685
+
1686
+ sage: from sage.combinat.matrices.latin import *
1687
+ sage: T1 = back_circulant(5)
1688
+ sage: x = isotopism( (0,1,2,3,4) )
1689
+ sage: y = isotopism(5) # identity
1690
+ sage: z = isotopism(5) # identity
1691
+ sage: T2 = T1.apply_isotopism(x, y, z)
1692
+ sage: is_bitrade(T1, T2)
1693
+ True
1694
+ sage: beta3([0, 0, 0], T1, T2)
1695
+ (0, 0, 4)
1696
+ """
1697
+ r = rce[0]
1698
+ c = rce[1]
1699
+ e = rce[2]
1700
+
1701
+ assert T1[r, c] == e
1702
+ assert e >= 0
1703
+
1704
+ # fixme this range could be iffy if we
1705
+ # work with latin bitrade rectangles...
1706
+ for x in range(T1.nrows()):
1707
+ if T2[r, c] == x:
1708
+ return (r, c, x)
1709
+
1710
+ raise ValueError
1711
+
1712
+
1713
+ def tau1(T1, T2, cells_map):
1714
+ r"""
1715
+ The definition of `\tau_1` is
1716
+
1717
+ .. MATH::
1718
+
1719
+ \tau_1 : T1 \rightarrow T1 \\
1720
+ \tau_1 = \beta_2^{-1} \beta_3
1721
+
1722
+ where the composition is left to right and `\beta_i : T2 \rightarrow T1`
1723
+ changes just the `i`-th coordinate of a triple.
1724
+
1725
+ EXAMPLES::
1726
+
1727
+ sage: from sage.combinat.matrices.latin import *
1728
+ sage: T1 = back_circulant(5)
1729
+ sage: x = isotopism( (0,1,2,3,4) )
1730
+ sage: y = isotopism(5) # identity
1731
+ sage: z = isotopism(5) # identity
1732
+ sage: T2 = T1.apply_isotopism(x, y, z)
1733
+ sage: is_bitrade(T1, T2)
1734
+ True
1735
+ sage: (cells_map, t1, t2, t3) = tau123(T1, T2)
1736
+ sage: t1 = tau1(T1, T2, cells_map)
1737
+ sage: t1
1738
+ [2, 3, 4, 5, 1, 7, 8, 9, 10, 6, 12, 13, 14, 15, 11, 17, 18, 19, 20, 16, 22, 23, 24, 25, 21]
1739
+ sage: t1.to_cycles()
1740
+ [(1, 2, 3, 4, 5), (6, 7, 8, 9, 10), (11, 12, 13, 14, 15), (16, 17, 18, 19, 20), (21, 22, 23, 24, 25)]
1741
+ """
1742
+
1743
+ # The cells_map has both directions, i.e. integer to
1744
+ # cell and cell to integer, so the size of T1 is
1745
+ # just half of len(cells_map).
1746
+ x = (int(len(cells_map)/2) + 1) * [-1]
1747
+
1748
+ for r in range(T1.nrows()):
1749
+ for c in range(T1.ncols()):
1750
+ e = T1[r, c]
1751
+
1752
+ if e < 0:
1753
+ continue
1754
+
1755
+ (r2, c2, e2) = beta2((r, c, e), T1, T2)
1756
+ (r3, c3, e3) = beta3((r2, c2, e2), T2, T1)
1757
+
1758
+ x[cells_map[(r, c)]] = cells_map[(r3, c3)]
1759
+
1760
+ x.pop(0)
1761
+ # remove the head of the list since we
1762
+ # have permutations on 1..(something).
1763
+
1764
+ return Permutation(x)
1765
+
1766
+
1767
+ def tau2(T1, T2, cells_map):
1768
+ r"""
1769
+ The definition of `\tau_2` is
1770
+
1771
+ .. MATH::
1772
+
1773
+ \tau_2 : T1 \rightarrow T1 \\
1774
+ \tau_2 = \beta_3^{-1} \beta_1
1775
+
1776
+ where the composition is left to right and `\beta_i : T2 \rightarrow T1`
1777
+ changes just the `i`-th coordinate of a triple.
1778
+
1779
+ EXAMPLES::
1780
+
1781
+ sage: from sage.combinat.matrices.latin import *
1782
+ sage: T1 = back_circulant(5)
1783
+ sage: x = isotopism( (0,1,2,3,4) )
1784
+ sage: y = isotopism(5) # identity
1785
+ sage: z = isotopism(5) # identity
1786
+ sage: T2 = T1.apply_isotopism(x, y, z)
1787
+ sage: is_bitrade(T1, T2)
1788
+ True
1789
+ sage: (cells_map, t1, t2, t3) = tau123(T1, T2)
1790
+ sage: t2 = tau2(T1, T2, cells_map)
1791
+ sage: t2
1792
+ [21, 22, 23, 24, 25, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
1793
+ sage: t2.to_cycles()
1794
+ [(1, 21, 16, 11, 6), (2, 22, 17, 12, 7), (3, 23, 18, 13, 8), (4, 24, 19, 14, 9), (5, 25, 20, 15, 10)]
1795
+ """
1796
+
1797
+ # The cells_map has both directions, i.e. integer to
1798
+ # cell and cell to integer, so the size of T1 is
1799
+ # just half of len(cells_map).
1800
+ x = (int(len(cells_map)/2) + 1) * [-1]
1801
+
1802
+ for r in range(T1.nrows()):
1803
+ for c in range(T1.ncols()):
1804
+ e = T1[r, c]
1805
+
1806
+ if e < 0:
1807
+ continue
1808
+
1809
+ (r2, c2, e2) = beta3((r, c, e), T1, T2)
1810
+ (r3, c3, e3) = beta1((r2, c2, e2), T2, T1)
1811
+
1812
+ x[cells_map[(r, c)]] = cells_map[(r3, c3)]
1813
+
1814
+ x.pop(0)
1815
+ # remove the head of the list since we
1816
+ # have permutations on 1..(something).
1817
+
1818
+ return Permutation(x)
1819
+
1820
+
1821
+ def tau3(T1, T2, cells_map):
1822
+ r"""
1823
+ The definition of `\tau_3` is
1824
+
1825
+ .. MATH::
1826
+
1827
+ \tau_3 : T1 \rightarrow T1 \\
1828
+ \tau_3 = \beta_1^{-1} \beta_2
1829
+
1830
+ where the composition is left to right and `\beta_i : T2 \rightarrow T1`
1831
+ changes just the `i`-th coordinate of a triple.
1832
+
1833
+ EXAMPLES::
1834
+
1835
+ sage: from sage.combinat.matrices.latin import *
1836
+ sage: T1 = back_circulant(5)
1837
+ sage: x = isotopism( (0,1,2,3,4) )
1838
+ sage: y = isotopism(5) # identity
1839
+ sage: z = isotopism(5) # identity
1840
+ sage: T2 = T1.apply_isotopism(x, y, z)
1841
+ sage: is_bitrade(T1, T2)
1842
+ True
1843
+ sage: (cells_map, t1, t2, t3) = tau123(T1, T2)
1844
+ sage: t3 = tau3(T1, T2, cells_map)
1845
+ sage: t3
1846
+ [10, 6, 7, 8, 9, 15, 11, 12, 13, 14, 20, 16, 17, 18, 19, 25, 21, 22, 23, 24, 5, 1, 2, 3, 4]
1847
+ sage: t3.to_cycles()
1848
+ [(1, 10, 14, 18, 22), (2, 6, 15, 19, 23), (3, 7, 11, 20, 24), (4, 8, 12, 16, 25), (5, 9, 13, 17, 21)]
1849
+ """
1850
+
1851
+ # The cells_map has both directions, i.e. integer to
1852
+ # cell and cell to integer, so the size of T1 is
1853
+ # just half of len(cells_map).
1854
+ x = (int(len(cells_map)/2) + 1) * [-1]
1855
+
1856
+ for r in range(T1.nrows()):
1857
+ for c in range(T1.ncols()):
1858
+ e = T1[r, c]
1859
+
1860
+ if e < 0:
1861
+ continue
1862
+
1863
+ (r2, c2, e2) = beta1((r, c, e), T1, T2)
1864
+ (r3, c3, e3) = beta2((r2, c2, e2), T2, T1)
1865
+
1866
+ x[cells_map[(r, c)]] = cells_map[(r3, c3)]
1867
+
1868
+ x.pop(0)
1869
+ # remove the head of the list since we
1870
+ # have permutations on 1..(something).
1871
+
1872
+ return Permutation(x)
1873
+
1874
+
1875
+ def back_circulant(n):
1876
+ """
1877
+ The back-circulant latin square of order n is the Cayley table for
1878
+ (Z_n, +), the integers under addition modulo n.
1879
+
1880
+ INPUT:
1881
+
1882
+ - ``n`` -- integer; order of the latin square
1883
+
1884
+ EXAMPLES::
1885
+
1886
+ sage: from sage.combinat.matrices.latin import *
1887
+ sage: back_circulant(5)
1888
+ [0 1 2 3 4]
1889
+ [1 2 3 4 0]
1890
+ [2 3 4 0 1]
1891
+ [3 4 0 1 2]
1892
+ [4 0 1 2 3]
1893
+ """
1894
+ assert n >= 1
1895
+
1896
+ L = LatinSquare(n, n)
1897
+
1898
+ for r in range(n):
1899
+ for c in range(n):
1900
+ L[r, c] = (r + c) % n
1901
+
1902
+ return L
1903
+
1904
+
1905
+ def forward_circulant(n):
1906
+ """
1907
+ The forward-circulant latin square of order n is the Cayley table
1908
+ for the operation r + c = (n-c+r) mod n.
1909
+
1910
+ INPUT:
1911
+
1912
+ - ``n`` -- integer; order of the latin square
1913
+
1914
+ EXAMPLES::
1915
+
1916
+ sage: from sage.combinat.matrices.latin import *
1917
+ sage: forward_circulant(5)
1918
+ [0 4 3 2 1]
1919
+ [1 0 4 3 2]
1920
+ [2 1 0 4 3]
1921
+ [3 2 1 0 4]
1922
+ [4 3 2 1 0]
1923
+ """
1924
+ assert n >= 1
1925
+
1926
+ L = LatinSquare(n, n)
1927
+
1928
+ for r in range(n):
1929
+ for c in range(n):
1930
+ L[r, c] = (n-c+r) % n
1931
+
1932
+ return L
1933
+
1934
+
1935
+ def direct_product(L1, L2, L3, L4):
1936
+ """
1937
+ The 'direct product' of four latin squares L1, L2, L3, L4 of order
1938
+ n is the latin square of order 2n consisting of
1939
+
1940
+ ::
1941
+
1942
+ -----------
1943
+ | L1 | L2 |
1944
+ -----------
1945
+ | L3 | L4 |
1946
+ -----------
1947
+
1948
+ where the subsquares L2 and L3 have entries offset by n.
1949
+
1950
+ EXAMPLES::
1951
+
1952
+ sage: from sage.combinat.matrices.latin import *
1953
+ sage: direct_product(back_circulant(4), back_circulant(4), elementary_abelian_2group(2), elementary_abelian_2group(2))
1954
+ [0 1 2 3 4 5 6 7]
1955
+ [1 2 3 0 5 6 7 4]
1956
+ [2 3 0 1 6 7 4 5]
1957
+ [3 0 1 2 7 4 5 6]
1958
+ [4 5 6 7 0 1 2 3]
1959
+ [5 4 7 6 1 0 3 2]
1960
+ [6 7 4 5 2 3 0 1]
1961
+ [7 6 5 4 3 2 1 0]
1962
+ """
1963
+ assert L1.nrows() == L2.nrows() == L3.nrows() == L4.nrows()
1964
+ assert L1.ncols() == L2.ncols() == L3.ncols() == L4.ncols()
1965
+ assert L1.nrows() == L1.ncols()
1966
+
1967
+ n = L1.nrows()
1968
+
1969
+ D = LatinSquare(2*n, 2*n)
1970
+
1971
+ for r in range(n):
1972
+ for c in range(n):
1973
+ D[r, c] = L1[r, c]
1974
+ D[r, c+n] = L2[r, c] + n
1975
+ D[r+n, c] = L3[r, c] + n
1976
+ D[r+n, c+n] = L4[r, c]
1977
+
1978
+ return D
1979
+
1980
+
1981
+ def elementary_abelian_2group(s):
1982
+ """
1983
+ Return the latin square based on the Cayley table for the
1984
+ elementary abelian 2-group of order 2s.
1985
+
1986
+ INPUT:
1987
+
1988
+ - ``s`` -- integer; order of the latin square will be 2s
1989
+
1990
+ EXAMPLES::
1991
+
1992
+ sage: from sage.combinat.matrices.latin import *
1993
+ sage: elementary_abelian_2group(3)
1994
+ [0 1 2 3 4 5 6 7]
1995
+ [1 0 3 2 5 4 7 6]
1996
+ [2 3 0 1 6 7 4 5]
1997
+ [3 2 1 0 7 6 5 4]
1998
+ [4 5 6 7 0 1 2 3]
1999
+ [5 4 7 6 1 0 3 2]
2000
+ [6 7 4 5 2 3 0 1]
2001
+ [7 6 5 4 3 2 1 0]
2002
+ """
2003
+ assert s > 0
2004
+
2005
+ if s == 1:
2006
+ L = LatinSquare(2, 2)
2007
+ L[0, 0] = 0
2008
+ L[0, 1] = 1
2009
+ L[1, 0] = 1
2010
+ L[1, 1] = 0
2011
+
2012
+ return L
2013
+ else:
2014
+ L_prev = elementary_abelian_2group(s-1)
2015
+ L = LatinSquare(2**s, 2**s)
2016
+
2017
+ offset = L.nrows() // 2
2018
+
2019
+ for r in range(L_prev.nrows()):
2020
+ for c in range(L_prev.ncols()):
2021
+ L[r, c] = L_prev[r, c]
2022
+ L[r+offset, c] = L_prev[r, c] + offset
2023
+ L[r, c+offset] = L_prev[r, c] + offset
2024
+ L[r+offset, c+offset] = L_prev[r, c]
2025
+ return L
2026
+
2027
+
2028
+ def coin():
2029
+ """
2030
+ Simulate a fair coin (returns ``True`` or ``False``) using
2031
+ ZZ.random_element(2).
2032
+
2033
+ EXAMPLES::
2034
+
2035
+ sage: from sage.combinat.matrices.latin import coin
2036
+ sage: x = coin()
2037
+ sage: x == 0 or x == 1
2038
+ True
2039
+ """
2040
+ return ZZ.random_element(2) == 0
2041
+
2042
+
2043
+ def next_conjugate(L):
2044
+ """
2045
+ Permute L[r, c] = e to the conjugate L[c, e] = r.
2046
+
2047
+ We assume that L is an n by n matrix and has values in the range 0,
2048
+ 1, ..., n-1.
2049
+
2050
+ EXAMPLES::
2051
+
2052
+ sage: from sage.combinat.matrices.latin import *
2053
+ sage: L = back_circulant(6)
2054
+ sage: L
2055
+ [0 1 2 3 4 5]
2056
+ [1 2 3 4 5 0]
2057
+ [2 3 4 5 0 1]
2058
+ [3 4 5 0 1 2]
2059
+ [4 5 0 1 2 3]
2060
+ [5 0 1 2 3 4]
2061
+ sage: next_conjugate(L)
2062
+ [0 1 2 3 4 5]
2063
+ [5 0 1 2 3 4]
2064
+ [4 5 0 1 2 3]
2065
+ [3 4 5 0 1 2]
2066
+ [2 3 4 5 0 1]
2067
+ [1 2 3 4 5 0]
2068
+ sage: L == next_conjugate(next_conjugate(next_conjugate(L)))
2069
+ True
2070
+ """
2071
+ assert L.nrows() == L.ncols()
2072
+
2073
+ n = L.nrows()
2074
+
2075
+ C = LatinSquare(n, n)
2076
+
2077
+ for r in range(n):
2078
+ for c in range(n):
2079
+ e = L[r, c]
2080
+ assert e >= 0 and e < n
2081
+
2082
+ C[c, e] = r
2083
+
2084
+ return C
2085
+
2086
+
2087
+ def row_containing_sym(L, c, x):
2088
+ """
2089
+ Given an improper latin square L with L[r1, c] = L[r2, c] = x,
2090
+ return r1 or r2 with equal probability. This is an internal
2091
+ function and should only be used in LatinSquare_generator().
2092
+
2093
+ EXAMPLES::
2094
+
2095
+ sage: from sage.combinat.matrices.latin import *
2096
+ sage: L = matrix([(0, 1, 0, 3), (3, 0, 2, 1), (1, 0, 3, 2), (2, 3, 1, 0)])
2097
+ sage: L
2098
+ [0 1 0 3]
2099
+ [3 0 2 1]
2100
+ [1 0 3 2]
2101
+ [2 3 1 0]
2102
+ sage: c = row_containing_sym(L, 1, 0)
2103
+ sage: c == 1 or c == 2
2104
+ True
2105
+ """
2106
+ r1 = -1
2107
+ r2 = -1
2108
+
2109
+ for r in range(L.nrows()):
2110
+ if r1 >= 0 and r2 >= 0:
2111
+ break
2112
+
2113
+ if L[r, c] == x and r1 < 0:
2114
+ r1 = r
2115
+ continue
2116
+
2117
+ if L[r, c] == x and r2 < 0:
2118
+ r2 = r
2119
+ break
2120
+
2121
+ assert r1 >= 0 and r2 >= 0
2122
+
2123
+ return r1 if coin() else r2
2124
+
2125
+
2126
+ def column_containing_sym(L, r, x):
2127
+ """
2128
+ Given an improper latin square L with L[r, c1] = L[r, c2] = x,
2129
+ return c1 or c2 with equal probability. This is an internal
2130
+ function and should only be used in LatinSquare_generator().
2131
+
2132
+ EXAMPLES::
2133
+
2134
+ sage: from sage.combinat.matrices.latin import *
2135
+ sage: L = matrix([(1, 0, 2, 3), (0, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)])
2136
+ sage: L
2137
+ [1 0 2 3]
2138
+ [0 2 3 0]
2139
+ [2 3 0 1]
2140
+ [3 0 1 2]
2141
+ sage: c = column_containing_sym(L, 1, 0)
2142
+ sage: c == 0 or c == 3
2143
+ True
2144
+ """
2145
+
2146
+ c1 = -1
2147
+ c2 = -1
2148
+
2149
+ for c in range(L.ncols()):
2150
+ if c1 >= 0 and c2 >= 0:
2151
+ break
2152
+
2153
+ if L[r, c] == x and c1 < 0:
2154
+ c1 = c
2155
+ continue
2156
+
2157
+ if L[r, c] == x and c2 < 0:
2158
+ c2 = c
2159
+ break
2160
+
2161
+ assert c1 >= 0 and c2 >= 0
2162
+
2163
+ return c1 if coin() else c2
2164
+
2165
+
2166
+ def LatinSquare_generator(L_start, check_assertions=False):
2167
+ """
2168
+ Generator for a sequence of uniformly distributed latin squares,
2169
+ given L_start as the initial latin square.
2170
+
2171
+ This code implements
2172
+ the Markov chain algorithm of Jacobson and Matthews (1996), see
2173
+ below for the BibTex entry. This generator will never throw the
2174
+ ``StopIteration`` exception, so it provides an infinite sequence of
2175
+ latin squares.
2176
+
2177
+ EXAMPLES:
2178
+
2179
+ Use the back circulant latin square of order 4 as the initial
2180
+ square and print the next two latin squares given by the Markov
2181
+ chain::
2182
+
2183
+ sage: from sage.combinat.matrices.latin import *
2184
+ sage: g = LatinSquare_generator(back_circulant(4))
2185
+ sage: next(g).is_latin_square()
2186
+ True
2187
+
2188
+ REFERENCES:
2189
+
2190
+ .. [JacMat96] Mark T. Jacobson and Peter Matthews, "Generating uniformly
2191
+ distributed random Latin squares", Journal of Combinatorial Designs,
2192
+ 4 (1996)
2193
+ """
2194
+ if check_assertions:
2195
+ assert L_start.is_latin_square()
2196
+
2197
+ n = L_start.nrows()
2198
+
2199
+ r1 = r2 = c1 = c2 = x = y = z = -1
2200
+ proper = True
2201
+
2202
+ from copy import copy
2203
+ L = copy(L_start)
2204
+
2205
+ L_cer = LatinSquare(n, n)
2206
+ L_erc = LatinSquare(n, n)
2207
+
2208
+ while True:
2209
+ if proper:
2210
+ if check_assertions:
2211
+ assert L.is_latin_square()
2212
+
2213
+ #################################
2214
+ # Update the other two conjugates
2215
+ for r in range(n):
2216
+ for c in range(n):
2217
+ e = L[r, c]
2218
+
2219
+ L_cer[c, e] = r
2220
+ L_erc[e, r] = c
2221
+ #################################
2222
+
2223
+ yield L
2224
+
2225
+ r1 = ZZ.random_element(n)
2226
+ c1 = ZZ.random_element(n)
2227
+ x = L[r1, c1]
2228
+
2229
+ y = x
2230
+ while y == x:
2231
+ y = ZZ.random_element(n)
2232
+
2233
+ # Now find y in row r1 and column c1.
2234
+ if check_assertions:
2235
+ r2 = 0
2236
+ c2 = 0
2237
+ while L[r1, c2] != y:
2238
+ c2 += 1
2239
+ while L[r2, c1] != y:
2240
+ r2 += 1
2241
+
2242
+ assert L_erc[y, r1] == c2
2243
+ assert L_cer[c1, y] == r2
2244
+
2245
+ c2 = L_erc[y, r1]
2246
+ r2 = L_cer[c1, y]
2247
+
2248
+ if check_assertions:
2249
+ assert L[r1, c2] == y
2250
+ if check_assertions:
2251
+ assert L[r2, c1] == y
2252
+
2253
+ L[r1, c1] = y
2254
+ L[r1, c2] = x
2255
+ L[r2, c1] = x
2256
+
2257
+ # Now deal with the unknown point.
2258
+ # We want to form z + (y - x)
2259
+ z = L[r2, c2]
2260
+
2261
+ if z == x:
2262
+ L[r2, c2] = y
2263
+ else:
2264
+ # z and y have positive coefficients
2265
+ # x is the improper term with a negative coefficient
2266
+ proper = False
2267
+ else: # improper square,
2268
+ # L[r2, c2] = y + z - x
2269
+ # y and z are proper while x is the
2270
+ # improper symbol in the cell L[r2, c2].
2271
+
2272
+ r1 = row_containing_sym(L, c2, x)
2273
+ c1 = column_containing_sym(L, r2, x)
2274
+
2275
+ if check_assertions:
2276
+ assert L[r1, c2] == x
2277
+ if check_assertions:
2278
+ assert L[r2, c1] == x
2279
+
2280
+ # choose one of the proper symbols
2281
+ # uniformly at random (we will use whatever
2282
+ # lands in variable y).
2283
+ if coin():
2284
+ y, z = z, y
2285
+
2286
+ # Add/subtract the symbolic difference (y - x)
2287
+ L[r2, c2] = z
2288
+ L[r1, c2] = y
2289
+ L[r2, c1] = y
2290
+
2291
+ if L[r1, c1] == y:
2292
+ L[r1, c1] = x
2293
+ proper = True
2294
+ else: # got another improper square
2295
+ z = L[r1, c1]
2296
+ x, y = y, x
2297
+ r2 = r1
2298
+ c2 = c1
2299
+
2300
+ # Now we have L[r2, c2] = z+y-x as
2301
+ # usual
2302
+ proper = False # for emphasis
2303
+
2304
+
2305
+ def group_to_LatinSquare(G):
2306
+ """
2307
+ Construct a latin square on the symbols [0, 1, ..., n-1] for a
2308
+ group with an n by n Cayley table.
2309
+
2310
+ EXAMPLES::
2311
+
2312
+ sage: from sage.combinat.matrices.latin import group_to_LatinSquare
2313
+
2314
+ sage: group_to_LatinSquare(DihedralGroup(2))
2315
+ [0 1 2 3]
2316
+ [1 0 3 2]
2317
+ [2 3 0 1]
2318
+ [3 2 1 0]
2319
+
2320
+ ::
2321
+
2322
+ sage: G = libgap.Group(PermutationGroupElement((1,2,3)))
2323
+ sage: group_to_LatinSquare(G)
2324
+ [0 1 2]
2325
+ [1 2 0]
2326
+ [2 0 1]
2327
+ """
2328
+ if isinstance(G, GapElement):
2329
+ rows = (list(x) for x in list(libgap.MultiplicationTable(G)))
2330
+ new_rows = []
2331
+
2332
+ for x in rows:
2333
+ new_rows.append([int(xx) - 1 for xx in x])
2334
+
2335
+ return matrix(new_rows)
2336
+
2337
+ # Otherwise we must have some kind of Sage permutation group object,
2338
+ # such as sage.groups.perm_gps.permgroup.PermutationGroup_generic
2339
+ # or maybe sage.groups.perm_gps.permgroup_named.
2340
+
2341
+ T = G.cayley_table()
2342
+ return matrix(ZZ, T.table())
2343
+
2344
+
2345
+ def alternating_group_bitrade_generators(m):
2346
+ r"""
2347
+ Construct generators a, b, c for the alternating group on 3m+1
2348
+ points, such that a\*b\*c = 1.
2349
+
2350
+ EXAMPLES::
2351
+
2352
+ sage: from sage.combinat.matrices.latin import *
2353
+ sage: a, b, c, G = alternating_group_bitrade_generators(1)
2354
+ sage: (a, b, c, G)
2355
+ ((1,2,3), (1,4,2), (2,4,3), Permutation Group with generators [(1,2,3), (1,4,2)])
2356
+ sage: a*b*c
2357
+ ()
2358
+
2359
+ ::
2360
+
2361
+ sage: (T1, T2) = bitrade_from_group(a, b, c, G)
2362
+ sage: T1
2363
+ [ 0 -1 3 1]
2364
+ [-1 1 0 2]
2365
+ [ 1 3 2 -1]
2366
+ [ 2 0 -1 3]
2367
+ sage: T2
2368
+ [ 1 -1 0 3]
2369
+ [-1 0 2 1]
2370
+ [ 2 1 3 -1]
2371
+ [ 0 3 -1 2]
2372
+ """
2373
+ assert m >= 1
2374
+
2375
+ a = tuple(range(1, 2*m+1 + 1))
2376
+
2377
+ b = tuple(range(m + 1, 0, -1)) + tuple(range(2*m+2, 3*m+1 + 1))
2378
+
2379
+ a = PermutationConstructor(a)
2380
+ b = PermutationConstructor(b)
2381
+ c = PermutationConstructor((a*b)**(-1))
2382
+
2383
+ G = PermutationGroup([a, b])
2384
+
2385
+ return (a, b, c, G)
2386
+
2387
+
2388
+ def pq_group_bitrade_generators(p, q):
2389
+ """
2390
+ Generators for a group of order pq where p and q are primes such
2391
+ that (q % p) == 1.
2392
+
2393
+ EXAMPLES::
2394
+
2395
+ sage: from sage.combinat.matrices.latin import *
2396
+ sage: pq_group_bitrade_generators(3,7)
2397
+ ((2,3,5)(4,7,6), (1,2,3,4,5,6,7), (1,4,2)(3,5,6), Permutation Group with generators [(2,3,5)(4,7,6), (1,2,3,4,5,6,7)])
2398
+ """
2399
+ assert is_prime(p)
2400
+ assert is_prime(q)
2401
+ assert (q % p) == 1
2402
+
2403
+ # beta is a primitive root of the
2404
+ # congruence x^p = 1 mod q
2405
+ F = FiniteField(q)
2406
+ fgen = F.multiplicative_generator()
2407
+ beta = fgen**((q-1)/p)
2408
+
2409
+ assert beta != 1
2410
+ assert (beta**p % q) == 1
2411
+
2412
+ Q = tuple(range(1, q+1))
2413
+
2414
+ P = []
2415
+ seenValues = {}
2416
+ for i in range(2, q):
2417
+ if i in seenValues:
2418
+ continue
2419
+
2420
+ cycle = []
2421
+ for k in range(p):
2422
+ x = (1 + (i-1)*beta**k) % q
2423
+ if x == 0:
2424
+ x = q
2425
+
2426
+ seenValues[x] = True
2427
+ cycle.append(x)
2428
+ P.append(tuple(map(Integer, cycle)))
2429
+
2430
+ G = PermutationGroup([P, Q])
2431
+ assert G.order() == p*q
2432
+ assert not G.is_abelian()
2433
+
2434
+ a = PermutationConstructor(P)
2435
+ b = PermutationConstructor(Q)
2436
+ c = PermutationConstructor((a*b)**(-1))
2437
+
2438
+ return (a, b, c, PermutationGroup([P, Q]))
2439
+
2440
+
2441
+ def p3_group_bitrade_generators(p):
2442
+ """
2443
+ Generators for a group of order p3 where p is a prime.
2444
+
2445
+ EXAMPLES::
2446
+
2447
+ sage: from sage.combinat.matrices.latin import *
2448
+ sage: p3_group_bitrade_generators(3) # random output
2449
+ ((2,6,7)(3,8,9),
2450
+ (1,2,3)(4,7,8)(5,6,9),
2451
+ (1,9,2)(3,7,4)(5,8,6),
2452
+ Permutation Group with generators [(2,6,7)(3,8,9), (1,2,3)(4,7,8)(5,6,9)])
2453
+ """
2454
+ assert is_prime(p)
2455
+
2456
+ F = libgap.FreeGroup(3)
2457
+ a, b, c = F.GeneratorsOfGroup()
2458
+
2459
+ rels = []
2460
+ rels.append(a**p)
2461
+ rels.append(b**p)
2462
+ rels.append(c**p)
2463
+ rels.append(a*b*((b*a*c)**(-1)))
2464
+ rels.append(c*a*((a*c)**(-1)))
2465
+ rels.append(c*b*((b*c)**(-1)))
2466
+
2467
+ G = F.FactorGroupFpGroupByRels(rels)
2468
+ u, v, _ = G.GeneratorsOfGroup()
2469
+
2470
+ iso = libgap.IsomorphismPermGroup(G)
2471
+
2472
+ x = PermutationConstructor(libgap.Image(iso, u))
2473
+ y = PermutationConstructor(libgap.Image(iso, v))
2474
+
2475
+ return (x, y, (x*y)**(-1), PermutationGroup([x, y]))
2476
+
2477
+
2478
+ def check_bitrade_generators(a, b, c):
2479
+ r"""
2480
+ Three group elements a, b, c will generate a bitrade if a\*b\*c = 1
2481
+ and the subgroups a, b, c intersect (pairwise) in just the
2482
+ identity.
2483
+
2484
+ EXAMPLES::
2485
+
2486
+ sage: from sage.combinat.matrices.latin import *
2487
+ sage: a, b, c, G = p3_group_bitrade_generators(3)
2488
+ sage: check_bitrade_generators(a, b, c)
2489
+ True
2490
+ sage: check_bitrade_generators(a, b, libgap(gap('()')))
2491
+ False
2492
+ """
2493
+ A = PermutationGroup([a])
2494
+ B = PermutationGroup([b])
2495
+ C = PermutationGroup([c])
2496
+
2497
+ if a*b != c**(-1):
2498
+ return False
2499
+
2500
+ X = libgap.Intersection(libgap.Intersection(A, B), C)
2501
+ return X.Size() == 1
2502
+
2503
+
2504
+ def is_bitrade(T1, T2):
2505
+ """
2506
+ Combinatorially, a pair (T1, T2) of partial latin squares is a
2507
+ bitrade if they are disjoint, have the same shape, and have row and
2508
+ column balance. For definitions of each of these terms see the
2509
+ relevant function in this file.
2510
+
2511
+ EXAMPLES::
2512
+
2513
+ sage: from sage.combinat.matrices.latin import *
2514
+ sage: T1 = back_circulant(5)
2515
+ sage: x = isotopism( (0,1,2,3,4) )
2516
+ sage: y = isotopism(5) # identity
2517
+ sage: z = isotopism(5) # identity
2518
+ sage: T2 = T1.apply_isotopism(x, y, z)
2519
+ sage: is_bitrade(T1, T2)
2520
+ True
2521
+ """
2522
+ return (is_disjoint(T1, T2) and is_same_shape(T1, T2) and
2523
+ is_row_and_col_balanced(T1, T2))
2524
+
2525
+
2526
+ def is_primary_bitrade(a, b, c, G):
2527
+ """
2528
+ A bitrade generated from elements a, b, c is primary if a, b, c =
2529
+ G.
2530
+
2531
+ EXAMPLES::
2532
+
2533
+ sage: from sage.combinat.matrices.latin import *
2534
+ sage: (a, b, c, G) = p3_group_bitrade_generators(5)
2535
+ sage: is_primary_bitrade(a, b, c, G)
2536
+ True
2537
+ """
2538
+ return G == PermutationGroup([a, b, c])
2539
+
2540
+
2541
+ def tau_to_bitrade(t1, t2, t3):
2542
+ """
2543
+ Given permutations t1, t2, t3 that represent a latin bitrade,
2544
+ convert them to an explicit latin bitrade (T1, T2). The result is
2545
+ unique up to isotopism.
2546
+
2547
+ EXAMPLES::
2548
+
2549
+ sage: from sage.combinat.matrices.latin import *
2550
+ sage: T1 = back_circulant(5)
2551
+ sage: x = isotopism( (0,1,2,3,4) )
2552
+ sage: y = isotopism(5) # identity
2553
+ sage: z = isotopism(5) # identity
2554
+ sage: T2 = T1.apply_isotopism(x, y, z)
2555
+ sage: _, t1, t2, t3 = tau123(T1, T2)
2556
+ sage: U1, U2 = tau_to_bitrade(t1, t2, t3)
2557
+ sage: assert is_bitrade(U1, U2)
2558
+ sage: U1
2559
+ [0 1 2 3 4]
2560
+ [1 2 3 4 0]
2561
+ [2 3 4 0 1]
2562
+ [3 4 0 1 2]
2563
+ [4 0 1 2 3]
2564
+ sage: U2
2565
+ [4 0 1 2 3]
2566
+ [0 1 2 3 4]
2567
+ [1 2 3 4 0]
2568
+ [2 3 4 0 1]
2569
+ [3 4 0 1 2]
2570
+ """
2571
+ c1 = t1.to_cycles()
2572
+ c2 = t2.to_cycles()
2573
+ c3 = t3.to_cycles()
2574
+
2575
+ pt_to_cycle1 = {}
2576
+ pt_to_cycle2 = {}
2577
+ pt_to_cycle3 = {}
2578
+
2579
+ for i in range(len(c1)):
2580
+ for j in range(len(c1[i])):
2581
+ pt_to_cycle1[c1[i][j]] = i
2582
+
2583
+ for i in range(len(c2)):
2584
+ for j in range(len(c2[i])):
2585
+ pt_to_cycle2[c2[i][j]] = i
2586
+
2587
+ for i in range(len(c3)):
2588
+ for j in range(len(c3[i])):
2589
+ pt_to_cycle3[c3[i][j]] = i
2590
+
2591
+ n = max(len(c1), len(c2), len(c3))
2592
+
2593
+ T1 = LatinSquare(n)
2594
+ T2 = LatinSquare(n)
2595
+
2596
+ for r in range(len(c1)):
2597
+ for c in range(len(c2)):
2598
+ for s in range(len(c3)):
2599
+ nr_common = len(reduce(set.intersection,
2600
+ [set(c1[r]), set(c2[c]), set(c3[s])]))
2601
+ assert nr_common in [0, 1]
2602
+
2603
+ if nr_common == 1:
2604
+ T1[r, c] = s
2605
+
2606
+ for cycle in c1:
2607
+ for pt1 in cycle:
2608
+ pt2 = t1[pt1 - 1]
2609
+ pt3 = t2[pt2 - 1]
2610
+ assert t3[pt3 - 1] == pt1
2611
+
2612
+ r = pt_to_cycle1[pt1]
2613
+ c = pt_to_cycle2[pt2]
2614
+ s = pt_to_cycle3[pt3]
2615
+
2616
+ T2[r, c] = s
2617
+
2618
+ return T1, T2
2619
+
2620
+
2621
+ def bitrade_from_group(a, b, c, G):
2622
+ """
2623
+ Given group elements a, b, c in G such that abc = 1 and the
2624
+ subgroups a, b, c intersect (pairwise) only in the identity,
2625
+ construct a bitrade (T1, T2) where rows, columns, and symbols
2626
+ correspond to cosets of a, b, and c, respectively.
2627
+
2628
+ EXAMPLES::
2629
+
2630
+ sage: from sage.combinat.matrices.latin import *
2631
+ sage: a, b, c, G = alternating_group_bitrade_generators(1)
2632
+ sage: (T1, T2) = bitrade_from_group(a, b, c, G)
2633
+ sage: T1
2634
+ [ 0 -1 3 1]
2635
+ [-1 1 0 2]
2636
+ [ 1 3 2 -1]
2637
+ [ 2 0 -1 3]
2638
+ sage: T2
2639
+ [ 1 -1 0 3]
2640
+ [-1 0 2 1]
2641
+ [ 2 1 3 -1]
2642
+ [ 0 3 -1 2]
2643
+ """
2644
+ hom = libgap.ActionHomomorphism(G, libgap.RightCosets(G, libgap.TrivialSubgroup(G)), libgap.OnRight)
2645
+
2646
+ t1 = libgap.Image(hom, a)
2647
+ t2 = libgap.Image(hom, b)
2648
+ t3 = libgap.Image(hom, c)
2649
+
2650
+ t1 = Permutation(str(t1).replace('\n', ''))
2651
+ t2 = Permutation(str(t2).replace('\n', ''))
2652
+ t3 = Permutation(str(t3).replace('\n', ''))
2653
+
2654
+ return tau_to_bitrade(t1, t2, t3)
2655
+
2656
+
2657
+ def is_disjoint(T1, T2):
2658
+ """
2659
+ The partial latin squares T1 and T2 are disjoint if T1[r, c] !=
2660
+ T2[r, c] or T1[r, c] == T2[r, c] == -1 for each cell [r, c].
2661
+
2662
+ EXAMPLES::
2663
+
2664
+ sage: from sage.combinat.matrices.latin import is_disjoint, back_circulant, isotopism
2665
+ sage: is_disjoint(back_circulant(2), back_circulant(2))
2666
+ False
2667
+
2668
+ ::
2669
+
2670
+ sage: T1 = back_circulant(5)
2671
+ sage: x = isotopism( (0,1,2,3,4) )
2672
+ sage: y = isotopism(5) # identity
2673
+ sage: z = isotopism(5) # identity
2674
+ sage: T2 = T1.apply_isotopism(x, y, z)
2675
+ sage: is_disjoint(T1, T2)
2676
+ True
2677
+ """
2678
+ for i in range(T1.nrows()):
2679
+ for j in range(T1.ncols()):
2680
+ if T1[i, j] < 0 and T2[i, j] < 0:
2681
+ continue
2682
+ if T1[i, j] == T2[i, j]:
2683
+ return False
2684
+
2685
+ return True
2686
+
2687
+
2688
+ def is_same_shape(T1, T2):
2689
+ """
2690
+ Two partial latin squares T1, T2 have the same shape if T1[r, c] =
2691
+ 0 if and only if T2[r, c] = 0.
2692
+
2693
+ EXAMPLES::
2694
+
2695
+ sage: from sage.combinat.matrices.latin import *
2696
+ sage: is_same_shape(elementary_abelian_2group(2), back_circulant(4))
2697
+ True
2698
+ sage: is_same_shape(LatinSquare(5), LatinSquare(5))
2699
+ True
2700
+ sage: is_same_shape(forward_circulant(5), LatinSquare(5))
2701
+ False
2702
+ """
2703
+ for i in range(T1.nrows()):
2704
+ for j in range(T1.ncols()):
2705
+ if T1[i, j] < 0 and T2[i, j] < 0:
2706
+ continue
2707
+ if T1[i, j] >= 0 and T2[i, j] >= 0:
2708
+ continue
2709
+
2710
+ return False
2711
+
2712
+ return True
2713
+
2714
+
2715
+ def is_row_and_col_balanced(T1, T2):
2716
+ """
2717
+ Partial latin squares T1 and T2 are balanced if the symbols
2718
+ appearing in row r of T1 are the same as the symbols appearing in
2719
+ row r of T2, for each r, and if the same condition holds on
2720
+ columns.
2721
+
2722
+ EXAMPLES::
2723
+
2724
+ sage: from sage.combinat.matrices.latin import *
2725
+ sage: T1 = matrix([[0,1,-1,-1], [-1,-1,-1,-1], [-1,-1,-1,-1], [-1,-1,-1,-1]])
2726
+ sage: T2 = matrix([[0,1,-1,-1], [-1,-1,-1,-1], [-1,-1,-1,-1], [-1,-1,-1,-1]])
2727
+ sage: is_row_and_col_balanced(T1, T2)
2728
+ True
2729
+ sage: T2 = matrix([[0,3,-1,-1], [-1,-1,-1,-1], [-1,-1,-1,-1], [-1,-1,-1,-1]])
2730
+ sage: is_row_and_col_balanced(T1, T2)
2731
+ False
2732
+ """
2733
+ for r in range(T1.nrows()):
2734
+ val1 = set(x for x in T1.row(r) if x >= 0)
2735
+ val2 = set(x for x in T2.row(r) if x >= 0)
2736
+ if val1 != val2:
2737
+ return False
2738
+
2739
+ for c in range(T1.ncols()):
2740
+ val1 = set(x for x in T1.column(c) if x >= 0)
2741
+ val2 = set(x for x in T2.column(c) if x >= 0)
2742
+ if val1 != val2:
2743
+ return False
2744
+
2745
+ return True
2746
+
2747
+
2748
+ def dlxcpp_rows_and_map(P):
2749
+ """
2750
+ Internal function for ``dlxcpp_find_completions``. Given a partial
2751
+ latin square P we construct a list of rows of a 0-1 matrix M such
2752
+ that an exact cover of M corresponds to a completion of P to a
2753
+ latin square.
2754
+
2755
+ EXAMPLES::
2756
+
2757
+ sage: from sage.combinat.matrices.latin import *
2758
+ sage: dlxcpp_rows_and_map(LatinSquare(2))
2759
+ ([[0, 4, 8],
2760
+ [1, 5, 8],
2761
+ [2, 4, 9],
2762
+ [3, 5, 9],
2763
+ [0, 6, 10],
2764
+ [1, 7, 10],
2765
+ [2, 6, 11],
2766
+ [3, 7, 11]],
2767
+ {(0, 4, 8): (0, 0, 0),
2768
+ (0, 6, 10): (1, 0, 0),
2769
+ (1, 5, 8): (0, 0, 1),
2770
+ (1, 7, 10): (1, 0, 1),
2771
+ (2, 4, 9): (0, 1, 0),
2772
+ (2, 6, 11): (1, 1, 0),
2773
+ (3, 5, 9): (0, 1, 1),
2774
+ (3, 7, 11): (1, 1, 1)})
2775
+ """
2776
+ assert P.nrows() == P.ncols()
2777
+
2778
+ n = P.nrows()
2779
+
2780
+ # We will need 3n^2 columns in total:
2781
+ #
2782
+ # n^2 for the xCy columns
2783
+ # n^2 for the xRy columns
2784
+ # n^2 for the xy columns
2785
+
2786
+ dlx_rows = []
2787
+ cmap = {}
2788
+
2789
+ for r in range(n):
2790
+ valsrow = P.vals_in_row(r)
2791
+
2792
+ for c in range(n):
2793
+ valscol = P.vals_in_col(c)
2794
+
2795
+ for e in range(n):
2796
+ # These should be constants
2797
+ c_OFFSET = e + c*n
2798
+ r_OFFSET = e + r*n + n*n
2799
+ xy_OFFSET = 2*n*n + r*n + c
2800
+
2801
+ cmap[(c_OFFSET, r_OFFSET, xy_OFFSET)] = (r, c, e)
2802
+
2803
+ # We only want the correct value to pop in here
2804
+ if P[r, c] >= 0 and P[r, c] != e:
2805
+ continue
2806
+ if P[r, c] < 0 and e in valsrow:
2807
+ continue
2808
+ if P[r, c] < 0 and e in valscol:
2809
+ continue
2810
+
2811
+ dlx_rows.append([c_OFFSET, r_OFFSET, xy_OFFSET])
2812
+
2813
+ return dlx_rows, cmap
2814
+
2815
+
2816
+ def dlxcpp_find_completions(P, nr_to_find=None):
2817
+ """
2818
+ Return a list of all latin squares L of the same order as P such
2819
+ that P is contained in L. The optional parameter nr_to_find
2820
+ limits the number of latin squares that are found.
2821
+
2822
+ EXAMPLES::
2823
+
2824
+ sage: from sage.combinat.matrices.latin import *
2825
+ sage: dlxcpp_find_completions(LatinSquare(2))
2826
+ [[0 1]
2827
+ [1 0], [1 0]
2828
+ [0 1]]
2829
+
2830
+ ::
2831
+
2832
+ sage: dlxcpp_find_completions(LatinSquare(2), 1)
2833
+ [[0 1]
2834
+ [1 0]]
2835
+ """
2836
+ assert P.nrows() == P.ncols()
2837
+
2838
+ dlx_rows, cmap = dlxcpp_rows_and_map(P)
2839
+
2840
+ SOLUTIONS = {}
2841
+ for x in DLXCPP(dlx_rows):
2842
+ x.sort()
2843
+ SOLUTIONS[tuple(x)] = True
2844
+
2845
+ if nr_to_find is not None and len(SOLUTIONS) >= nr_to_find:
2846
+ break
2847
+
2848
+ comps = []
2849
+
2850
+ for i in SOLUTIONS:
2851
+ soln = list(i)
2852
+
2853
+ from copy import deepcopy
2854
+ Q = deepcopy(P)
2855
+
2856
+ for x in soln:
2857
+ (r, c, e) = cmap[tuple(dlx_rows[x])]
2858
+
2859
+ if Q[r, c] >= 0:
2860
+ assert Q[r, c] == e
2861
+ else:
2862
+ Q[r, c] = e
2863
+
2864
+ comps.append(Q)
2865
+
2866
+ return comps
2867
+
2868
+
2869
+ def bitrade(T1, T2):
2870
+ r"""
2871
+ Form the bitrade (Q1, Q2) from (T1, T2) by setting empty the cells
2872
+ (r, c) such that T1[r, c] == T2[r, c].
2873
+
2874
+ EXAMPLES::
2875
+
2876
+ sage: from sage.combinat.matrices.latin import *
2877
+ sage: B1 = back_circulant(5)
2878
+ sage: alpha = isotopism((0,1,2,3,4))
2879
+ sage: beta = isotopism((1,0,2,3,4))
2880
+ sage: gamma = isotopism((2,1,0,3,4))
2881
+ sage: B2 = B1.apply_isotopism(alpha, beta, gamma)
2882
+ sage: T1, T2 = bitrade(B1, B2)
2883
+ sage: T1
2884
+ [ 0 1 -1 3 4]
2885
+ [ 1 -1 -1 4 0]
2886
+ [ 2 -1 4 0 1]
2887
+ [ 3 4 0 1 2]
2888
+ [ 4 0 1 2 3]
2889
+ sage: T2
2890
+ [ 3 4 -1 0 1]
2891
+ [ 0 -1 -1 1 4]
2892
+ [ 1 -1 0 4 2]
2893
+ [ 4 0 1 2 3]
2894
+ [ 2 1 4 3 0]
2895
+ """
2896
+ assert T1.nrows() == T1.ncols()
2897
+ assert T2.nrows() == T2.ncols()
2898
+ assert T1.nrows() == T2.nrows()
2899
+
2900
+ n = T1.nrows()
2901
+
2902
+ from copy import copy
2903
+ Q1 = copy(T1)
2904
+ Q2 = copy(T2)
2905
+
2906
+ for r in range(n):
2907
+ for c in range(n):
2908
+ if T1[r, c] == T2[r, c]:
2909
+ Q1[r, c] = -1
2910
+ Q2[r, c] = -1
2911
+
2912
+ return Q1, Q2