passagemath-combinat 10.6.42__cp314-cp314t-win_amd64.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 (401) hide show
  1. passagemath_combinat/__init__.py +3 -0
  2. passagemath_combinat-10.6.42.dist-info/DELVEWHEEL +2 -0
  3. passagemath_combinat-10.6.42.dist-info/METADATA +160 -0
  4. passagemath_combinat-10.6.42.dist-info/RECORD +401 -0
  5. passagemath_combinat-10.6.42.dist-info/WHEEL +5 -0
  6. passagemath_combinat-10.6.42.dist-info/top_level.txt +3 -0
  7. passagemath_combinat.libs/libgmp-10-3a5f019e2510aeaad918cab2b57a689d.dll +0 -0
  8. passagemath_combinat.libs/libsymmetrica-3-7dcf900932804d0df5fd0919b4668720.dll +0 -0
  9. sage/algebras/affine_nil_temperley_lieb.py +263 -0
  10. sage/algebras/all.py +24 -0
  11. sage/algebras/all__sagemath_combinat.py +35 -0
  12. sage/algebras/askey_wilson.py +935 -0
  13. sage/algebras/associated_graded.py +345 -0
  14. sage/algebras/cellular_basis.py +350 -0
  15. sage/algebras/cluster_algebra.py +2766 -0
  16. sage/algebras/down_up_algebra.py +860 -0
  17. sage/algebras/free_algebra.py +1698 -0
  18. sage/algebras/free_algebra_element.py +345 -0
  19. sage/algebras/free_algebra_quotient.py +405 -0
  20. sage/algebras/free_algebra_quotient_element.py +295 -0
  21. sage/algebras/free_zinbiel_algebra.py +885 -0
  22. sage/algebras/hall_algebra.py +783 -0
  23. sage/algebras/hecke_algebras/all.py +4 -0
  24. sage/algebras/hecke_algebras/ariki_koike_algebra.py +1796 -0
  25. sage/algebras/hecke_algebras/ariki_koike_specht_modules.py +475 -0
  26. sage/algebras/hecke_algebras/cubic_hecke_algebra.py +3520 -0
  27. sage/algebras/hecke_algebras/cubic_hecke_base_ring.py +1473 -0
  28. sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py +1079 -0
  29. sage/algebras/iwahori_hecke_algebra.py +3095 -0
  30. sage/algebras/jordan_algebra.py +1773 -0
  31. sage/algebras/lie_conformal_algebras/abelian_lie_conformal_algebra.py +113 -0
  32. sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py +156 -0
  33. sage/algebras/lie_conformal_algebras/all.py +18 -0
  34. sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra.py +134 -0
  35. sage/algebras/lie_conformal_algebras/examples.py +43 -0
  36. sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra.py +131 -0
  37. sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py +139 -0
  38. sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py +174 -0
  39. sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py +167 -0
  40. sage/algebras/lie_conformal_algebras/freely_generated_lie_conformal_algebra.py +107 -0
  41. sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py +135 -0
  42. sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py +353 -0
  43. sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +236 -0
  44. sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py +78 -0
  45. sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py +328 -0
  46. sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra.py +117 -0
  47. sage/algebras/lie_conformal_algebras/neveu_schwarz_lie_conformal_algebra.py +86 -0
  48. sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra.py +82 -0
  49. sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra.py +205 -0
  50. sage/algebras/nil_coxeter_algebra.py +191 -0
  51. sage/algebras/q_commuting_polynomials.py +673 -0
  52. sage/algebras/q_system.py +608 -0
  53. sage/algebras/quantum_clifford.py +959 -0
  54. sage/algebras/quantum_groups/ace_quantum_onsager.py +693 -0
  55. sage/algebras/quantum_groups/all.py +9 -0
  56. sage/algebras/quantum_groups/fock_space.py +2219 -0
  57. sage/algebras/quantum_groups/q_numbers.py +207 -0
  58. sage/algebras/quantum_groups/quantum_group_gap.py +2695 -0
  59. sage/algebras/quantum_groups/representations.py +591 -0
  60. sage/algebras/quantum_matrix_coordinate_algebra.py +1006 -0
  61. sage/algebras/quantum_oscillator.py +623 -0
  62. sage/algebras/quaternion_algebra.py +20 -0
  63. sage/algebras/quaternion_algebra_element.py +55 -0
  64. sage/algebras/rational_cherednik_algebra.py +525 -0
  65. sage/algebras/schur_algebra.py +670 -0
  66. sage/algebras/shuffle_algebra.py +1011 -0
  67. sage/algebras/splitting_algebra.py +779 -0
  68. sage/algebras/tensor_algebra.py +709 -0
  69. sage/algebras/yangian.py +1082 -0
  70. sage/algebras/yokonuma_hecke_algebra.py +1018 -0
  71. sage/all__sagemath_combinat.py +44 -0
  72. sage/combinat/SJT.py +255 -0
  73. sage/combinat/affine_permutation.py +2405 -0
  74. sage/combinat/algebraic_combinatorics.py +55 -0
  75. sage/combinat/all.py +53 -0
  76. sage/combinat/all__sagemath_combinat.py +195 -0
  77. sage/combinat/alternating_sign_matrix.py +2063 -0
  78. sage/combinat/baxter_permutations.py +346 -0
  79. sage/combinat/bijectionist.py +3220 -0
  80. sage/combinat/binary_recurrence_sequences.py +1180 -0
  81. sage/combinat/blob_algebra.py +685 -0
  82. sage/combinat/catalog_partitions.py +27 -0
  83. sage/combinat/chas/all.py +23 -0
  84. sage/combinat/chas/fsym.py +1180 -0
  85. sage/combinat/chas/wqsym.py +2601 -0
  86. sage/combinat/cluster_complex.py +326 -0
  87. sage/combinat/colored_permutations.py +2039 -0
  88. sage/combinat/colored_permutations_representations.py +964 -0
  89. sage/combinat/composition_signed.py +142 -0
  90. sage/combinat/composition_tableau.py +855 -0
  91. sage/combinat/constellation.py +1729 -0
  92. sage/combinat/core.py +751 -0
  93. sage/combinat/counting.py +12 -0
  94. sage/combinat/crystals/affine.py +742 -0
  95. sage/combinat/crystals/affine_factorization.py +518 -0
  96. sage/combinat/crystals/affinization.py +331 -0
  97. sage/combinat/crystals/alcove_path.py +2013 -0
  98. sage/combinat/crystals/all.py +22 -0
  99. sage/combinat/crystals/bkk_crystals.py +141 -0
  100. sage/combinat/crystals/catalog.py +115 -0
  101. sage/combinat/crystals/catalog_elementary_crystals.py +18 -0
  102. sage/combinat/crystals/catalog_infinity_crystals.py +33 -0
  103. sage/combinat/crystals/catalog_kirillov_reshetikhin.py +18 -0
  104. sage/combinat/crystals/crystals.py +257 -0
  105. sage/combinat/crystals/direct_sum.py +260 -0
  106. sage/combinat/crystals/elementary_crystals.py +1251 -0
  107. sage/combinat/crystals/fast_crystals.py +441 -0
  108. sage/combinat/crystals/fully_commutative_stable_grothendieck.py +1205 -0
  109. sage/combinat/crystals/generalized_young_walls.py +1076 -0
  110. sage/combinat/crystals/highest_weight_crystals.py +436 -0
  111. sage/combinat/crystals/induced_structure.py +695 -0
  112. sage/combinat/crystals/infinity_crystals.py +730 -0
  113. sage/combinat/crystals/kac_modules.py +863 -0
  114. sage/combinat/crystals/kirillov_reshetikhin.py +4196 -0
  115. sage/combinat/crystals/kyoto_path_model.py +497 -0
  116. sage/combinat/crystals/letters.cp314t-win_amd64.pyd +0 -0
  117. sage/combinat/crystals/letters.pxd +79 -0
  118. sage/combinat/crystals/letters.pyx +3056 -0
  119. sage/combinat/crystals/littelmann_path.py +1518 -0
  120. sage/combinat/crystals/monomial_crystals.py +1262 -0
  121. sage/combinat/crystals/multisegments.py +462 -0
  122. sage/combinat/crystals/mv_polytopes.py +467 -0
  123. sage/combinat/crystals/pbw_crystal.py +511 -0
  124. sage/combinat/crystals/pbw_datum.cp314t-win_amd64.pyd +0 -0
  125. sage/combinat/crystals/pbw_datum.pxd +4 -0
  126. sage/combinat/crystals/pbw_datum.pyx +487 -0
  127. sage/combinat/crystals/polyhedral_realization.py +372 -0
  128. sage/combinat/crystals/spins.cp314t-win_amd64.pyd +0 -0
  129. sage/combinat/crystals/spins.pxd +21 -0
  130. sage/combinat/crystals/spins.pyx +756 -0
  131. sage/combinat/crystals/star_crystal.py +290 -0
  132. sage/combinat/crystals/subcrystal.py +464 -0
  133. sage/combinat/crystals/tensor_product.py +1177 -0
  134. sage/combinat/crystals/tensor_product_element.cp314t-win_amd64.pyd +0 -0
  135. sage/combinat/crystals/tensor_product_element.pxd +35 -0
  136. sage/combinat/crystals/tensor_product_element.pyx +1870 -0
  137. sage/combinat/crystals/virtual_crystal.py +420 -0
  138. sage/combinat/cyclic_sieving_phenomenon.py +204 -0
  139. sage/combinat/debruijn_sequence.cp314t-win_amd64.pyd +0 -0
  140. sage/combinat/debruijn_sequence.pyx +355 -0
  141. sage/combinat/decorated_permutation.py +270 -0
  142. sage/combinat/degree_sequences.cp314t-win_amd64.pyd +0 -0
  143. sage/combinat/degree_sequences.pyx +588 -0
  144. sage/combinat/derangements.py +527 -0
  145. sage/combinat/descent_algebra.py +1008 -0
  146. sage/combinat/diagram.py +1551 -0
  147. sage/combinat/diagram_algebras.py +5886 -0
  148. sage/combinat/dyck_word.py +4349 -0
  149. sage/combinat/e_one_star.py +1623 -0
  150. sage/combinat/enumerated_sets.py +123 -0
  151. sage/combinat/expnums.cp314t-win_amd64.pyd +0 -0
  152. sage/combinat/expnums.pyx +148 -0
  153. sage/combinat/fast_vector_partitions.cp314t-win_amd64.pyd +0 -0
  154. sage/combinat/fast_vector_partitions.pyx +346 -0
  155. sage/combinat/fqsym.py +1977 -0
  156. sage/combinat/free_dendriform_algebra.py +954 -0
  157. sage/combinat/free_prelie_algebra.py +1141 -0
  158. sage/combinat/fully_commutative_elements.py +1077 -0
  159. sage/combinat/fully_packed_loop.py +1523 -0
  160. sage/combinat/gelfand_tsetlin_patterns.py +1409 -0
  161. sage/combinat/gray_codes.py +311 -0
  162. sage/combinat/grossman_larson_algebras.py +667 -0
  163. sage/combinat/growth.py +4352 -0
  164. sage/combinat/hall_polynomial.py +188 -0
  165. sage/combinat/hillman_grassl.py +866 -0
  166. sage/combinat/integer_matrices.py +329 -0
  167. sage/combinat/integer_vectors_mod_permgroup.py +1238 -0
  168. sage/combinat/k_tableau.py +4564 -0
  169. sage/combinat/kazhdan_lusztig.py +215 -0
  170. sage/combinat/key_polynomial.py +885 -0
  171. sage/combinat/knutson_tao_puzzles.py +2286 -0
  172. sage/combinat/lr_tableau.py +311 -0
  173. sage/combinat/matrices/all.py +24 -0
  174. sage/combinat/matrices/hadamard_matrix.py +3790 -0
  175. sage/combinat/matrices/latin.py +2912 -0
  176. sage/combinat/misc.py +401 -0
  177. sage/combinat/multiset_partition_into_sets_ordered.py +3541 -0
  178. sage/combinat/ncsf_qsym/all.py +21 -0
  179. sage/combinat/ncsf_qsym/combinatorics.py +317 -0
  180. sage/combinat/ncsf_qsym/generic_basis_code.py +1427 -0
  181. sage/combinat/ncsf_qsym/ncsf.py +5637 -0
  182. sage/combinat/ncsf_qsym/qsym.py +4053 -0
  183. sage/combinat/ncsf_qsym/tutorial.py +447 -0
  184. sage/combinat/ncsym/all.py +21 -0
  185. sage/combinat/ncsym/bases.py +855 -0
  186. sage/combinat/ncsym/dual.py +593 -0
  187. sage/combinat/ncsym/ncsym.py +2076 -0
  188. sage/combinat/necklace.py +551 -0
  189. sage/combinat/non_decreasing_parking_function.py +634 -0
  190. sage/combinat/nu_dyck_word.py +1474 -0
  191. sage/combinat/output.py +861 -0
  192. sage/combinat/parallelogram_polyomino.py +4326 -0
  193. sage/combinat/parking_functions.py +1602 -0
  194. sage/combinat/partition_algebra.py +1998 -0
  195. sage/combinat/partition_kleshchev.py +1982 -0
  196. sage/combinat/partition_shifting_algebras.py +584 -0
  197. sage/combinat/partition_tuple.py +3114 -0
  198. sage/combinat/path_tableaux/all.py +13 -0
  199. sage/combinat/path_tableaux/catalog.py +29 -0
  200. sage/combinat/path_tableaux/dyck_path.py +380 -0
  201. sage/combinat/path_tableaux/frieze.py +476 -0
  202. sage/combinat/path_tableaux/path_tableau.py +728 -0
  203. sage/combinat/path_tableaux/semistandard.py +510 -0
  204. sage/combinat/perfect_matching.py +779 -0
  205. sage/combinat/plane_partition.py +3300 -0
  206. sage/combinat/q_bernoulli.cp314t-win_amd64.pyd +0 -0
  207. sage/combinat/q_bernoulli.pyx +128 -0
  208. sage/combinat/quickref.py +81 -0
  209. sage/combinat/recognizable_series.py +2051 -0
  210. sage/combinat/regular_sequence.py +4316 -0
  211. sage/combinat/regular_sequence_bounded.py +543 -0
  212. sage/combinat/restricted_growth.py +81 -0
  213. sage/combinat/ribbon.py +20 -0
  214. sage/combinat/ribbon_shaped_tableau.py +489 -0
  215. sage/combinat/ribbon_tableau.py +1180 -0
  216. sage/combinat/rigged_configurations/all.py +46 -0
  217. sage/combinat/rigged_configurations/bij_abstract_class.py +548 -0
  218. sage/combinat/rigged_configurations/bij_infinity.py +370 -0
  219. sage/combinat/rigged_configurations/bij_type_A.py +163 -0
  220. sage/combinat/rigged_configurations/bij_type_A2_dual.py +338 -0
  221. sage/combinat/rigged_configurations/bij_type_A2_even.py +218 -0
  222. sage/combinat/rigged_configurations/bij_type_A2_odd.py +199 -0
  223. sage/combinat/rigged_configurations/bij_type_B.py +900 -0
  224. sage/combinat/rigged_configurations/bij_type_C.py +267 -0
  225. sage/combinat/rigged_configurations/bij_type_D.py +771 -0
  226. sage/combinat/rigged_configurations/bij_type_D_tri.py +392 -0
  227. sage/combinat/rigged_configurations/bij_type_D_twisted.py +576 -0
  228. sage/combinat/rigged_configurations/bij_type_E67.py +402 -0
  229. sage/combinat/rigged_configurations/bijection.py +143 -0
  230. sage/combinat/rigged_configurations/kleber_tree.py +1475 -0
  231. sage/combinat/rigged_configurations/kr_tableaux.py +1898 -0
  232. sage/combinat/rigged_configurations/rc_crystal.py +461 -0
  233. sage/combinat/rigged_configurations/rc_infinity.py +540 -0
  234. sage/combinat/rigged_configurations/rigged_configuration_element.py +2403 -0
  235. sage/combinat/rigged_configurations/rigged_configurations.py +1918 -0
  236. sage/combinat/rigged_configurations/rigged_partition.cp314t-win_amd64.pyd +0 -0
  237. sage/combinat/rigged_configurations/rigged_partition.pxd +15 -0
  238. sage/combinat/rigged_configurations/rigged_partition.pyx +680 -0
  239. sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +499 -0
  240. sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py +428 -0
  241. sage/combinat/rsk.py +3438 -0
  242. sage/combinat/schubert_polynomial.py +508 -0
  243. sage/combinat/set_partition.py +3318 -0
  244. sage/combinat/set_partition_iterator.cp314t-win_amd64.pyd +0 -0
  245. sage/combinat/set_partition_iterator.pyx +136 -0
  246. sage/combinat/set_partition_ordered.py +1590 -0
  247. sage/combinat/sf/abreu_nigro.py +346 -0
  248. sage/combinat/sf/all.py +52 -0
  249. sage/combinat/sf/character.py +576 -0
  250. sage/combinat/sf/classical.py +319 -0
  251. sage/combinat/sf/dual.py +996 -0
  252. sage/combinat/sf/elementary.py +549 -0
  253. sage/combinat/sf/hall_littlewood.py +1028 -0
  254. sage/combinat/sf/hecke.py +336 -0
  255. sage/combinat/sf/homogeneous.py +464 -0
  256. sage/combinat/sf/jack.py +1428 -0
  257. sage/combinat/sf/k_dual.py +1458 -0
  258. sage/combinat/sf/kfpoly.py +447 -0
  259. sage/combinat/sf/llt.py +789 -0
  260. sage/combinat/sf/macdonald.py +2019 -0
  261. sage/combinat/sf/monomial.py +525 -0
  262. sage/combinat/sf/multiplicative.py +113 -0
  263. sage/combinat/sf/new_kschur.py +1786 -0
  264. sage/combinat/sf/ns_macdonald.py +964 -0
  265. sage/combinat/sf/orthogonal.py +246 -0
  266. sage/combinat/sf/orthotriang.py +355 -0
  267. sage/combinat/sf/powersum.py +963 -0
  268. sage/combinat/sf/schur.py +880 -0
  269. sage/combinat/sf/sf.py +1653 -0
  270. sage/combinat/sf/sfa.py +7053 -0
  271. sage/combinat/sf/symplectic.py +253 -0
  272. sage/combinat/sf/witt.py +721 -0
  273. sage/combinat/shifted_primed_tableau.py +2735 -0
  274. sage/combinat/shuffle.py +830 -0
  275. sage/combinat/sidon_sets.py +146 -0
  276. sage/combinat/similarity_class_type.py +1721 -0
  277. sage/combinat/sine_gordon.py +618 -0
  278. sage/combinat/six_vertex_model.py +784 -0
  279. sage/combinat/skew_partition.py +2053 -0
  280. sage/combinat/skew_tableau.py +2989 -0
  281. sage/combinat/sloane_functions.py +8935 -0
  282. sage/combinat/specht_module.py +1403 -0
  283. sage/combinat/species/all.py +48 -0
  284. sage/combinat/species/characteristic_species.py +321 -0
  285. sage/combinat/species/composition_species.py +273 -0
  286. sage/combinat/species/cycle_species.py +284 -0
  287. sage/combinat/species/empty_species.py +155 -0
  288. sage/combinat/species/functorial_composition_species.py +148 -0
  289. sage/combinat/species/generating_series.py +673 -0
  290. sage/combinat/species/library.py +148 -0
  291. sage/combinat/species/linear_order_species.py +169 -0
  292. sage/combinat/species/misc.py +83 -0
  293. sage/combinat/species/partition_species.py +290 -0
  294. sage/combinat/species/permutation_species.py +268 -0
  295. sage/combinat/species/product_species.py +423 -0
  296. sage/combinat/species/recursive_species.py +476 -0
  297. sage/combinat/species/set_species.py +192 -0
  298. sage/combinat/species/species.py +820 -0
  299. sage/combinat/species/structure.py +539 -0
  300. sage/combinat/species/subset_species.py +243 -0
  301. sage/combinat/species/sum_species.py +225 -0
  302. sage/combinat/subword.py +564 -0
  303. sage/combinat/subword_complex.py +2122 -0
  304. sage/combinat/subword_complex_c.cp314t-win_amd64.pyd +0 -0
  305. sage/combinat/subword_complex_c.pyx +119 -0
  306. sage/combinat/super_tableau.py +821 -0
  307. sage/combinat/superpartition.py +1154 -0
  308. sage/combinat/symmetric_group_algebra.py +3774 -0
  309. sage/combinat/symmetric_group_representations.py +1830 -0
  310. sage/combinat/t_sequences.py +877 -0
  311. sage/combinat/tableau.py +9506 -0
  312. sage/combinat/tableau_residues.py +860 -0
  313. sage/combinat/tableau_tuple.py +5353 -0
  314. sage/combinat/tiling.py +2432 -0
  315. sage/combinat/triangles_FHM.py +777 -0
  316. sage/combinat/tutorial.py +1857 -0
  317. sage/combinat/vector_partition.py +337 -0
  318. sage/combinat/words/abstract_word.py +1722 -0
  319. sage/combinat/words/all.py +59 -0
  320. sage/combinat/words/alphabet.py +268 -0
  321. sage/combinat/words/finite_word.py +7201 -0
  322. sage/combinat/words/infinite_word.py +113 -0
  323. sage/combinat/words/lyndon_word.py +652 -0
  324. sage/combinat/words/morphic.py +351 -0
  325. sage/combinat/words/morphism.py +3878 -0
  326. sage/combinat/words/paths.py +2932 -0
  327. sage/combinat/words/shuffle_product.py +278 -0
  328. sage/combinat/words/suffix_trees.py +1873 -0
  329. sage/combinat/words/word.py +769 -0
  330. sage/combinat/words/word_char.cp314t-win_amd64.pyd +0 -0
  331. sage/combinat/words/word_char.pyx +847 -0
  332. sage/combinat/words/word_datatypes.cp314t-win_amd64.pyd +0 -0
  333. sage/combinat/words/word_datatypes.pxd +4 -0
  334. sage/combinat/words/word_datatypes.pyx +1067 -0
  335. sage/combinat/words/word_generators.py +2026 -0
  336. sage/combinat/words/word_infinite_datatypes.py +1218 -0
  337. sage/combinat/words/word_options.py +99 -0
  338. sage/combinat/words/words.py +2396 -0
  339. sage/data_structures/all__sagemath_combinat.py +1 -0
  340. sage/databases/all__sagemath_combinat.py +13 -0
  341. sage/databases/findstat.py +4897 -0
  342. sage/databases/oeis.py +2058 -0
  343. sage/databases/sloane.py +393 -0
  344. sage/dynamics/all__sagemath_combinat.py +14 -0
  345. sage/dynamics/cellular_automata/all.py +7 -0
  346. sage/dynamics/cellular_automata/catalog.py +34 -0
  347. sage/dynamics/cellular_automata/elementary.py +612 -0
  348. sage/dynamics/cellular_automata/glca.py +477 -0
  349. sage/dynamics/cellular_automata/solitons.py +1463 -0
  350. sage/dynamics/finite_dynamical_system.py +1249 -0
  351. sage/dynamics/finite_dynamical_system_catalog.py +382 -0
  352. sage/games/all.py +7 -0
  353. sage/games/hexad.py +704 -0
  354. sage/games/quantumino.py +591 -0
  355. sage/games/sudoku.py +889 -0
  356. sage/games/sudoku_backtrack.cp314t-win_amd64.pyd +0 -0
  357. sage/games/sudoku_backtrack.pyx +189 -0
  358. sage/groups/all__sagemath_combinat.py +1 -0
  359. sage/groups/indexed_free_group.py +489 -0
  360. sage/libs/all__sagemath_combinat.py +6 -0
  361. sage/libs/lrcalc/__init__.py +1 -0
  362. sage/libs/lrcalc/lrcalc.py +525 -0
  363. sage/libs/symmetrica/__init__.py +7 -0
  364. sage/libs/symmetrica/all.py +101 -0
  365. sage/libs/symmetrica/kostka.pxi +168 -0
  366. sage/libs/symmetrica/part.pxi +193 -0
  367. sage/libs/symmetrica/plet.pxi +42 -0
  368. sage/libs/symmetrica/sab.pxi +196 -0
  369. sage/libs/symmetrica/sb.pxi +332 -0
  370. sage/libs/symmetrica/sc.pxi +192 -0
  371. sage/libs/symmetrica/schur.pxi +956 -0
  372. sage/libs/symmetrica/symmetrica.cp314t-win_amd64.pyd +0 -0
  373. sage/libs/symmetrica/symmetrica.pxi +1172 -0
  374. sage/libs/symmetrica/symmetrica.pyx +39 -0
  375. sage/monoids/all.py +13 -0
  376. sage/monoids/automatic_semigroup.py +1054 -0
  377. sage/monoids/free_abelian_monoid.py +315 -0
  378. sage/monoids/free_abelian_monoid_element.cp314t-win_amd64.pyd +0 -0
  379. sage/monoids/free_abelian_monoid_element.pxd +16 -0
  380. sage/monoids/free_abelian_monoid_element.pyx +397 -0
  381. sage/monoids/free_monoid.py +335 -0
  382. sage/monoids/free_monoid_element.py +431 -0
  383. sage/monoids/hecke_monoid.py +65 -0
  384. sage/monoids/string_monoid.py +817 -0
  385. sage/monoids/string_monoid_element.py +547 -0
  386. sage/monoids/string_ops.py +143 -0
  387. sage/monoids/trace_monoid.py +972 -0
  388. sage/rings/all__sagemath_combinat.py +2 -0
  389. sage/sat/all.py +4 -0
  390. sage/sat/boolean_polynomials.py +405 -0
  391. sage/sat/converters/__init__.py +6 -0
  392. sage/sat/converters/anf2cnf.py +14 -0
  393. sage/sat/converters/polybori.py +611 -0
  394. sage/sat/solvers/__init__.py +5 -0
  395. sage/sat/solvers/cryptominisat.py +287 -0
  396. sage/sat/solvers/dimacs.py +783 -0
  397. sage/sat/solvers/picosat.py +228 -0
  398. sage/sat/solvers/sat_lp.py +156 -0
  399. sage/sat/solvers/satsolver.cp314t-win_amd64.pyd +0 -0
  400. sage/sat/solvers/satsolver.pxd +3 -0
  401. sage/sat/solvers/satsolver.pyx +405 -0
@@ -0,0 +1,3790 @@
1
+ # sage_setup: distribution = sagemath-combinat
2
+ # sage.doctest: needs sage.graphs sage.modules
3
+ r"""
4
+ Hadamard matrices
5
+
6
+ A Hadamard matrix is an `n\times n` matrix `H` whose entries are either `+1` or `-1`
7
+ and whose rows are mutually orthogonal. For example, the matrix `H_2`
8
+ defined by
9
+
10
+ .. MATH::
11
+
12
+ \left(\begin{array}{rr}
13
+ 1 & 1 \\
14
+ 1 & -1
15
+ \end{array}\right)
16
+
17
+ is a Hadamard matrix. An `n\times n` matrix `H` whose entries are either `+1` or
18
+ `-1` is a Hadamard matrix if and only if:
19
+
20
+ (a) `|det(H)|=n^{n/2}` or
21
+
22
+ (b) `H*H^t = n\cdot I_n`, where `I_n` is the identity matrix.
23
+
24
+ In general, the tensor product of an `m\times m` Hadamard matrix and an
25
+ `n\times n` Hadamard matrix is an `(mn)\times (mn)` matrix. In
26
+ particular, if there is an `n\times n` Hadamard matrix then there is a
27
+ `(2n)\times (2n)` Hadamard matrix (since one may tensor with `H_2`).
28
+ This particular case is sometimes called the Sylvester construction.
29
+
30
+ The Hadamard conjecture (possibly due to Paley) states that a Hadamard
31
+ matrix of order `n` exists if and only if `n= 1, 2` or `n` is a multiple
32
+ of `4`.
33
+
34
+ The module below implements constructions of Hadamard and skew Hadamard matrices
35
+ for all known orders `\le 1200`, plus some more greater than `1200`. It also
36
+ allows you to pull a Hadamard matrix from the database at [SloaHada]_.
37
+
38
+ The following code will test that a construction for all known orders `\le 4k`
39
+ is implemented. The assertion above can be verified by setting ``k=300``
40
+ (note that it will take a long time to run)::
41
+
42
+ sage: from sage.combinat.matrices.hadamard_matrix import (hadamard_matrix,
43
+ ....: skew_hadamard_matrix, is_hadamard_matrix,
44
+ ....: is_skew_hadamard_matrix)
45
+ sage: k = 20
46
+ sage: unknown_hadamard = [668, 716, 892, 1132]
47
+ sage: unknown_skew_hadamard = [356, 404, 428, 476, 596, 612, 668, 708, 712, 716,
48
+ ....: 764, 772, 804, 808, 820, 836, 856, 892, 900, 916,
49
+ ....: 932, 940, 952, 980, 996, 1004, 1012, 1028, 1036,
50
+ ....: 1044, 1060, 1076, 1100, 1108, 1132, 1140, 1148,
51
+ ....: 1156, 1180, 1192, 1196]
52
+ sage: for n in range(1, k+1):
53
+ ....: if 4*n not in unknown_hadamard:
54
+ ....: H = hadamard_matrix(4*n, check=False)
55
+ ....: assert is_hadamard_matrix(H)
56
+ ....: if 4*n not in unknown_skew_hadamard:
57
+ ....: H = skew_hadamard_matrix(4*n, check=False)
58
+ ....: assert is_skew_hadamard_matrix(H)
59
+
60
+ AUTHORS:
61
+
62
+ - David Joyner (2009-05-17): initial version
63
+ - Matteo Cati (2023-03-18): implemented more constructions for Hadamard and skew
64
+ Hadamard matrices, to cover all known orders up to 1200.
65
+
66
+ REFERENCES:
67
+
68
+ - [SloaHada]_
69
+
70
+ - [HadaWiki]_
71
+
72
+ - [Hora]_
73
+
74
+ - [CP2023]_
75
+ """
76
+
77
+ # *****************************************************************************
78
+ # This program is free software: you can redistribute it and/or modify
79
+ # it under the terms of the GNU General Public License as published by
80
+ # the Free Software Foundation, either version 2 of the License, or
81
+ # (at your option) any later version.
82
+ # https://www.gnu.org/licenses/
83
+ # *****************************************************************************
84
+
85
+ from math import sqrt
86
+ from urllib.request import urlopen
87
+
88
+ from sage.arith.misc import divisors, is_prime_power, is_square, is_prime
89
+ from sage.combinat.designs.difference_family import (get_fixed_relative_difference_set,
90
+ relative_difference_set_from_homomorphism,
91
+ skew_supplementary_difference_set,
92
+ complementary_difference_sets)
93
+ from sage.combinat.t_sequences import T_sequences_smallcases
94
+ from sage.rings.integer_ring import ZZ
95
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
96
+ from sage.matrix.constructor import (block_matrix,
97
+ block_diagonal_matrix,
98
+ diagonal_matrix,
99
+ identity_matrix as I,
100
+ ones_matrix as J,
101
+ matrix,
102
+ matrix_method,
103
+ zero_matrix)
104
+ from sage.misc.unknown import Unknown
105
+ from sage.modules.free_module_element import vector
106
+
107
+
108
+ def normalise_hadamard(H, skew=False):
109
+ r"""
110
+ Return the normalised Hadamard matrix corresponding to ``H``.
111
+
112
+ The normalised Hadamard matrix corresponding to a Hadamard matrix `H` is a
113
+ matrix whose every entry in the first row and column is +1.
114
+
115
+ If ``skew`` is True, the matrix returned will be skew-normal: a skew Hadamard
116
+ matrix with first row of all `+1`.
117
+
118
+ EXAMPLES::
119
+
120
+ sage: from sage.combinat.matrices.hadamard_matrix import normalise_hadamard, is_hadamard_matrix, skew_hadamard_matrix
121
+ sage: H = normalise_hadamard(hadamard_matrix(4))
122
+ sage: H == hadamard_matrix(4)
123
+ True
124
+ sage: H = normalise_hadamard(skew_hadamard_matrix(20, skew_normalize=False), skew=True)
125
+ sage: is_hadamard_matrix(H, skew=True, normalized=True)
126
+ True
127
+
128
+ If ``skew`` is ``True`` but the Hadamard matrix is not skew, the matrix returned
129
+ will not be normalized::
130
+
131
+ sage: H = normalise_hadamard(hadamard_matrix(92), skew=True)
132
+ sage: is_hadamard_matrix(H, normalized=True)
133
+ False
134
+ """
135
+
136
+ if skew:
137
+ dd = diagonal_matrix(H[0])
138
+ return dd*H*dd
139
+ else:
140
+ for i in range(H.ncols()):
141
+ if H[0, i] < 0:
142
+ H.rescale_col(i, -1)
143
+ for i in range(H.nrows()):
144
+ if H[i, 0] < 0:
145
+ H.rescale_row(i, -1)
146
+ return H
147
+
148
+
149
+ def hadamard_matrix_paleyI(n, normalize=True):
150
+ r"""
151
+ Implement the Paley type I construction.
152
+
153
+ The Paley type I case corresponds to the case `p=n-1 \cong 3 \mod{4}` for a
154
+ prime power `p` (see [Hora]_).
155
+
156
+ INPUT:
157
+
158
+ - ``n`` -- the matrix size
159
+ - ``normalize`` -- boolean (default: ``True``); whether to normalize the result
160
+
161
+ EXAMPLES:
162
+
163
+ We note that this method by default returns a normalised Hadamard matrix ::
164
+
165
+ sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_paleyI
166
+ sage: hadamard_matrix_paleyI(4)
167
+ [ 1 1 1 1]
168
+ [ 1 -1 1 -1]
169
+ [ 1 -1 -1 1]
170
+ [ 1 1 -1 -1]
171
+
172
+ Otherwise, it returns a skew Hadamard matrix `H`, i.e. `H=S+I`, with
173
+ `S=-S^\top` ::
174
+
175
+ sage: M = hadamard_matrix_paleyI(4, normalize=False); M
176
+ [ 1 1 1 1]
177
+ [-1 1 1 -1]
178
+ [-1 -1 1 1]
179
+ [-1 1 -1 1]
180
+ sage: S = M - identity_matrix(4); -S == S.T
181
+ True
182
+
183
+ TESTS::
184
+
185
+ sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix
186
+ sage: test_cases = [x+1 for x in range(100) if is_prime_power(x) and x%4==3]
187
+ sage: all(is_hadamard_matrix(hadamard_matrix_paleyI(n),normalized=True,verbose=True)
188
+ ....: for n in test_cases)
189
+ True
190
+ sage: all(is_hadamard_matrix(hadamard_matrix_paleyI(n,normalize=False),verbose=True)
191
+ ....: for n in test_cases)
192
+ True
193
+ """
194
+ p = n - 1
195
+ if not (is_prime_power(p) and (p % 4 == 3)):
196
+ raise ValueError("The order %s is not covered by the Paley type I construction." % n)
197
+
198
+ from sage.rings.finite_rings.finite_field_constructor import FiniteField
199
+ K = FiniteField(p, 'x')
200
+ K_list = list(K)
201
+ K_list.insert(0, K.zero())
202
+ H = matrix(ZZ, [[(1 if (x-y).is_square() else -1)
203
+ for x in K_list]
204
+ for y in K_list])
205
+ for i in range(n):
206
+ H[i, 0] = -1
207
+ H[0, i] = 1
208
+ if normalize:
209
+ for i in range(n):
210
+ H[i, i] = -1
211
+ H = normalise_hadamard(H)
212
+ return H
213
+
214
+
215
+ def symmetric_conference_matrix_paley(n):
216
+ r"""
217
+ Construct a symmetric conference matrix of order n.
218
+
219
+ A conference matrix is an `n\times n` matrix `C` with 0s on the main diagonal
220
+ and 1s and -1s elsewhere, satisfying `CC^\top=(n-1)I`. This construction assumes
221
+ that `q = n-1` is a prime power, with `q \cong 1 \mod 4`. See [Hora]_ or [Lon2013]_.
222
+
223
+ These matrices are used in :func:`hadamard_matrix_paleyII`.
224
+
225
+ INPUT:
226
+
227
+ - ``n`` -- integer; the order of the symmetric conference matrix to construct
228
+
229
+ EXAMPLES::
230
+
231
+ sage: from sage.combinat.matrices.hadamard_matrix import symmetric_conference_matrix_paley
232
+ sage: symmetric_conference_matrix_paley(6)
233
+ [ 0 1 1 1 1 1]
234
+ [ 1 0 1 -1 -1 1]
235
+ [ 1 1 0 1 -1 -1]
236
+ [ 1 -1 1 0 1 -1]
237
+ [ 1 -1 -1 1 0 1]
238
+ [ 1 1 -1 -1 1 0]
239
+
240
+ TESTS::
241
+
242
+ sage: symmetric_conference_matrix_paley(5)
243
+ Traceback (most recent call last):
244
+ ...
245
+ ValueError: The order 5 is not covered by Paley construction of symmetric conference matrices.
246
+ """
247
+ q = n - 1
248
+ if not (is_prime_power(q) and (q % 4 == 1)):
249
+ raise ValueError("The order %s is not covered by Paley construction of symmetric conference matrices." % n)
250
+
251
+ from sage.rings.finite_rings.finite_field_constructor import FiniteField
252
+ K = FiniteField(q, 'x')
253
+ K_list = list(K)
254
+ K_list.insert(0, K.zero())
255
+ H = matrix(ZZ, [[(1 if (x-y).is_square() else -1)
256
+ for x in K_list]
257
+ for y in K_list])
258
+ for i in range(n):
259
+ H[0, i] = 1
260
+ H[i, 0] = 1
261
+ H[i, i] = 0
262
+ return H
263
+
264
+
265
+ def hadamard_matrix_paleyII(n):
266
+ r"""
267
+ Implement the Paley type II construction.
268
+
269
+ The Paley type II case corresponds to the case `p=n/2-1 \cong 1 \mod{4}` for a
270
+ prime power `p` (see [Hora]_).
271
+
272
+ EXAMPLES::
273
+
274
+ sage: sage.combinat.matrices.hadamard_matrix.hadamard_matrix_paleyII(12).det()
275
+ 2985984
276
+ sage: 12^6
277
+ 2985984
278
+
279
+ We note that the method returns a normalised Hadamard matrix ::
280
+
281
+ sage: sage.combinat.matrices.hadamard_matrix.hadamard_matrix_paleyII(12)
282
+ [ 1 1| 1 1| 1 1| 1 1| 1 1| 1 1]
283
+ [ 1 -1|-1 1|-1 1|-1 1|-1 1|-1 1]
284
+ [-----+-----+-----+-----+-----+-----]
285
+ [ 1 -1| 1 -1| 1 1|-1 -1|-1 -1| 1 1]
286
+ [ 1 1|-1 -1| 1 -1|-1 1|-1 1| 1 -1]
287
+ [-----+-----+-----+-----+-----+-----]
288
+ [ 1 -1| 1 1| 1 -1| 1 1|-1 -1|-1 -1]
289
+ [ 1 1| 1 -1|-1 -1| 1 -1|-1 1|-1 1]
290
+ [-----+-----+-----+-----+-----+-----]
291
+ [ 1 -1|-1 -1| 1 1| 1 -1| 1 1|-1 -1]
292
+ [ 1 1|-1 1| 1 -1|-1 -1| 1 -1|-1 1]
293
+ [-----+-----+-----+-----+-----+-----]
294
+ [ 1 -1|-1 -1|-1 -1| 1 1| 1 -1| 1 1]
295
+ [ 1 1|-1 1|-1 1| 1 -1|-1 -1| 1 -1]
296
+ [-----+-----+-----+-----+-----+-----]
297
+ [ 1 -1| 1 1|-1 -1|-1 -1| 1 1| 1 -1]
298
+ [ 1 1| 1 -1|-1 1|-1 1| 1 -1|-1 -1]
299
+
300
+ TESTS::
301
+
302
+ sage: from sage.combinat.matrices.hadamard_matrix import (hadamard_matrix_paleyII, is_hadamard_matrix)
303
+ sage: test_cases = [2*(x+1) for x in range(50) if is_prime_power(x) and x%4==1]
304
+ sage: all(is_hadamard_matrix(hadamard_matrix_paleyII(n),normalized=True,verbose=True)
305
+ ....: for n in test_cases)
306
+ True
307
+ """
308
+ q = n//2 - 1
309
+ if not (n % 2 == 0 and is_prime_power(q) and (q % 4 == 1)):
310
+ raise ValueError("The order %s is not covered by the Paley type II construction." % n)
311
+
312
+ H = symmetric_conference_matrix_paley(q+1)
313
+
314
+ tr = { 0: matrix(2, 2, [ 1, -1, -1, -1]),
315
+ 1: matrix(2, 2, [ 1, 1, 1, -1]),
316
+ -1: matrix(2, 2, [-1, -1, -1, 1])}
317
+
318
+ H = block_matrix(q+1, q+1, [tr[v] for r in H for v in r])
319
+
320
+ return normalise_hadamard(H)
321
+
322
+
323
+ def hadamard_matrix_from_symmetric_conference_matrix(n, existence=False, check=True):
324
+ r"""
325
+ Construct a Hadamard matrix of order `n` from a symmetric conference matrix
326
+ of order `n/2`.
327
+
328
+ The construction is described in Theorem 4.3.24 of [IS2006]_.
329
+ The symmetric conference matrices are obtained from
330
+ :func:`sage.combinat.matrices.hadamard_matrix.symmetric_conference_matrix`.
331
+
332
+ INPUT:
333
+
334
+ - ``n`` -- integer; the order of the matrix to be constructed
335
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only check if
336
+ the matrix exists
337
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the matrix
338
+ is a Hadamard before returning
339
+
340
+ OUTPUT:
341
+
342
+ If ``existence=False``, returns the Hadamard matrix of order `n`. It raises
343
+ an error if no data is available to construct the matrix of the given order,
344
+ or if `n` does not satisfies the constraints.
345
+ If ``existence=True``, returns a boolean representing whether the matrix
346
+ can be constructed or not.
347
+
348
+ EXAMPLES:
349
+
350
+ By default the function returns the Hadamard matrix ::
351
+
352
+ sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_from_symmetric_conference_matrix
353
+ sage: hadamard_matrix_from_symmetric_conference_matrix(20) # needs database_graphs
354
+ 20 x 20 dense matrix over Integer Ring...
355
+
356
+ If ``existence`` is set to True, the function returns True if the matrix exists,
357
+ False if the conference matrix does not exist, and Unknown if the conference
358
+ matrix cannot be constructed yet ::
359
+
360
+ sage: hadamard_matrix_from_symmetric_conference_matrix(12, existence=True) # needs database_graphs
361
+ True
362
+ sage: hadamard_matrix_from_symmetric_conference_matrix(4*787, existence=True) # needs database_graphs
363
+ True
364
+
365
+ TESTS::
366
+
367
+ sage: # needs database_graphs
368
+ sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix
369
+ sage: is_hadamard_matrix(hadamard_matrix_from_symmetric_conference_matrix(60, check=False))
370
+ True
371
+ sage: hadamard_matrix_from_symmetric_conference_matrix(64, existence=True)
372
+ False
373
+ sage: hadamard_matrix_from_symmetric_conference_matrix(4*787, existence=True)
374
+ True
375
+ sage: hadamard_matrix_from_symmetric_conference_matrix(64)
376
+ Traceback (most recent call last):
377
+ ...
378
+ ValueError: Cannot construct Hadamard matrix of order 64, a symmetric conference matrix of order 32 is not available in sage.
379
+ sage: hadamard_matrix_from_symmetric_conference_matrix(14)
380
+ Traceback (most recent call last):
381
+ ...
382
+ ValueError: No Hadamard matrix of order 14 exists.
383
+ """
384
+ if n < 0 or n % 4 != 0:
385
+ raise ValueError(f'No Hadamard matrix of order {n} exists.')
386
+
387
+ m = n//2
388
+ exists = symmetric_conference_matrix(m, existence=True)
389
+
390
+ if existence:
391
+ return exists
392
+
393
+ if not exists:
394
+ raise ValueError(f'Cannot construct Hadamard matrix of order {n}, a symmetric conference matrix of order {m} is not available in sage.')
395
+
396
+ C = symmetric_conference_matrix(m)
397
+
398
+ H = block_matrix([[C + I(m), C - I(m)],
399
+ [C - I(m), -C - I(m)]])
400
+
401
+ if check:
402
+ assert is_hadamard_matrix(H)
403
+ return H
404
+
405
+
406
+ def hadamard_matrix_miyamoto_construction(n, existence=False, check=True):
407
+ r"""
408
+ Construct Hadamard matrix using the Miyamoto construction.
409
+
410
+ If `q = n/4` is a prime power, and there exists a Hadamard matrix of order
411
+ `q-1`, then a Hadamard matrix of order `n` can be constructed (see [Miy1991]_).
412
+
413
+ INPUT:
414
+
415
+ - ``n`` -- integer; the order of the matrix to be constructed
416
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the matrix
417
+ is a Hadamard before returning
418
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only check if
419
+ the matrix exists
420
+
421
+ OUTPUT:
422
+
423
+ If ``existence=False``, returns the Hadamard matrix of order `n`. It raises
424
+ an error if no data is available to construct the matrix of the given order,
425
+ or if `n` does not satisfies the constraints.
426
+ If ``existence=True``, returns a boolean representing whether the matrix
427
+ can be constructed or not.
428
+
429
+ EXAMPLES:
430
+
431
+ By default the function returns the Hadamard matrix ::
432
+
433
+ sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_miyamoto_construction
434
+ sage: hadamard_matrix_miyamoto_construction(20) # needs database_graphs
435
+ 20 x 20 dense matrix over Integer Ring...
436
+
437
+ If ``existence`` is set to True, the function returns a boolean ::
438
+
439
+ sage: hadamard_matrix_miyamoto_construction(36, existence=True) # needs database_graphs
440
+ True
441
+
442
+ TESTS::
443
+
444
+ sage: # needs database_graphs
445
+ sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix
446
+ sage: is_hadamard_matrix(hadamard_matrix_miyamoto_construction(68, check=False))
447
+ True
448
+ sage: hadamard_matrix_miyamoto_construction(64, existence=True)
449
+ False
450
+ sage: hadamard_matrix_miyamoto_construction(4*65, existence=True)
451
+ True
452
+ sage: is_hadamard_matrix(hadamard_matrix_miyamoto_construction(4*65, check=False))
453
+ True
454
+ sage: hadamard_matrix_miyamoto_construction(64)
455
+ Traceback (most recent call last):
456
+ ...
457
+ ValueError: The order 64 is not covered by Miyamoto construction.
458
+ sage: hadamard_matrix_miyamoto_construction(14)
459
+ Traceback (most recent call last):
460
+ ...
461
+ ValueError: No Hadamard matrix of order 14 exists.
462
+ """
463
+ if n < 0 or n % 4 != 0:
464
+ raise ValueError(f'No Hadamard matrix of order {n} exists.')
465
+
466
+ q = n // 4
467
+ if existence:
468
+ # return is_prime_power(q) and q % 4 == 1 and hadamard_matrix(q-1, existence=True) is True
469
+ return symmetric_conference_matrix(q+1, existence=True) and hadamard_matrix(q-1, existence=True) is True
470
+
471
+ # if not (is_prime_power(q) and q % 4 == 1 and hadamard_matrix(q-1, existence=True)):
472
+ if not (symmetric_conference_matrix(q+1, existence=True) and hadamard_matrix(q-1, existence=True)):
473
+ raise ValueError(f'The order {n} is not covered by Miyamoto construction.')
474
+
475
+ m = (q-1) // 2
476
+
477
+ C = symmetric_conference_matrix(q + 1)
478
+
479
+ neg = [i for i in range(2, m+2) if C[1, i] == -1]
480
+ pos = [i for i in range(m+2, 2*m+2) if C[1, i] == 1]
481
+
482
+ for i, j in zip(neg, pos):
483
+ C.swap_rows(i, j)
484
+ C.swap_columns(i, j)
485
+
486
+ C1 = -C.submatrix(row=2, col=2, nrows=m, ncols=m)
487
+ C2 = C.submatrix(row=2, col=m+2, nrows=m, ncols=m)
488
+ C4 = C.submatrix(row=m+2, col=m+2, nrows=m, ncols=m)
489
+
490
+ K = hadamard_matrix(q - 1)
491
+ K1 = K.submatrix(row=0, col=0, nrows=(q-1)//2, ncols=(q-1)//2)
492
+ K2 = K.submatrix(row=0, col=(q-1)//2, nrows=(q-1)//2, ncols=(q-1)//2)
493
+ K3 = -K.submatrix(row=(q-1)//2, col=0, nrows=(q-1)//2, ncols=(q-1)//2)
494
+ K4 = K.submatrix(row=(q-1)//2, col=(q-1)//2, nrows=(q-1)//2, ncols=(q-1)//2)
495
+
496
+ Zr = zero_matrix(m)
497
+ Us = [[C1, C2, Zr, Zr], [C2.T, C4, Zr, Zr], [Zr, Zr, C1, C2], [Zr, Zr, C2.T, C4]]
498
+ Vs = [[I(m), Zr, K1, K2], [Zr, I(m), K3, K4], [K1.T, K3.T, I(m), Zr], [K2.T, K4.T, Zr, I(m)]]
499
+
500
+ def T(i, j):
501
+ return block_matrix([[Us[i][j]+Vs[i][j], Us[i][j]-Vs[i][j]],
502
+ [Us[i][j]-Vs[i][j], Us[i][j]+Vs[i][j]]])
503
+
504
+ e = matrix([[1] * (2*m)])
505
+ one = matrix([1])
506
+ H = block_matrix([[ one, -e, one, e, one, e, one, e],
507
+ [-e.T, T(0, 0), e.T, T(0, 1), e.T, T(0, 2), e.T, T(0, 3)],
508
+ [-one, -e, one, -e, one, e, -one, -e],
509
+ [-e.T, -T(1, 0), -e.T, T(1, 1), e.T, T(1, 2), -e.T, -T(1, 3)],
510
+ [-one, -e, -one, -e, one, -e, one, e],
511
+ [-e.T, -T(2, 0), -e.T, -T(2, 1), -e.T, T(2, 2), e.T, T(2, 3)],
512
+ [-one, -e, one, e, -one, -e, one, -e],
513
+ [-e.T, -T(3, 0), e.T, T(3, 1), -e.T, -T(3, 2), -e.T, T(3, 3)]])
514
+
515
+ if check:
516
+ assert is_hadamard_matrix(H)
517
+ return H
518
+
519
+
520
+ def hadamard_matrix_williamson_type(a, b, c, d, check=True):
521
+ r"""
522
+ Construction of Williamson type Hadamard matrix.
523
+
524
+ Given `n\times n` circulant matrices `A`, `B`, `C`, `D` with 1,-1 entries,
525
+ and satisfying `AA^\top + BB^\top + CC^\top + DD^\top = 4nI`,
526
+ one can construct a Hadamard matrix of order `4n`, cf. [Ha83]_.
527
+
528
+ INPUT:
529
+
530
+ - ``a`` -- (1,-1) list; the 1st row of `A`
531
+ - ``b`` -- (1,-1) list; the 1st row of `B`
532
+ - ``d`` -- (1,-1) list; the 1st row of `C`
533
+ - ``c`` -- (1,-1) list; the 1st row of `D`
534
+ - ``check`` -- boolean (default: ``True``); whether to check that the output
535
+ is a Hadamard matrix before returning it
536
+
537
+ EXAMPLES::
538
+
539
+ sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_williamson_type
540
+ sage: a = [ 1, 1, 1]
541
+ sage: b = [ 1, -1, -1]
542
+ sage: c = [ 1, -1, -1]
543
+ sage: d = [ 1, -1, -1]
544
+ sage: M = hadamard_matrix_williamson_type(a,b,c,d,check=True)
545
+
546
+ TESTS::
547
+
548
+ sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_williamson_type, is_hadamard_matrix
549
+ sage: a = [ 1, 1, 1]
550
+ sage: b = [ 1, -1, -1]
551
+ sage: c = [ 1, -1, -1]
552
+ sage: d = [ 1, -1, -1]
553
+ sage: is_hadamard_matrix(hadamard_matrix_williamson_type(a,b,c,d))
554
+ True
555
+ sage: e = [1, 1, 1]
556
+ sage: hadamard_matrix_williamson_type(a,b,c,e, check=True)
557
+ Traceback (most recent call last):
558
+ ...
559
+ AssertionError
560
+ sage: f = [1, -1, 1, -1]
561
+ sage: hadamard_matrix_williamson_type(a,b,c,f, check=True)
562
+ Traceback (most recent call last):
563
+ ...
564
+ AssertionError
565
+ """
566
+ A, B, C, D = map(matrix.circulant, [a, b, c, d])
567
+
568
+ n = len(a)
569
+ assert len(a) == len(b) == len(c) == len(d)
570
+ assert A*A.T+B*B.T+C*C.T+D*D.T == 4*n*I(n)
571
+
572
+ M = block_matrix([[ A, B, C, D],
573
+ [-B, A, -D, C],
574
+ [-C, D, A, -B],
575
+ [-D, -C, B, A]])
576
+ if check:
577
+ assert is_hadamard_matrix(M, normalized=False, skew=False)
578
+ return M
579
+
580
+
581
+ def williamson_type_quadruples_smallcases(n, existence=False):
582
+ r"""
583
+ Quadruples of matrices that can be used to construct Williamson type Hadamard matrices.
584
+
585
+ This function contains for some values of n, four `n\times n` matrices used in the
586
+ Williamson construction of Hadamard matrices. Namely, the function returns the first row of
587
+ 4 `n\times n` circulant matrices with the properties described in
588
+ :func:`sage.combinat.matrices.hadamard_matrix.hadamard_matrix_williamson_type`.
589
+ The matrices for `n = 3, 5, ..., 29, 37, 43` are given in [Ha83]_. The matrices
590
+ for `n = 31, 33, 39, 41, 45, 49, 51, 55, 57, 61, 63` are given in [Lon2013]_.
591
+
592
+ INPUT:
593
+
594
+ - ``n`` -- integer; the order of the matrices to be returned
595
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only check that
596
+ we have the quadruple
597
+
598
+ OUTPUT:
599
+
600
+ If ``existence`` is false, returns a tuple containing four vectors, each being the first line
601
+ of one of the four matrices. It raises an error if no such matrices are available.
602
+ If ``existence`` is true, returns a boolean representing whether the matrices are available or not.
603
+
604
+ EXAMPLES::
605
+
606
+ sage: from sage.combinat.matrices.hadamard_matrix import williamson_type_quadruples_smallcases
607
+ sage: williamson_type_quadruples_smallcases(29)
608
+ ((1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1),
609
+ (1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1),
610
+ (1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1),
611
+ (1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1))
612
+ sage: williamson_type_quadruples_smallcases(43, existence=True)
613
+ True
614
+
615
+ TESTS::
616
+
617
+ sage: williamson_type_quadruples_smallcases(123, existence=True)
618
+ False
619
+ sage: williamson_type_quadruples_smallcases(123)
620
+ Traceback (most recent call last):
621
+ ...
622
+ ValueError: The Williamson type quadruple of order 123 is not yet implemented.
623
+ """
624
+ db = {
625
+ 1: ('+', '+', '+', '+'),
626
+ 3: ('+++', '+--', '+--', '+--'),
627
+ 5: ('+-++-', '++--+', '+----', '+----'),
628
+ 7: ('+--++--', '+-+--+-', '++----+', '+------'),
629
+ 9: ('+---++---', '+--+--+--', '+-+----+-', '++------+'),
630
+ 11: ('++--------+', '++-+-++-+-+', '++-++--++-+', '+-++----++-'),
631
+ 13: ('++++-+--+-+++', '+---+-++-+---', '++---+--+---+', '++---+--+---+'),
632
+ 15: ('+-+---++++---+-', '++-++------++-+',
633
+ '++-++++--++++-+', '++-++-+--+-++-+'),
634
+ 17: ('+---+++----+++---', '++-+---+--+---+-+',
635
+ '+--+-++++++++-+--', '+-++-+++--+++-++-'),
636
+ 19: ('++--+++-+--+-+++--+', '++-++--+-++-+--++-+',
637
+ '+-+---++++++++---+-', '++--+-++++++++-+--+'),
638
+ 21: ('+--++++---++---++++--', '++++-+---+--+---+-+++',
639
+ '++--+-+-++--++-+-+--+', '++-+++++-+--+-+++++-+'),
640
+ 23: ('++---+---+-++-+---+---+', '+-++-++--++++++--++-++-',
641
+ '+++---++-+-++-+-++---++', '+++-+++-+------+-+++-++'),
642
+ 25: ('++++-+-+-+--++--+-+-+-+++', '++--+--+-++++++++-+--+--+',
643
+ '+++--+--++++--++++--+--++', '+-+--+++--++++++--+++--+-'),
644
+ 27: ('+--+--+-+++--++--+++-+--+--', '+++-++-+---++--++---+-++-++',
645
+ '+---+++++-+-++++-+-+++++---', '+---+++++-+-++++-+-+++++---'),
646
+ 29: ('+++---++--+-+----+-+--++---++', '+-+---++--+-++++++-+--++---+-',
647
+ '++++-++-+---++++++---+-++-+++', '++--+--+-+++-++++-+++-+--+--+'),
648
+ 31: ('++++++-+--+---++++---+--+-+++++', '+--++---+-+-++----++-+-+---++--',
649
+ '+--++---+-+-++----++-+-+---++--', '+-----+-++-+++----+++-++-+-----'),
650
+ 33: ('++++++-+-+-+++------+++-+-+-+++++', '++-+-++-+----+++--+++----+-++-+-+',
651
+ '++--++-+++-+--+-++-+--+-+++-++--+', '+--++--+++++-++----++-+++++--++--'),
652
+ 37: ('+--+-+-+-++---+--++++--+---++-+-+-+--', '+---++-++--+-+-++----++-+-+--++-++---',
653
+ '+++++-+-----++----++----++-----+-++++', '+--+++-+-----+----++----+-----+-+++--'),
654
+ 39: ('+++--+-+-----+--++----++--+-----+-+--++', '+++--++-+---+-+--+----+--+-+---+-++--++',
655
+ '++++---+--++----+-+--+-+----++--+---+++', '+---++-+-+-----+++-++-+++-----+-+-++---'),
656
+ 41: ('++++--+-++++-++--++----++--++-++++-+--+++', '++++--+-++++-++--++----++--++-++++-+--+++',
657
+ '+++-++-+-+-+-----+++--+++-----+-+-+-++-++', '+--+--+-+-+-+++++---++---+++++-+-+-+--+--'),
658
+ 43: ('++---++++-+--+--++--------++--+--+-++++---+', '+++-+-++--+-+-++++-+----+-++++-+-+--++-+-++',
659
+ '++-++++++----+-+--++-++-++--+-+----++++++-+', '+---++--++++-+-+++-++--++-+++-+-++++--++---'),
660
+ 45: ('+++++-++----+-++--++-++-++--++-+----++-++++', '+++---++--+-+-+-++--------++-+-+-+--++---++',
661
+ '++-+-++++-+--+--+++--++--+++--+--+-++++-+-+', '+-++-----++++-+-+++-++++-+++-+-++++-----++-'),
662
+ 49: ('++++-++-+---++-+++---++-++-++---+++-++---+-++-+++', '++++-++-+---++-+++---++-++-++---+++-++---+-++-+++',
663
+ '+----+-++++--+-+++-+-+++--+++-+-+++-+--++++-+----', '+++++-+----++-+---+-+---++---+-+---+-++----+-++++'),
664
+ 51: ('+---+++-++-+-+++--+++++--++--+++++--+++-+-++-+++---', '----+++-++-+-+++--+++++--++--+++++--+++-+-++-+++---',
665
+ '-+--+----+-+++-+-+++++--+--+--+++++-+-+++-+----+--+', '-+--+----+-+++-+-+++++--+--+--+++++-+-+++-+----+--+'),
666
+ 55: ('+-+--+-+-++--+-+++++-+++--++++--+++-+++++-+--++-+-+--+-', '--+--+-+-++--+-+++++-+++--++++--+++-+++++-+--++-+-+--+-',
667
+ '+++----++-++--++----+-+-++++++++-+-+----++--++-++----++', '+++----++-++--++----+-+-++++++++-+-+----++--++-++----++'),
668
+ 57: ('+---++-+--++++-+++-++---+-++++++-+---++-+++-++++--+-++---', '----++-+--++++-+++-++---+-++++++-+---++-+++-++++--+-++---',
669
+ '--+-+-+++--+--+-++---+++++-++++-+++++---++-+--+--+++-+-+-', '--+-+-+++--+--+-++---+++++-++++-+++++---++-+--+--+++-+-+-'),
670
+ 61: ('++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+', '++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+',
671
+ '+---+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---', '++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++'),
672
+ 63: ('++-+++--++-++--+--+-++-+-+++--------+++-+-++-+--+--++-++--+++-+', '-+-+++--++-++--+--+-++-+-+++--------+++-+-++-+--+--++-++--+++-+',
673
+ '++++-++-+-++++-+---+---+++---++++++---+++---+---+-++++-+-++-+++', '++++-++-+-++++-+---+---+++---++++++---+++---+---+-++++-+-++-+++'),
674
+ }
675
+
676
+ def pmtoZ(s):
677
+ return [1 if x == '+' else -1 for x in s]
678
+
679
+ if existence:
680
+ return n in db
681
+
682
+ if n not in db:
683
+ raise ValueError("The Williamson type quadruple of order %s is not yet implemented." % n)
684
+
685
+ a, b, c, d = (vector(pmtoZ(s)) for s in db[n])
686
+ return a, b, c, d
687
+
688
+
689
+ def williamson_hadamard_matrix_smallcases(n, existence=False, check=True):
690
+ r"""
691
+ Construct Williamson type Hadamard matrices for some small values of n.
692
+
693
+ This function uses the data contained in
694
+ :func:`sage.combinat.matrices.hadamard_matrix.williamson_type_quadruples_smallcases`
695
+ to create Hadamard matrices of the Williamson type, using the construction from
696
+ :func:`sage.combinat.matrices.hadamard_matrix.hadamard_matrix_williamson_type`.
697
+
698
+ INPUT:
699
+
700
+ - ``n`` -- integer; the order of the matrix
701
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only check that
702
+ we can do the construction
703
+ - ``check`` -- boolean (default: ``True``); if ``True`` check the result
704
+
705
+ TESTS::
706
+
707
+ sage: from sage.combinat.matrices.hadamard_matrix import williamson_hadamard_matrix_smallcases
708
+ sage: williamson_hadamard_matrix_smallcases(116)
709
+ 116 x 116 dense matrix over Integer Ring...
710
+ sage: williamson_hadamard_matrix_smallcases(172)
711
+ 172 x 172 dense matrix over Integer Ring...
712
+ sage: williamson_hadamard_matrix_smallcases(1000)
713
+ Traceback (most recent call last):
714
+ ...
715
+ ValueError: The Williamson type Hadamard matrix of order 1000 is not yet implemented.
716
+ """
717
+ assert n % 4 == 0
718
+
719
+ if not williamson_type_quadruples_smallcases(n // 4, existence=True):
720
+ if existence:
721
+ return False
722
+ raise ValueError("The Williamson type Hadamard matrix of order %s is not yet implemented." % n)
723
+
724
+ if existence:
725
+ return True
726
+
727
+ a, b, c, d = williamson_type_quadruples_smallcases(n//4)
728
+ return hadamard_matrix_williamson_type(a, b, c, d, check=check)
729
+
730
+
731
+ def hadamard_matrix_156():
732
+ r"""
733
+ Construct a Hadamard matrix of order 156.
734
+
735
+ The matrix is created using the construction detailed in [BH1965]_.
736
+ This uses four circulant matrices of size `13\times 13`,
737
+ which are composed into a `156\times 156` block matrix.
738
+
739
+ TESTS::
740
+
741
+ sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix, hadamard_matrix_156
742
+ sage: is_hadamard_matrix(hadamard_matrix_156())
743
+ True
744
+ sage: hadamard_matrix_156()
745
+ 156 x 156 dense matrix over Integer Ring...
746
+ """
747
+ a = [1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1]
748
+ b = [1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1]
749
+ c = [1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1]
750
+ d = [1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1]
751
+
752
+ A, B, C, D = map(matrix.circulant, [a, b, c, d])
753
+
754
+ return block_matrix([[ A, A, A, B, -B, C, -C, -D, B, C, -D, -D],
755
+ [ A, -A, B, -A, -B, -D, D, -C, -B, -D, -C, -C],
756
+ [ A, -B, -A, A, -D, D, -B, B, -C, -D, C, -C],
757
+ [ B, A, -A, -A, D, D, D, C, C, -B, -B, -C],
758
+ [ B, -D, D, D, A, A, A, C, -C, B, -C, B],
759
+ [ B, C, -D, D, A, -A, C, -A, -D, C, B, -B],
760
+ [ D, -C, B, -B, A, -C, -A, A, B, C, D, -D],
761
+ [-C, -D, -C, -D, C, A, -A, -A, -D, B, -B, -B],
762
+ [ D, -C, -B, -B, -B, C, C, -D, A, A, A, D],
763
+ [-D, -B, C, C, C, B, B, -D, A, -A, D, -A],
764
+ [ C, -B, -C, C, D, -B, -D, -B, A, -D, -A, A],
765
+ [-C, -D, -D, C, -C, -B, B, B, D, A, -A, -A]])
766
+
767
+
768
+ def construction_four_symbol_delta_code_I(X, Y, Z, W):
769
+ r"""
770
+ Construct 4-symbol `\delta` code of length `2n+1`.
771
+
772
+ The 4-symbol `\delta` code is constructed from sequences `X, Y, Z, W` of
773
+ length `n+1`, `n+1`, `n`, `n` satisfying for all `s > 0`:
774
+
775
+ .. MATH::
776
+
777
+ N_X(s) + N_Y(s) + N_Z(s) + N_W(s) = 0
778
+
779
+ where `N_A(s)` is the nonperiodic correlation function:
780
+
781
+ .. MATH::
782
+
783
+ N_A(s) = \sum_{i=1}^{n-s}a_ia_{i+s}
784
+
785
+ The construction (detailed in [Tur1974]_) is as follows:
786
+
787
+ .. MATH::
788
+
789
+ \begin{aligned}
790
+ T_1 &= X;Z \\
791
+ T_2 &= X;-Z \\
792
+ T_3 &= Y;W \\
793
+ T_4 &= Y;-W
794
+ \end{aligned}
795
+
796
+ INPUT:
797
+
798
+ - ``X`` -- list; the first sequence (length `n+1`)
799
+ - ``Y`` -- list; the second sequence (length `n+1`)
800
+ - ``Z`` -- list; the third sequence (length `n`)
801
+ - ``W`` -- list; the fourth sequence (length `n`)
802
+
803
+ OUTPUT: tuple containing the 4-symbol `\delta` code of length `2n+1`
804
+
805
+ EXAMPLES::
806
+
807
+ sage: from sage.combinat.matrices.hadamard_matrix import construction_four_symbol_delta_code_I
808
+ sage: construction_four_symbol_delta_code_I([1, 1], [1, -1], [1], [1])
809
+ ([1, 1, 1], [1, 1, -1], [1, -1, 1], [1, -1, -1])
810
+
811
+ TESTS::
812
+
813
+ sage: construction_four_symbol_delta_code_I([1, 1], [1, -1], [1, 1], [1])
814
+ Traceback (most recent call last):
815
+ ...
816
+ AssertionError
817
+ sage: construction_four_symbol_delta_code_I([1, 1], [1, 1], [-1], [1])
818
+ Traceback (most recent call last):
819
+ ...
820
+ AssertionError
821
+ """
822
+ n = len(X)
823
+ assert len(Y) == n and len(Z) == n-1 and len(W) == n-1
824
+
825
+ def autocorrelation(seq, j):
826
+ return sum([seq[i]*seq[i+j] for i in range(len(seq)-j)])
827
+ for j in range(1, n):
828
+ assert sum(autocorrelation(seq, j) for seq in [X, Y, Z, W]) == 0
829
+
830
+ T1 = X + Z
831
+ T2 = X + [-z for z in Z]
832
+ T3 = Y + W
833
+ T4 = Y + [-w for w in W]
834
+ return T1, T2, T3, T4
835
+
836
+
837
+ def construction_four_symbol_delta_code_II(X, Y, Z, W):
838
+ r"""
839
+ Construct 4-symbol `\delta` code of length `4n+3`.
840
+
841
+ The 4-symbol `\delta` code is constructed from sequences `X, Y, Z, W` of
842
+ length `n+1`, `n+1`, `n`, `n` satisfying for all `s > 0`:
843
+
844
+ .. MATH::
845
+ N_X(s) + N_Y(s) + N_Z(s) + N_W(s) = 0
846
+
847
+ where `N_A(s)` is the nonperiodic correlation function:
848
+
849
+ .. MATH::
850
+
851
+ N_A(s) = \sum_{i=1}^{n-s}a_ia_{i+s}
852
+
853
+ The construction (detailed in [Tur1974]_) is as follows (writing
854
+ `A/B` to mean `A` alternated with `B`):
855
+
856
+ .. MATH::
857
+
858
+ \begin{aligned}
859
+ T_1 &= X/Z;Y/W;1 \\
860
+ T_2 &= X/Z;Y/-W;-1 \\
861
+ T_3 &= X/Z;-Y/-W;1 \\
862
+ T_4 &= X/Z;-Y/W;-1
863
+ \end{aligned}
864
+
865
+ INPUT:
866
+
867
+ - ``X`` -- list; the first sequence (length `n+1`)
868
+ - ``Y`` -- list; the second sequence (length `n+1`)
869
+ - ``Z`` -- list; the third sequence (length `n`)
870
+ - ``W`` -- list; the fourth sequence (length `n`)
871
+
872
+ OUTPUT: tuple containing the four 4-symbol `\delta` code of length `4n+3`
873
+
874
+ EXAMPLES::
875
+
876
+ sage: from sage.combinat.matrices.hadamard_matrix import construction_four_symbol_delta_code_II
877
+ sage: construction_four_symbol_delta_code_II([1, 1], [1, -1], [1], [1])
878
+ ([1, 1, 1, 1, 1, -1, 1],
879
+ [1, 1, 1, 1, -1, -1, -1],
880
+ [1, 1, 1, -1, -1, 1, 1],
881
+ [1, 1, 1, -1, 1, 1, -1])
882
+
883
+ TESTS::
884
+
885
+ sage: construction_four_symbol_delta_code_II([1, 1], [1, -1], [1, 1], [1])
886
+ Traceback (most recent call last):
887
+ ...
888
+ AssertionError
889
+ sage: construction_four_symbol_delta_code_II([1, 1], [1, 1], [-1], [1, 1])
890
+ Traceback (most recent call last):
891
+ ...
892
+ AssertionError
893
+ """
894
+
895
+ n = len(Z)
896
+ assert len(X) == n+1 and len(Y) == n+1 and len(W) == n
897
+
898
+ def autocorrelation(seq, j):
899
+ return sum([seq[i]*seq[i+j] for i in range(len(seq)-j)])
900
+
901
+ for j in range(1, n):
902
+ assert sum(autocorrelation(seq, j) for seq in [X, Y, Z, W]) == 0
903
+
904
+ def alternate(seq1, seq2):
905
+ return [seq1[i//2] if i % 2 == 0 else seq2[(i-1)//2] for i in range(len(seq1)+len(seq2))]
906
+
907
+ XaltZ = alternate(X, Z)
908
+ Wneg = [-w for w in W]
909
+ Yneg = [-y for y in Y]
910
+
911
+ T1 = XaltZ + alternate(Y, W) + [1]
912
+ T2 = XaltZ + alternate(Y, Wneg) + [-1]
913
+ T3 = XaltZ + alternate(Yneg, Wneg) + [1]
914
+ T4 = XaltZ + alternate(Yneg, W) + [-1]
915
+ return T1, T2, T3, T4
916
+
917
+
918
+ def four_symbol_delta_code_smallcases(n, existence=False):
919
+ r"""
920
+ Return the 4-symobl `\delta` code of length ``n`` if available.
921
+
922
+ The 4-symbol `\delta` codes are constructed using :func:`construction_four_symbol_delta_code_I`
923
+ or :func:`construction_four_symbol_delta_code_II`.
924
+ The base sequences used are taken from [Tur1974]_.
925
+
926
+ INPUT:
927
+
928
+ - ``n`` -- integer; the length of the desired 4-symbol `\delta` code
929
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only check if
930
+ the sequences are available
931
+
932
+ EXAMPLES::
933
+
934
+ sage: from sage.combinat.matrices.hadamard_matrix import four_symbol_delta_code_smallcases
935
+ sage: four_symbol_delta_code_smallcases(3)
936
+ ([1, -1, 1], [1, -1, -1], [1, 1, 1], [1, 1, -1])
937
+ sage: four_symbol_delta_code_smallcases(3, existence=True)
938
+ True
939
+
940
+ TESTS::
941
+
942
+ sage: four_symbol_delta_code_smallcases(17)
943
+ Traceback (most recent call last):
944
+ ...
945
+ ValueError: The four-symbol delta code of length 17 have not yet been implemented
946
+ sage: four_symbol_delta_code_smallcases(17, existence=True)
947
+ False
948
+ """
949
+ db = {
950
+ 1: ([1, -1], [1, 1], [1], [1]),
951
+ 14: ([1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1],
952
+ [1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1],
953
+ [1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1],
954
+ [1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1])
955
+ }
956
+
957
+ T1, T2, T3, T4 = None, None, None, None
958
+ if n % 2 == 1 and (n-1)//2 in db:
959
+ if existence:
960
+ return True
961
+ X, Y, Z, W = db[(n-1)//2]
962
+ T1, T2, T3, T4 = construction_four_symbol_delta_code_I(X, Y, Z, W)
963
+ elif n % 4 == 3 and (n-3) // 4 in db:
964
+ if existence:
965
+ return True
966
+ X, Y, Z, W = db[(n-3)//4]
967
+ T1, T2, T3, T4 = construction_four_symbol_delta_code_II(X, Y, Z, W)
968
+
969
+ if existence:
970
+ return False
971
+
972
+ if T1 is None:
973
+ raise ValueError("The four-symbol delta code of length %s have not yet been implemented" % n)
974
+
975
+ return T1, T2, T3, T4
976
+
977
+
978
+ def _construction_goethals_seidel_matrix(A, B, C, D):
979
+ r"""
980
+ Construct the Goethals Seidel matrix.
981
+
982
+ The matrix is described in [GS70s]_. Given matrices `A`, `B`, `C`, `D`
983
+ the construction is:
984
+
985
+ .. MATH::
986
+
987
+ \left(\begin{array}{rrrr}
988
+ A & BR & CR & DR \\
989
+ -BR & A & -D\top R & C\top R \\
990
+ -CR & D\top R & A & -B\top R \\
991
+ -DR & -C\top R & B\top R & A
992
+ \end{array}\right)
993
+
994
+ Where `R` is the anti-diagonal matrix with all nonzero entries
995
+ equal to one.
996
+
997
+ INPUT:
998
+
999
+ - ``A`` -- the first matrix used in the construction
1000
+ - ``B`` -- the second matrix used in the construction
1001
+ - ``C`` -- the third matrix used in the construction
1002
+ - ``D`` -- the fourth matrix used in the construction
1003
+
1004
+ TESTS::
1005
+
1006
+ sage: from sage.combinat.matrices.hadamard_matrix import _construction_goethals_seidel_matrix
1007
+ sage: A = matrix([[1, -1], [1, 1]])
1008
+ sage: B = matrix([[-1, -1], [-1, 1]])
1009
+ sage: C = matrix([[1, 1], [1, -1]])
1010
+ sage: D = matrix([[-1, 1], [-1, 1]])
1011
+ sage: _construction_goethals_seidel_matrix(A, B, C, D)
1012
+ [ 1 -1|-1 -1| 1 1| 1 -1]
1013
+ [ 1 1| 1 -1|-1 1| 1 -1]
1014
+ [-----+-----+-----+-----]
1015
+ [ 1 1| 1 -1| 1 1| 1 1]
1016
+ [-1 1| 1 1|-1 -1|-1 1]
1017
+ [-----+-----+-----+-----]
1018
+ [-1 -1|-1 -1| 1 -1| 1 1]
1019
+ [ 1 -1| 1 1| 1 1|-1 1]
1020
+ [-----+-----+-----+-----]
1021
+ [-1 1|-1 -1|-1 -1| 1 -1]
1022
+ [-1 1| 1 -1| 1 -1| 1 1]
1023
+ """
1024
+ n = len(A[0])
1025
+ R = matrix(ZZ, n, n, lambda i, j: 1 if i+j == n-1 else 0)
1026
+ return block_matrix([[ A, B*R, C*R, D*R],
1027
+ [-B*R, A, -D.T*R, C.T*R],
1028
+ [-C*R, D.T*R, A, -B.T*R],
1029
+ [-D*R, -C.T*R, B.T*R, A]])
1030
+
1031
+
1032
+ def hadamard_matrix_from_sds(n, existence=False, check=True):
1033
+ r"""
1034
+ Construction of Hadamard matrices from supplementary difference sets.
1035
+
1036
+ Given four SDS with parameters `4-\{n; n_1, n_2, n_3, n_4; \lambda\}` with
1037
+ `n_1 + n_2 + n_3 + n_4 = n+\lambda` we can construct four (-1,+1) sequences `a_i = (a_{i,0},...,a_{i,n-1})`
1038
+ where `a_{i,j} = -1` iff `j \in S_i`. These will be the fist rows of four circulant
1039
+ matrices `A_1, A_2, A_3, A_4` which, when plugged into the Goethals-Seidel array, create an
1040
+ Hadamard matrix of order `4n` (see [Djo1994b]_).
1041
+
1042
+ The supplementary difference sets are taken from
1043
+ :func:`sage.combinat.designs.difference_family.supplementary_difference_set`.
1044
+
1045
+ INPUT:
1046
+
1047
+ - ``n`` -- integer; the order of the matrix to be constructed
1048
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the matrix
1049
+ is a Hadamard before returning
1050
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only check if the
1051
+ matrix exists
1052
+
1053
+ OUTPUT:
1054
+
1055
+ If ``existence=False``, returns the Hadamard matrix of order `n`. It raises
1056
+ an error if no data is available to construct the matrix of the given order,
1057
+ or if `n` is not a multiple of `4`.
1058
+ If ``existence=True``, returns a boolean representing whether the matrix
1059
+ can be constructed or not.
1060
+
1061
+ EXAMPLES:
1062
+
1063
+ By default The function returns the Hadamard matrix ::
1064
+
1065
+ sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_from_sds
1066
+ sage: hadamard_matrix_from_sds(148)
1067
+ 148 x 148 dense matrix over Integer Ring...
1068
+
1069
+ If ``existence`` is set to True, the function returns a boolean ::
1070
+
1071
+ sage: hadamard_matrix_from_sds(764, existence=True)
1072
+ True
1073
+
1074
+ TESTS::
1075
+
1076
+ sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_from_sds, is_hadamard_matrix
1077
+ sage: is_hadamard_matrix(hadamard_matrix_from_sds(172))
1078
+ True
1079
+ sage: hadamard_matrix_from_sds(64, existence=True)
1080
+ False
1081
+ sage: hadamard_matrix_from_sds(64)
1082
+ Traceback (most recent call last):
1083
+ ...
1084
+ ValueError: SDS of order 16 not yet implemented.
1085
+ sage: hadamard_matrix_from_sds(14)
1086
+ Traceback (most recent call last):
1087
+ ...
1088
+ ValueError: n must be a positive multiple of four
1089
+ """
1090
+ from sage.combinat.designs.difference_family import supplementary_difference_set_hadamard
1091
+
1092
+ if n <= 0 or n % 4 != 0:
1093
+ raise ValueError('n must be a positive multiple of four')
1094
+ t = n // 4
1095
+
1096
+ if existence:
1097
+ return supplementary_difference_set_hadamard(t, existence=True)
1098
+
1099
+ _, [S1, S2, S3, S4] = supplementary_difference_set_hadamard(t, check=False)
1100
+ a = [-1 if i in S1 else 1 for i in range(t)]
1101
+ b = [-1 if i in S2 else 1 for i in range(t)]
1102
+ c = [-1 if i in S3 else 1 for i in range(t)]
1103
+ d = [-1 if i in S4 else 1 for i in range(t)]
1104
+
1105
+ if n == 956:
1106
+ a, b, c, d = [-el for el in d], a, b, c
1107
+
1108
+ A, B, C, D = map(matrix.circulant, [a, b, c, d])
1109
+ if check:
1110
+ assert A*A.T+B*B.T+C*C.T+D*D.T == 4*t*I(t)
1111
+
1112
+ H = _construction_goethals_seidel_matrix(A, B, C, D)
1113
+ if check:
1114
+ assert is_hadamard_matrix(H)
1115
+ return H
1116
+
1117
+
1118
+ def hadamard_matrix_cooper_wallis_construction(x1, x2, x3, x4, A, B, C, D, check=True):
1119
+ r"""
1120
+ Create a Hadamard matrix using the construction detailed in [CW1972]_.
1121
+
1122
+ Given four circulant matrices `X_1`, X_2, X_3, X_4` of order `n` with entries (0, 1, -1)
1123
+ such that the entrywise product of two distinct matrices is always equal to `0` and that
1124
+ `\sum_{i=1}^{4}X_iX_i^\top = nI_n` holds, and four matrices `A, B, C, D` of order `m` with
1125
+ elements (1, -1) such that `MN^\top = NM^\top` for all distinct `M`, `N` and
1126
+ `AA^\top + BB^\top + CC^\top + DD^\top = 4mI_n` holds, we construct a Hadamard matrix
1127
+ of order `4nm`.
1128
+
1129
+ INPUT:
1130
+
1131
+ - ``x1`` -- list or vector; the first row of the circulant matrix `X_1`
1132
+ - ``x2`` -- list or vector; the first row of the circulant matrix `X_2`
1133
+ - ``x3`` -- list or vector; the first row of the circulant matrix `X_3`
1134
+ - ``x4`` -- list or vector; the first row of the circulant matrix `X_4`
1135
+ - ``A`` -- the matrix described above
1136
+ - ``B`` -- the matrix described above
1137
+ - ``C`` -- the matrix described above
1138
+ - ``D`` -- the matrix described above
1139
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the resulting
1140
+ matrix is Hadamard before returning it
1141
+
1142
+ EXAMPLES::
1143
+
1144
+ sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_cooper_wallis_construction
1145
+ sage: from sage.combinat.t_sequences import T_sequences_smallcases
1146
+ sage: seqs = T_sequences_smallcases(19)
1147
+ sage: hadamard_matrix_cooper_wallis_construction(seqs[0], seqs[1], seqs[2], seqs[3], matrix([1]), matrix([1]), matrix([1]), matrix([1]))
1148
+ 76 x 76 dense matrix over Integer Ring...
1149
+
1150
+ TESTS::
1151
+
1152
+ sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_cooper_wallis_construction, is_hadamard_matrix
1153
+ sage: seqs = T_sequences_smallcases(13)
1154
+ sage: H = hadamard_matrix_cooper_wallis_construction(seqs[0], seqs[1], seqs[2], seqs[3], matrix([1]), matrix([1]), matrix([1]), matrix([1]))
1155
+ sage: is_hadamard_matrix(H)
1156
+ True
1157
+ sage: len(H[0]) == 13*4*1
1158
+ True
1159
+ sage: hadamard_matrix_cooper_wallis_construction(seqs[0], seqs[1], seqs[2], seqs[3], matrix([1]), matrix([1, -1]), matrix([1]), matrix([1]))
1160
+ Traceback (most recent call last):
1161
+ ...
1162
+ AssertionError
1163
+ sage: hadamard_matrix_cooper_wallis_construction([1,-1], [1, 1], [1,1], [1,1], matrix([1]), matrix([1]), matrix([1]), matrix([1]))
1164
+ Traceback (most recent call last):
1165
+ ...
1166
+ AssertionError
1167
+ """
1168
+
1169
+ n = len(x1)
1170
+ assert n == len(x2) == len(x3) == len(x4)
1171
+
1172
+ X1, X2, X3, X4 = map(matrix.circulant, [x1, x2, x3, x4])
1173
+
1174
+ matrices = [X1, X2, X3, X4]
1175
+ for i in range(4):
1176
+ for j in range(i+1, 4):
1177
+ assert matrices[i].elementwise_product(matrices[j]) == zero_matrix(n)
1178
+ assert X1*X1.T + X2*X2.T + X3*X3.T + X4*X4.T == n*I(n)
1179
+
1180
+ m = len(A[0])
1181
+ assert m == len(B[0]) == len(C[0]) == len(D[0])
1182
+ will_matrices = [A, B, C, D]
1183
+ for i in range(4):
1184
+ for j in range(i+1, 4):
1185
+ assert will_matrices[i]*will_matrices[j].T == will_matrices[j]*will_matrices[i].T
1186
+ assert A*A.T + B*B.T + C*C.T + D*D.T == 4*m*I(m)
1187
+
1188
+ e1 = _construction_goethals_seidel_matrix(X1, X2, X3, X4)
1189
+ e2 = _construction_goethals_seidel_matrix(X2, -X1, X4, -X3)
1190
+ e3 = _construction_goethals_seidel_matrix(X3, -X4, -X1, X2)
1191
+ e4 = _construction_goethals_seidel_matrix(X4, X3, -X2, -X1)
1192
+
1193
+ H = e1.tensor_product(A) + e2.tensor_product(B) + e3.tensor_product(C) + e4.tensor_product(D)
1194
+ if check:
1195
+ assert is_hadamard_matrix(H)
1196
+ return H
1197
+
1198
+
1199
+ def hadamard_matrix_cooper_wallis_smallcases(n, check=True, existence=False):
1200
+ r"""
1201
+ Construct Hadamard matrices using the Cooper-Wallis construction for some small values of `n`.
1202
+
1203
+ This function calls the function :func:`hadamard_matrix_cooper_wallis_construction`
1204
+ with the appropriate arguments.
1205
+ It constructs the matrices `X_1`, `X_2`, `X_3`, `X_4` using either
1206
+ T-matrices or the T-sequences from :func:`sage.combinat.t_sequences.T_sequences_smallcases`.
1207
+ The matrices `A`, `B`, `C`, `D` are taken from :func:`williamson_type_quadruples_smallcases`.
1208
+
1209
+ Data for T-matrices of order 67 is taken from [Saw1985]_.
1210
+
1211
+ INPUT:
1212
+
1213
+ - ``n`` -- integer; the order of the matrix to be constructed
1214
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the matrix
1215
+ is a Hadamard matrix before returning
1216
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only check if
1217
+ the matrix exists
1218
+
1219
+ OUTPUT:
1220
+
1221
+ If ``existence=False``, returns the Hadamard matrix of order `n`. It raises an error if no data
1222
+ is available to construct the matrix of the given order.
1223
+ If ``existence=True``, returns a boolean representing whether the matrix can be constructed or not.
1224
+
1225
+ .. SEEALSO::
1226
+
1227
+ :func:`hadamard_matrix_cooper_wallis_construction`
1228
+
1229
+ EXAMPLES:
1230
+
1231
+ By default The function returns the Hadamard matrix ::
1232
+
1233
+ sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_cooper_wallis_smallcases
1234
+ sage: hadamard_matrix_cooper_wallis_smallcases(28)
1235
+ 28 x 28 dense matrix over Integer Ring...
1236
+
1237
+ If ``existence`` is set to True, the function returns a boolean ::
1238
+
1239
+ sage: hadamard_matrix_cooper_wallis_smallcases(20, existence=True)
1240
+ True
1241
+
1242
+ TESTS::
1243
+
1244
+ sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_cooper_wallis_smallcases, is_hadamard_matrix
1245
+ sage: is_hadamard_matrix(hadamard_matrix_cooper_wallis_smallcases(188))
1246
+ True
1247
+ sage: hadamard_matrix_cooper_wallis_smallcases(64, existence=True)
1248
+ False
1249
+ sage: hadamard_matrix_cooper_wallis_smallcases(64)
1250
+ Traceback (most recent call last):
1251
+ ...
1252
+ ValueError: The Cooper-Wallis construction for Hadamard matrices of order 64 is not yet implemented.
1253
+ sage: hadamard_matrix_cooper_wallis_smallcases(14)
1254
+ Traceback (most recent call last):
1255
+ ...
1256
+ AssertionError
1257
+ """
1258
+ assert n % 4 == 0 and n > 0
1259
+
1260
+ db = {
1261
+ 67: (
1262
+ [1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, -1, -1, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, -1, 0, -1, 0, 1, -1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
1263
+ [0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 1, 1, 0, 0, -1, -1, -1, 0, 0, 0, 0, 0, -1, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0],
1264
+ [0, 0, 0, 0, 0, -1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, -1, 0, 0, 1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, -1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 1, -1, -1, 0, 1, 0, 0, 0, 0, 0, 0],
1265
+ [0, 0, -1, -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1, 1, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, -1, 1, 1, 0, 0, 0]
1266
+ )
1267
+ }
1268
+
1269
+ for T_seq_len in divisors(n//4):
1270
+ will_size = n // (4*T_seq_len)
1271
+ if (T_seq_len in db or T_sequences_smallcases(T_seq_len, existence=True)) and williamson_type_quadruples_smallcases(will_size, existence=True):
1272
+ if existence:
1273
+ return True
1274
+
1275
+ e1, e2, e3, e4 = None, None, None, None
1276
+ if T_seq_len in db:
1277
+ e1, e2, e3, e4 = db[T_seq_len]
1278
+ else:
1279
+ e1, e2, e3, e4 = T_sequences_smallcases(T_seq_len, check=False)
1280
+
1281
+ will_matrices = williamson_type_quadruples_smallcases(will_size)
1282
+ A, B, C, D = map(matrix.circulant, will_matrices)
1283
+ M = hadamard_matrix_cooper_wallis_construction(e1, e2, e3, e4, A, B, C, D, check=False)
1284
+
1285
+ if check:
1286
+ assert is_hadamard_matrix(M)
1287
+ return M
1288
+
1289
+ if existence:
1290
+ return False
1291
+ raise ValueError("The Cooper-Wallis construction for Hadamard matrices of order %s is not yet implemented." % n)
1292
+
1293
+
1294
+ def _get_baumert_hall_units(n, existence=False):
1295
+ r"""
1296
+ Construct Baumert-Hall units of size `n` from available 4-symbol `\delta` codes.
1297
+
1298
+ The construction is detailed in Theroem 2 from [Tur1974]_, and is based on the
1299
+ Goethals-Seidel construction of Hadamard matrices.
1300
+ We need a 4-symbol `\delta` code to detail the first row of circulant matrices M1, M2, M3, M4
1301
+ used in the construction.
1302
+
1303
+ INPUT:
1304
+
1305
+ - ``n`` -- integer; the size of the Baumert-Hall units
1306
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only check whether
1307
+ the units can be constructed
1308
+
1309
+ OUTPUT:
1310
+
1311
+ If ``existence=True``, return a boolean representing whether the Baumert-Hall
1312
+ units can be constructed. Otherwise, return a tuple containing the four
1313
+ Baumert-Hall units.
1314
+
1315
+ EXAMPLES::
1316
+
1317
+ sage: from sage.combinat.matrices.hadamard_matrix import _get_baumert_hall_units
1318
+ sage: _get_baumert_hall_units(28)
1319
+ (28 x 28 dense matrix over Integer Ring,
1320
+ 28 x 28 dense matrix over Integer Ring,
1321
+ 28 x 28 dense matrix over Integer Ring,
1322
+ 28 x 28 dense matrix over Integer Ring)
1323
+
1324
+ TESTS::
1325
+
1326
+ sage: _get_baumert_hall_units(116, existence=True)
1327
+ True
1328
+ sage: _get_baumert_hall_units(200, existence=True)
1329
+ False
1330
+ sage: _get_baumert_hall_units(15)
1331
+ Traceback (most recent call last):
1332
+ ...
1333
+ AssertionError
1334
+ sage: _get_baumert_hall_units(200)
1335
+ Traceback (most recent call last):
1336
+ ...
1337
+ ValueError: The Baumert-Hall units of size 200 have not yet been implemented
1338
+ """
1339
+ assert n % 4 == 0 and n > 0
1340
+
1341
+ delta_codes_len = n//4
1342
+ if not four_symbol_delta_code_smallcases(delta_codes_len, existence=True):
1343
+ if existence:
1344
+ return False
1345
+ raise ValueError("The Baumert-Hall units of size %s have not yet been implemented" % n)
1346
+
1347
+ if existence:
1348
+ return True
1349
+
1350
+ T1, T2, T3, T4 = four_symbol_delta_code_smallcases(delta_codes_len)
1351
+ M1 = matrix.circulant(T1)
1352
+ M2 = matrix.circulant(T2)
1353
+ M3 = matrix.circulant(T3)
1354
+ M4 = matrix.circulant(T4)
1355
+
1356
+ M1hat = matrix(ZZ, 0.25*(M1+M2+M3+M4))
1357
+ M2hat = matrix(ZZ, 0.25*(M1-M2-M3+M4))
1358
+ M3hat = matrix(ZZ, 0.25*(M1+M2-M3-M4))
1359
+ M4hat = matrix(ZZ, 0.25*(M1-M2+M3-M4))
1360
+
1361
+ e1 = _construction_goethals_seidel_matrix(M1hat, -M2hat, -M3hat, -M4hat)
1362
+ e2 = _construction_goethals_seidel_matrix(M2hat, M1hat, M4hat, -M3hat)
1363
+ e3 = _construction_goethals_seidel_matrix(M3hat, -M4hat, M1hat, M2hat)
1364
+ e4 = _construction_goethals_seidel_matrix(M4hat, M3hat, -M2hat, M1hat)
1365
+ return e1, e2, e3, e4
1366
+
1367
+
1368
+ def hadamard_matrix_turyn_type(a, b, c, d, e1, e2, e3, e4, check=True):
1369
+ r"""
1370
+ Construction of Turyn type Hadamard matrix.
1371
+
1372
+ Given `n\times n` circulant matrices `A`, `B`, `C`, `D` with 1,-1 entries,
1373
+ satisfying `AA^\top + BB^\top + CC^\top + DD^\top = 4nI`, and a set of
1374
+ Baumert-Hall units of order `4t`, one can construct a Hadamard matrix of order
1375
+ `4tn` as detailed by Turyn in [Tur1974]_.
1376
+
1377
+ INPUT:
1378
+
1379
+ - ``a`` -- 1,-1 list; the 1st row of `A`
1380
+ - ``b`` -- 1,-1 list; the 1st row of `B`
1381
+ - ``d`` -- 1,-1 list; the 1st row of `C`
1382
+ - ``c`` -- 1,-1 list; the 1st row of `D`
1383
+ - ``e1`` -- matrix; the first Baumert-Hall unit
1384
+ - ``e2`` -- matrix; the second Baumert-Hall unit
1385
+ - ``e3`` -- matrix; the third Baumert-Hall unit
1386
+ - ``e4`` -- matrix; the fourth Baumert-Hall unit
1387
+ - ``check`` -- boolean (default: ``True``); whether to check that the output
1388
+ is a Hadamard matrix before returning it
1389
+
1390
+ EXAMPLES::
1391
+
1392
+ sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_turyn_type, _get_baumert_hall_units
1393
+ sage: A, B, C, D = _get_baumert_hall_units(28)
1394
+ sage: hadamard_matrix_turyn_type([1], [1], [1], [1], A, B, C, D)
1395
+ 28 x 28 dense matrix over Integer Ring...
1396
+
1397
+ TESTS::
1398
+
1399
+ sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_turyn_type, _get_baumert_hall_units, is_hadamard_matrix
1400
+ sage: A, B, C, D = _get_baumert_hall_units(12)
1401
+ sage: is_hadamard_matrix(hadamard_matrix_turyn_type([1], [1], [1], [1], A, B, C, D))
1402
+ True
1403
+ sage: hadamard_matrix_turyn_type([1, -1], [1], [1], [1], A, B, C, D)
1404
+ Traceback (most recent call last):
1405
+ ...
1406
+ AssertionError
1407
+ sage: hadamard_matrix_turyn_type([1, -1], [1, 1], [1, 1], [1, 1], A, B, C, D)
1408
+ Traceback (most recent call last):
1409
+ ...
1410
+ AssertionError
1411
+ """
1412
+ A, B, C, D = map(matrix.circulant, [a, b, c, d])
1413
+
1414
+ n = len(a)
1415
+ assert len(a) == len(b) == len(c) == len(d)
1416
+ assert A*A.T+B*B.T+C*C.T+D*D.T == 4*n*I(n)
1417
+
1418
+ t4 = len(e1[0])
1419
+ assert t4 % 4 == 0
1420
+ t = t4 // 4
1421
+
1422
+ # Check that e1, e2, e3, e4 are valid Baumert-Hall units
1423
+ for i in range(t4):
1424
+ for j in range(t4):
1425
+ assert abs(e1[i, j]) + abs(e2[i, j]) + abs(e3[i, j]) + abs(e4[i, j]) == 1
1426
+
1427
+ assert e1*e1.T == t*I(t4) and e2*e2.T == t*I(t4) and e3*e3.T == t*I(t4) and e4*e4.T == t*I(t4)
1428
+
1429
+ units = [e1, e2, e3, e4]
1430
+ for i in range(len(units)):
1431
+ for j in range(i+1, len(units)):
1432
+ assert units[i]*units[j].T + units[j]*units[i].T == 0*I(t4)
1433
+
1434
+ H = e1.tensor_product(A) + e2.tensor_product(B) + e3.tensor_product(C) + e4.tensor_product(D)
1435
+ if check:
1436
+ assert is_hadamard_matrix(H)
1437
+ return H
1438
+
1439
+
1440
+ def turyn_type_hadamard_matrix_smallcases(n, existence=False, check=True):
1441
+ r"""
1442
+ Construct a Hadamard matrix of order `n` from available 4-symbol `\delta` codes and Williamson quadruples.
1443
+
1444
+ The function looks for Baumert-Hall units and Williamson type matrices from
1445
+ :func:`four_symbol_delta_code_smallcases` and :func:`williamson_type_quadruples_smallcases`
1446
+ and use them to construct a Hadamard matrix with the Turyn construction
1447
+ defined in :func:`hadamard_matrix_turyn_type`.
1448
+
1449
+ INPUT:
1450
+
1451
+ - ``n`` -- integer; the order of the matrix to be constructed
1452
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only check if
1453
+ the matrix exists
1454
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the matrix
1455
+ is a Hadamard matrix before returning
1456
+
1457
+ EXAMPLES::
1458
+
1459
+ sage: from sage.combinat.matrices.hadamard_matrix import turyn_type_hadamard_matrix_smallcases
1460
+ sage: turyn_type_hadamard_matrix_smallcases(28, existence=True)
1461
+ True
1462
+ sage: turyn_type_hadamard_matrix_smallcases(28)
1463
+ 28 x 28 dense matrix over Integer Ring...
1464
+
1465
+ TESTS::
1466
+
1467
+ sage: from sage.combinat.matrices.hadamard_matrix import turyn_type_hadamard_matrix_smallcases, is_hadamard_matrix
1468
+ sage: is_hadamard_matrix(turyn_type_hadamard_matrix_smallcases(236)) # long time
1469
+ True
1470
+ sage: turyn_type_hadamard_matrix_smallcases(64, existence=True)
1471
+ False
1472
+ sage: turyn_type_hadamard_matrix_smallcases(64)
1473
+ Traceback (most recent call last):
1474
+ ...
1475
+ ValueError: The Turyn type construction for Hadamard matrices of order 64 is not yet implemented.
1476
+ """
1477
+ assert n % 4 == 0 and n > 0
1478
+
1479
+ for delta_code_len in divisors(n//4):
1480
+ units_size = delta_code_len*4
1481
+ will_size = n//units_size
1482
+ if _get_baumert_hall_units(units_size, existence=True) and williamson_type_quadruples_smallcases(will_size, existence=True):
1483
+ if existence:
1484
+ return True
1485
+
1486
+ e1, e2, e3, e4 = _get_baumert_hall_units(units_size)
1487
+ a, b, c, d = williamson_type_quadruples_smallcases(will_size)
1488
+ return hadamard_matrix_turyn_type(a, b, c, d, e1, e2, e3, e4, check=check)
1489
+
1490
+ if existence:
1491
+ return False
1492
+ raise ValueError("The Turyn type construction for Hadamard matrices of order %s is not yet implemented." % n)
1493
+
1494
+
1495
+ def hadamard_matrix_spence_construction(n, existence=False, check=True):
1496
+ r"""
1497
+ Create a Hadamard matrix of order `n` using the Spence construction.
1498
+
1499
+ This construction (detailed in [Spe1975]_), uses supplementary difference sets implemented in
1500
+ :func:`sage.combinat.designs.difference_family.supplementary_difference_set_from_rel_diff_set` to create the
1501
+ desired matrix.
1502
+
1503
+ INPUT:
1504
+
1505
+ - ``n`` -- integer; the order of the matrix to be constructed
1506
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only check if
1507
+ the matrix exists
1508
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the matrix
1509
+ is a Hadamard matrix before returning
1510
+
1511
+ OUTPUT:
1512
+
1513
+ If ``existence=True``, returns a boolean representing whether the Hadamard
1514
+ matrix can be constructed. Otherwise, returns the Hadamard matrix, or raises
1515
+ an error if it cannot be constructed.
1516
+
1517
+ EXAMPLES::
1518
+
1519
+ sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_spence_construction
1520
+ sage: hadamard_matrix_spence_construction(36)
1521
+ 36 x 36 dense matrix over Integer Ring...
1522
+
1523
+ If ``existence`` is ``True``, the function returns a boolean ::
1524
+
1525
+ sage: hadamard_matrix_spence_construction(52, existence=True)
1526
+ True
1527
+
1528
+ TESTS::
1529
+
1530
+ sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix
1531
+ sage: is_hadamard_matrix(hadamard_matrix_spence_construction(100))
1532
+ True
1533
+ sage: hadamard_matrix_spence_construction(48, existence=True)
1534
+ False
1535
+ sage: hadamard_matrix_spence_construction(48)
1536
+ Traceback (most recent call last):
1537
+ ...
1538
+ ValueError: The order 48 is not covered by Spence construction.
1539
+ sage: hadamard_matrix_spence_construction(5)
1540
+ Traceback (most recent call last):
1541
+ ...
1542
+ AssertionError
1543
+ sage: hadamard_matrix_spence_construction(0)
1544
+ Traceback (most recent call last):
1545
+ ...
1546
+ AssertionError
1547
+ """
1548
+ from sage.combinat.designs.difference_family import supplementary_difference_set_from_rel_diff_set
1549
+
1550
+ assert n % 4 == 0 and n > 0
1551
+
1552
+ q = n//4
1553
+
1554
+ if existence:
1555
+ return supplementary_difference_set_from_rel_diff_set(q, existence=True)
1556
+
1557
+ if not supplementary_difference_set_from_rel_diff_set(q, existence=True):
1558
+ raise ValueError(f'The order {n} is not covered by Spence construction.')
1559
+
1560
+ G, [S1, S2, S3, S4] = supplementary_difference_set_from_rel_diff_set(q, check=False)
1561
+
1562
+ Glist = list(G)
1563
+ A1 = matrix.circulant([1 if j in S1 else -1 for j in Glist])
1564
+ A2 = matrix.circulant([1 if j in S4 else -1 for j in Glist])
1565
+ A3 = matrix.circulant([1 if j in S3 else -1 for j in Glist])
1566
+ A4 = matrix.circulant([1 if j in S2 else -1 for j in Glist])
1567
+
1568
+ P = matrix(ZZ, [[1 if (i + j) % (q-1) == 0 else 0 for i in range(1, q)] for j in range(1, q)])
1569
+
1570
+ e = matrix([1]*(q-1))
1571
+ m1 = matrix([-1])
1572
+ p1 = matrix([1])
1573
+ H = block_matrix([[ p1, m1, p1, p1, e, e, e, e],
1574
+ [ p1, p1, m1, p1, -e, e, -e, e],
1575
+ [ m1, p1, p1, p1, -e, e, e, -e],
1576
+ [ m1, m1, m1, p1, -e, -e, e, e],
1577
+ [-e.T, e.T, e.T, -e.T, A1, A2*P, A3*P, A4*P],
1578
+ [-e.T, -e.T, e.T, e.T, -A2*P, A1, -A4.T*P, A3.T*P],
1579
+ [-e.T, -e.T, -e.T, -e.T, -A3*P, A4.T*P, A1, -A2.T*P],
1580
+ [ e.T, -e.T, e.T, -e.T, -A4*P, -A3.T*P, A2.T*P, A1]])
1581
+ if check:
1582
+ assert is_hadamard_matrix(H, verbose=True)
1583
+
1584
+ return H
1585
+
1586
+
1587
+ def is_hadamard_matrix(M, normalized=False, skew=False, verbose=False):
1588
+ r"""
1589
+ Test if ``M`` is a Hadamard matrix.
1590
+
1591
+ INPUT:
1592
+
1593
+ - ``M`` -- a matrix
1594
+ - ``normalized`` -- boolean (default: ``False``); whether to test if ``M``
1595
+ is a normalized Hadamard matrix, i.e. has its first row/column filled with +1
1596
+ - ``skew`` -- boolean (default: ``False``); whether to test if ``M`` is a skew
1597
+ Hadamard matrix, i.e. `M=S+I` for `-S=S^\top`, and `I` the identity matrix
1598
+ - ``verbose`` -- boolean (default: ``False``); whether to be verbose when
1599
+ the matrix is not Hadamard
1600
+
1601
+ EXAMPLES::
1602
+
1603
+ sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix
1604
+ sage: h = matrix.hadamard(12)
1605
+ sage: is_hadamard_matrix(h)
1606
+ True
1607
+ sage: from sage.combinat.matrices.hadamard_matrix import skew_hadamard_matrix
1608
+ sage: h = skew_hadamard_matrix(12)
1609
+ sage: is_hadamard_matrix(h, skew=True)
1610
+ True
1611
+ sage: h = matrix.hadamard(12)
1612
+ sage: h[0,0] = 2
1613
+ sage: is_hadamard_matrix(h,verbose=True)
1614
+ The matrix does not only contain +1 and -1 entries, e.g. 2
1615
+ False
1616
+ sage: h = matrix.hadamard(12)
1617
+ sage: for i in range(12):
1618
+ ....: h[i,2] = -h[i,2]
1619
+ sage: is_hadamard_matrix(h,verbose=True,normalized=True)
1620
+ The matrix is not normalized
1621
+ False
1622
+
1623
+ TESTS::
1624
+
1625
+ sage: h = matrix.hadamard(12)
1626
+ sage: is_hadamard_matrix(h, skew=True)
1627
+ False
1628
+ sage: is_hadamard_matrix(h, skew=True, verbose=True)
1629
+ The matrix is not skew
1630
+ False
1631
+ sage: h = skew_hadamard_matrix(12)
1632
+ sage: is_hadamard_matrix(h, skew=True, verbose=True)
1633
+ True
1634
+ sage: is_hadamard_matrix(h, skew=False, verbose=True)
1635
+ True
1636
+ sage: h = -h
1637
+ sage: is_hadamard_matrix(h, skew=True, verbose=True)
1638
+ The matrix is not skew - diagonal entries must be all 1
1639
+ False
1640
+ sage: is_hadamard_matrix(h, skew=False, verbose=True)
1641
+ True
1642
+ sage: h = skew_hadamard_matrix(20, skew_normalize=False)
1643
+ sage: is_hadamard_matrix(h, skew=True, normalized=True, verbose=True)
1644
+ The matrix is not skew-normalized
1645
+ False
1646
+ sage: from sage.combinat.matrices.hadamard_matrix import normalise_hadamard
1647
+ sage: h = normalise_hadamard(h, skew=True)
1648
+ sage: is_hadamard_matrix(h, skew=True, normalized=True, verbose=True)
1649
+ True
1650
+ """
1651
+ n = M.ncols()
1652
+ if n != M.nrows():
1653
+ if verbose:
1654
+ print("The matrix is not square ({}x{})".format(M.nrows(), n))
1655
+ return False
1656
+
1657
+ if n == 0:
1658
+ return True
1659
+
1660
+ for r in M:
1661
+ for v in r:
1662
+ if v*v != 1:
1663
+ if verbose:
1664
+ print("The matrix does not only contain +1 and -1 entries, e.g. " + str(v))
1665
+ return False
1666
+
1667
+ prod = (M*M.transpose()).dict()
1668
+ if (len(prod) != n or set(prod.values()) != {n} or any((i, i) not in prod for i in range(n))):
1669
+ if verbose:
1670
+ print("The product M*M.transpose() is not equal to nI")
1671
+ return False
1672
+
1673
+ if normalized:
1674
+ if skew and (set(M.row(0)) != {1}):
1675
+ if verbose:
1676
+ print("The matrix is not skew-normalized")
1677
+ return False
1678
+ elif not skew and (set(M.row(0)) != {1} or set(M.column(0)) != {1}):
1679
+ if verbose:
1680
+ print("The matrix is not normalized")
1681
+ return False
1682
+
1683
+ if skew:
1684
+ for i in range(n-1):
1685
+ for j in range(i+1, n):
1686
+ if M[i, j] != -M[j, i]:
1687
+ if verbose:
1688
+ print("The matrix is not skew")
1689
+ return False
1690
+ for i in range(n):
1691
+ if M[i, i] != 1:
1692
+ if verbose:
1693
+ print("The matrix is not skew - diagonal entries must be all 1")
1694
+ return False
1695
+ return True
1696
+
1697
+
1698
+ def is_skew_hadamard_matrix(M, normalized=False, verbose=False):
1699
+ r"""
1700
+ Test if ``M`` is a skew Hadamard matrix.
1701
+
1702
+ this is a wrapper around the function :func:`is_hadamard_matrix`
1703
+
1704
+ INPUT:
1705
+
1706
+ - ``M`` -- a matrix
1707
+ - ``normalized`` -- boolean (default: ``False``); whether to test if ``M``
1708
+ is a skew-normalized Hadamard matrix, i.e. has its first row filled with +1
1709
+ - ``verbose`` -- boolean (default: ``False``); whether to be verbose when the
1710
+ matrix is not skew Hadamard
1711
+
1712
+ EXAMPLES::
1713
+
1714
+ sage: from sage.combinat.matrices.hadamard_matrix import is_skew_hadamard_matrix, skew_hadamard_matrix
1715
+ sage: h = matrix.hadamard(12)
1716
+ sage: is_skew_hadamard_matrix(h, verbose=True)
1717
+ The matrix is not skew
1718
+ False
1719
+ sage: h = skew_hadamard_matrix(12)
1720
+ sage: is_skew_hadamard_matrix(h)
1721
+ True
1722
+ sage: from sage.combinat.matrices.hadamard_matrix import normalise_hadamard
1723
+ sage: h = normalise_hadamard(skew_hadamard_matrix(12), skew=True)
1724
+ sage: is_skew_hadamard_matrix(h, verbose=True, normalized=True)
1725
+ True
1726
+ """
1727
+ return is_hadamard_matrix(M, skew=True, normalized=normalized, verbose=verbose)
1728
+
1729
+
1730
+ @matrix_method
1731
+ def hadamard_matrix(n, existence=False, check=True, construction_name=False):
1732
+ r"""
1733
+ Try to construct a Hadamard matrix using the available methods.
1734
+
1735
+ Currently all orders `\le 1200` for which a construction is
1736
+ known are implemented. For `n > 1200`, only some orders are available.
1737
+
1738
+ INPUT:
1739
+
1740
+ - ``n`` -- integer; dimension of the matrix
1741
+ - ``existence`` -- boolean (default: ``False``); whether to build the matrix
1742
+ or merely query if a construction is available in Sage. When set to ``True``,
1743
+ the function returns:
1744
+
1745
+ - ``True`` -- meaning that Sage knows how to build the matrix
1746
+ - ``Unknown`` -- meaning that Sage does not know how to build the
1747
+ matrix, although the matrix may exist (see :mod:`sage.misc.unknown`).
1748
+ - ``False`` -- meaning that the matrix does not exist
1749
+
1750
+ - ``check`` -- boolean (default: ``True``); whether to check that output is
1751
+ correct before returning it. As this is expected to be useless, you may
1752
+ want to disable it whenever you want speed.
1753
+ - ``construction_name`` -- boolean (default: ``False``); if it is ``True``,
1754
+ ``existence`` is ``True``, and a matrix exists, output the construction name.
1755
+ It has no effect if ``existence`` is set to ``False``.
1756
+
1757
+ EXAMPLES::
1758
+
1759
+ sage: hadamard_matrix(12).det()
1760
+ 2985984
1761
+ sage: 12^6
1762
+ 2985984
1763
+ sage: hadamard_matrix(1)
1764
+ [1]
1765
+ sage: hadamard_matrix(2)
1766
+ [ 1 1]
1767
+ [ 1 -1]
1768
+ sage: hadamard_matrix(8) # random
1769
+ [ 1 1 1 1 1 1 1 1]
1770
+ [ 1 -1 1 -1 1 -1 1 -1]
1771
+ [ 1 1 -1 -1 1 1 -1 -1]
1772
+ [ 1 -1 -1 1 1 -1 -1 1]
1773
+ [ 1 1 1 1 -1 -1 -1 -1]
1774
+ [ 1 -1 1 -1 -1 1 -1 1]
1775
+ [ 1 1 -1 -1 -1 -1 1 1]
1776
+ [ 1 -1 -1 1 -1 1 1 -1]
1777
+ sage: hadamard_matrix(8).det() == 8^4
1778
+ True
1779
+
1780
+ We note that :func:`hadamard_matrix` returns a normalised Hadamard matrix
1781
+ (the entries in the first row and column are all +1) ::
1782
+
1783
+ sage: hadamard_matrix(12) # random
1784
+ [ 1 1| 1 1| 1 1| 1 1| 1 1| 1 1]
1785
+ [ 1 -1|-1 1|-1 1|-1 1|-1 1|-1 1]
1786
+ [-----+-----+-----+-----+-----+-----]
1787
+ [ 1 -1| 1 -1| 1 1|-1 -1|-1 -1| 1 1]
1788
+ [ 1 1|-1 -1| 1 -1|-1 1|-1 1| 1 -1]
1789
+ [-----+-----+-----+-----+-----+-----]
1790
+ [ 1 -1| 1 1| 1 -1| 1 1|-1 -1|-1 -1]
1791
+ [ 1 1| 1 -1|-1 -1| 1 -1|-1 1|-1 1]
1792
+ [-----+-----+-----+-----+-----+-----]
1793
+ [ 1 -1|-1 -1| 1 1| 1 -1| 1 1|-1 -1]
1794
+ [ 1 1|-1 1| 1 -1|-1 -1| 1 -1|-1 1]
1795
+ [-----+-----+-----+-----+-----+-----]
1796
+ [ 1 -1|-1 -1|-1 -1| 1 1| 1 -1| 1 1]
1797
+ [ 1 1|-1 1|-1 1| 1 -1|-1 -1| 1 -1]
1798
+ [-----+-----+-----+-----+-----+-----]
1799
+ [ 1 -1| 1 1|-1 -1|-1 -1| 1 1| 1 -1]
1800
+ [ 1 1| 1 -1|-1 1|-1 1| 1 -1|-1 -1]
1801
+
1802
+ To find how the matrix is obtained, use ``construction_name`` ::
1803
+
1804
+ sage: matrix.hadamard(476, existence=True, construction_name=True)
1805
+ 'cooper-wallis small cases: 476'
1806
+
1807
+ TESTS::
1808
+
1809
+ sage: matrix.hadamard(10,existence=True)
1810
+ False
1811
+ sage: matrix.hadamard(12,existence=True)
1812
+ True
1813
+ sage: matrix.hadamard(668,existence=True) # needs database_graphs
1814
+ Unknown
1815
+ sage: matrix.hadamard(10)
1816
+ Traceback (most recent call last):
1817
+ ...
1818
+ ValueError: The Hadamard matrix of order 10 does not exist
1819
+ sage: matrix.hadamard(312, existence=True)
1820
+ True
1821
+ sage: matrix.hadamard(1904, existence=True)
1822
+ True
1823
+ sage: matrix.hadamard(324, existence=True)
1824
+ True
1825
+ """
1826
+ name = str(n)
1827
+ if construction_name:
1828
+ def report_name(nam):
1829
+ return nam
1830
+ else:
1831
+ def report_name(nam):
1832
+ return True
1833
+
1834
+ if not (n % 4 == 0) and (n > 2):
1835
+ if existence:
1836
+ return False
1837
+ raise ValueError("The Hadamard matrix of order %s does not exist" % n)
1838
+ if n == 2:
1839
+ if existence:
1840
+ return report_name(name)
1841
+ M = matrix([[1, 1], [1, -1]])
1842
+ elif n == 1:
1843
+ if existence:
1844
+ return report_name(name)
1845
+ M = matrix([1])
1846
+ elif is_prime_power(n//2 - 1) and (n//2 - 1) % 4 == 1:
1847
+ name = "paleyII " + name
1848
+ if existence:
1849
+ return report_name(name)
1850
+ M = hadamard_matrix_paleyII(n)
1851
+ elif n == 4 or n % 8 == 0 and hadamard_matrix(n//2, existence=True) is True:
1852
+ name = "doubling " + name
1853
+ if existence:
1854
+ return report_name(name)
1855
+ had = hadamard_matrix(n//2, check=False)
1856
+ chad1 = matrix([list(r) + list(r) for r in had.rows()])
1857
+ mhad = (-1) * had
1858
+ R = len(had.rows())
1859
+ chad2 = matrix([list(had.rows()[i]) + list(mhad.rows()[i])
1860
+ for i in range(R)])
1861
+ M = chad1.stack(chad2)
1862
+ elif is_prime_power(n - 1) and (n - 1) % 4 == 3:
1863
+ name = "paleyI " + name
1864
+ if existence:
1865
+ return report_name(name)
1866
+ M = hadamard_matrix_paleyI(n)
1867
+ elif williamson_hadamard_matrix_smallcases(n, existence=True):
1868
+ name = "small cases: " + name
1869
+ if existence:
1870
+ return report_name(name)
1871
+ M = williamson_hadamard_matrix_smallcases(n, check=False)
1872
+ elif n == 156:
1873
+ if existence:
1874
+ return report_name(name)
1875
+ M = hadamard_matrix_156()
1876
+ elif hadamard_matrix_cooper_wallis_smallcases(n, existence=True):
1877
+ name = "cooper-wallis small cases: " + name
1878
+ if existence:
1879
+ return report_name(name)
1880
+ M = hadamard_matrix_cooper_wallis_smallcases(n, check=False)
1881
+ elif turyn_type_hadamard_matrix_smallcases(n, existence=True):
1882
+ name = "turyn type small cases: " + name
1883
+ if existence:
1884
+ return report_name(name)
1885
+ M = turyn_type_hadamard_matrix_smallcases(n, check=False)
1886
+ elif hadamard_matrix_miyamoto_construction(n, existence=True):
1887
+ name = "miyamoto " + name
1888
+ if existence:
1889
+ return report_name(name)
1890
+ M = hadamard_matrix_miyamoto_construction(n, check=False)
1891
+ elif hadamard_matrix_from_sds(n, existence=True):
1892
+ name = "SDS " + name
1893
+ if existence:
1894
+ return report_name(name)
1895
+ M = hadamard_matrix_from_sds(n, check=False)
1896
+ elif hadamard_matrix_spence_construction(n, existence=True):
1897
+ name = "spence " + name
1898
+ if existence:
1899
+ return report_name(name)
1900
+ M = hadamard_matrix_spence_construction(n, check=False)
1901
+ elif skew_hadamard_matrix(n, existence=True) is True:
1902
+ name = "skew " + name
1903
+ if existence:
1904
+ return report_name(name)
1905
+ M = skew_hadamard_matrix(n, check=False)
1906
+ elif regular_symmetric_hadamard_matrix_with_constant_diagonal(n, 1, existence=True) is True:
1907
+ name = "RSHCD " + name
1908
+ if existence:
1909
+ return report_name(name)
1910
+ M = regular_symmetric_hadamard_matrix_with_constant_diagonal(n, 1)
1911
+ elif hadamard_matrix_from_symmetric_conference_matrix(n, existence=True) is True:
1912
+ name = "Construction from symmetric conference matrix " + name
1913
+ if existence:
1914
+ return report_name(name)
1915
+ M = hadamard_matrix_from_symmetric_conference_matrix(n, check=False)
1916
+ else:
1917
+ if existence:
1918
+ return Unknown
1919
+ raise ValueError("The Hadamard matrix of order %s is not yet implemented." % n)
1920
+
1921
+ if check:
1922
+ assert is_hadamard_matrix(M)
1923
+
1924
+ return M
1925
+
1926
+
1927
+ def hadamard_matrix_www(url_file, comments=False):
1928
+ r"""
1929
+ Pull file from Sloane's database and return the corresponding Hadamard
1930
+ matrix as a Sage matrix.
1931
+
1932
+ You must input a filename of the form "had.n.xxx.txt" as described
1933
+ on the webpage http://neilsloane.com/hadamard/, where
1934
+ "xxx" could be empty or a number of some characters.
1935
+
1936
+ If ``comments=True`` then the "Automorphism..." line of the had.n.xxx.txt
1937
+ file is printed if it exists. Otherwise nothing is done.
1938
+
1939
+ EXAMPLES::
1940
+
1941
+ sage: hadamard_matrix_www("had.4.txt") # optional - internet
1942
+ [ 1 1 1 1]
1943
+ [ 1 -1 1 -1]
1944
+ [ 1 1 -1 -1]
1945
+ [ 1 -1 -1 1]
1946
+ sage: hadamard_matrix_www("had.16.2.txt",comments=True) # optional - internet
1947
+ Automorphism group has order = 49152 = 2^14 * 3
1948
+ [ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
1949
+ [ 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1]
1950
+ [ 1 1 -1 -1 1 1 -1 -1 1 1 -1 -1 1 1 -1 -1]
1951
+ [ 1 -1 -1 1 1 -1 -1 1 1 -1 -1 1 1 -1 -1 1]
1952
+ [ 1 1 1 1 -1 -1 -1 -1 1 1 1 1 -1 -1 -1 -1]
1953
+ [ 1 -1 1 -1 -1 1 -1 1 1 -1 1 -1 -1 1 -1 1]
1954
+ [ 1 1 -1 -1 -1 -1 1 1 1 1 -1 -1 -1 -1 1 1]
1955
+ [ 1 -1 -1 1 -1 1 1 -1 1 -1 -1 1 -1 1 1 -1]
1956
+ [ 1 1 1 1 1 1 1 1 -1 -1 -1 -1 -1 -1 -1 -1]
1957
+ [ 1 1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 1 1 1 1]
1958
+ [ 1 1 -1 -1 1 -1 1 -1 -1 -1 1 1 -1 1 -1 1]
1959
+ [ 1 1 -1 -1 -1 1 -1 1 -1 -1 1 1 1 -1 1 -1]
1960
+ [ 1 -1 1 -1 1 -1 -1 1 -1 1 -1 1 -1 1 1 -1]
1961
+ [ 1 -1 1 -1 -1 1 1 -1 -1 1 -1 1 1 -1 -1 1]
1962
+ [ 1 -1 -1 1 1 1 -1 -1 -1 1 1 -1 -1 -1 1 1]
1963
+ [ 1 -1 -1 1 -1 -1 1 1 -1 1 1 -1 1 1 -1 -1]
1964
+ """
1965
+ n = int(url_file.split(".")[1])
1966
+ rws = []
1967
+ url = "http://neilsloane.com/hadamard/" + url_file
1968
+ with urlopen(url) as f:
1969
+ s = [line.decode() for line in f.readlines()]
1970
+ for i in range(n):
1971
+ line = s[i]
1972
+ rws.append([1 if line[j] == "+" else -1 for j in range(n)])
1973
+ if comments:
1974
+ lastline = s[-1]
1975
+ if lastline[0] == "A":
1976
+ print(lastline)
1977
+ return matrix(rws)
1978
+
1979
+
1980
+ _rshcd_cache = {}
1981
+
1982
+
1983
+ def regular_symmetric_hadamard_matrix_with_constant_diagonal(n, e, existence=False):
1984
+ r"""
1985
+ Return a Regular Symmetric Hadamard Matrix with Constant Diagonal.
1986
+
1987
+ A Hadamard matrix is said to be *regular* if its rows all sum to the same
1988
+ value.
1989
+
1990
+ For `\epsilon\in\{-1,+1\}`, we say that `M` is a `(n,\epsilon)-RSHCD` if
1991
+ `M` is a regular symmetric Hadamard matrix with constant diagonal
1992
+ `\delta\in\{-1,+1\}` and row sums all equal to `\delta \epsilon
1993
+ \sqrt(n)`. For more information, see [HX2010]_ or 10.5.1 in
1994
+ [BH2012]_. For the case `n=324`, see :func:`RSHCD_324` and [CP2016]_.
1995
+
1996
+ INPUT:
1997
+
1998
+ - ``n`` -- integer; side of the matrix
1999
+ - ``e`` -- `-1` or `+1`; the value of `\epsilon`
2000
+
2001
+ EXAMPLES::
2002
+
2003
+ sage: from sage.combinat.matrices.hadamard_matrix import regular_symmetric_hadamard_matrix_with_constant_diagonal
2004
+ sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(4,1)
2005
+ [ 1 1 1 -1]
2006
+ [ 1 1 -1 1]
2007
+ [ 1 -1 1 1]
2008
+ [-1 1 1 1]
2009
+ sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(4,-1)
2010
+ [ 1 -1 -1 -1]
2011
+ [-1 1 -1 -1]
2012
+ [-1 -1 1 -1]
2013
+ [-1 -1 -1 1]
2014
+
2015
+ Other hardcoded values::
2016
+
2017
+ sage: for n,e in [(36,1),(36,-1),(100,1),(100,-1),(196, 1)]: # long time
2018
+ ....: print(repr(regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e)))
2019
+ 36 x 36 dense matrix over Integer Ring
2020
+ 36 x 36 dense matrix over Integer Ring
2021
+ 100 x 100 dense matrix over Integer Ring
2022
+ 100 x 100 dense matrix over Integer Ring
2023
+ 196 x 196 dense matrix over Integer Ring
2024
+
2025
+ sage: for n,e in [(324,1),(324,-1)]: # not tested - long time, tested in RSHCD_324
2026
+ ....: print(repr(regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e)))
2027
+ 324 x 324 dense matrix over Integer Ring
2028
+ 324 x 324 dense matrix over Integer Ring
2029
+
2030
+ From two close prime powers::
2031
+
2032
+ sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(64,-1)
2033
+ 64 x 64 dense matrix over Integer Ring (use the '.str()' method to see the entries)
2034
+
2035
+ From a prime power and a conference matrix::
2036
+
2037
+ sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(676,1) # long time
2038
+ 676 x 676 dense matrix over Integer Ring (use the '.str()' method to see the entries)
2039
+
2040
+ Recursive construction::
2041
+
2042
+ sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(144,-1) # needs database_graphs
2043
+ 144 x 144 dense matrix over Integer Ring (use the '.str()' method to see the entries)
2044
+
2045
+ REFERENCE:
2046
+
2047
+ - [BH2012]_
2048
+
2049
+ - [HX2010]_
2050
+ """
2051
+ if existence and (n, e) in _rshcd_cache:
2052
+ return _rshcd_cache[n, e]
2053
+
2054
+ from sage.graphs.strongly_regular_db import strongly_regular_graph
2055
+
2056
+ def true():
2057
+ _rshcd_cache[n, e] = True
2058
+ return True
2059
+
2060
+ M = None
2061
+ if abs(e) != 1:
2062
+ raise ValueError
2063
+ sqn = None
2064
+ if is_square(n):
2065
+ sqn = int(sqrt(n))
2066
+ if n < 0:
2067
+ if existence:
2068
+ return False
2069
+ raise ValueError
2070
+ elif n == 4:
2071
+ if existence:
2072
+ return true()
2073
+ if e == 1:
2074
+ M = J(4)-2*matrix(4, [[int(i+j == 3) for i in range(4)] for j in range(4)])
2075
+ else:
2076
+ M = -J(4)+2*I(4)
2077
+ elif n == 36:
2078
+ if existence:
2079
+ return true()
2080
+ if e == 1:
2081
+ M = strongly_regular_graph(36, 15, 6, 6).adjacency_matrix()
2082
+ M = J(36) - 2*M
2083
+ else:
2084
+ M = strongly_regular_graph(36, 14, 4, 6).adjacency_matrix()
2085
+ M = -J(36) + 2*M + 2*I(36)
2086
+ elif n == 100:
2087
+ if existence:
2088
+ return true()
2089
+ if e == -1:
2090
+ M = strongly_regular_graph(100, 44, 18, 20).adjacency_matrix()
2091
+ M = 2*M - J(100) + 2*I(100)
2092
+ else:
2093
+ M = strongly_regular_graph(100, 45, 20, 20).adjacency_matrix()
2094
+ M = J(100) - 2*M
2095
+ elif n == 196 and e == 1:
2096
+ if existence:
2097
+ return true()
2098
+ M = strongly_regular_graph(196, 91, 42, 42).adjacency_matrix()
2099
+ M = J(196) - 2*M
2100
+ elif n == 324:
2101
+ if existence:
2102
+ return true()
2103
+ M = RSHCD_324(e)
2104
+ elif (e == 1 and
2105
+ n % 16 == 0 and
2106
+ sqn is not None and
2107
+ is_prime_power(sqn - 1) and
2108
+ is_prime_power(sqn + 1)):
2109
+ if existence:
2110
+ return true()
2111
+ M = -rshcd_from_close_prime_powers(sqn)
2112
+
2113
+ elif (e == 1 and
2114
+ sqn is not None and
2115
+ sqn % 4 == 2 and
2116
+ strongly_regular_graph(sqn-1, (sqn-2)//2, (sqn-6)//4,
2117
+ existence=True) is True and
2118
+ is_prime_power(ZZ(sqn + 1))):
2119
+ if existence:
2120
+ return true()
2121
+ M = rshcd_from_prime_power_and_conference_matrix(sqn+1)
2122
+
2123
+ # Recursive construction: the Kronecker product of two RSHCD is a RSHCD
2124
+ else:
2125
+ from itertools import product
2126
+ for n1, e1 in product(divisors(n)[1:-1], [-1, 1]):
2127
+ e2 = e1*e
2128
+ n2 = n//n1
2129
+ if (regular_symmetric_hadamard_matrix_with_constant_diagonal(n1, e1, existence=True) is True and
2130
+ regular_symmetric_hadamard_matrix_with_constant_diagonal(n2, e2, existence=True)) is True:
2131
+ if existence:
2132
+ return true()
2133
+ M1 = regular_symmetric_hadamard_matrix_with_constant_diagonal(n1, e1)
2134
+ M2 = regular_symmetric_hadamard_matrix_with_constant_diagonal(n2, e2)
2135
+ M = M1.tensor_product(M2)
2136
+ break
2137
+
2138
+ if M is None:
2139
+ from sage.misc.unknown import Unknown
2140
+ _rshcd_cache[n, e] = Unknown
2141
+ if existence:
2142
+ return Unknown
2143
+ raise ValueError("I do not know how to build a {}-RSHCD".format((n, e)))
2144
+
2145
+ assert M*M.transpose() == n*I(n)
2146
+ assert set(map(sum, M)) == {ZZ(e*sqn)}
2147
+
2148
+ return M
2149
+
2150
+
2151
+ def RSHCD_324(e):
2152
+ r"""
2153
+ Return a size 324x324 Regular Symmetric Hadamard Matrix with Constant Diagonal.
2154
+
2155
+ We build the matrix `M` for the case `n=324`, `\epsilon=1` directly from
2156
+ :meth:`JankoKharaghaniTonchevGraph
2157
+ <sage.graphs.graph_generators.GraphGenerators.JankoKharaghaniTonchevGraph>`
2158
+ and for the case `\epsilon=-1` from the "twist" `M'` of `M`, using Lemma 11
2159
+ in [HX2010]_. Namely, it turns out that the matrix
2160
+
2161
+ .. MATH::
2162
+
2163
+ M'=\begin{pmatrix} M_{12} & M_{11}\\ M_{11}^\top & M_{21} \end{pmatrix},
2164
+ \quad\text{where}\quad
2165
+ M=\begin{pmatrix} M_{11} & M_{12}\\ M_{21} & M_{22} \end{pmatrix},
2166
+
2167
+ and the `M_{ij}` are 162x162-blocks, also RSHCD, its diagonal blocks having zero row
2168
+ sums, as needed by [loc.cit.]. Interestingly, the corresponding
2169
+ `(324,152,70,72)`-strongly regular graph
2170
+ has a vertex-transitive automorphism group of order 2592, twice the order of the
2171
+ (intransitive) automorphism group of the graph corresponding to `M`. Cf. [CP2016]_.
2172
+
2173
+ INPUT:
2174
+
2175
+ - ``e`` -- `-1` or `+1`; the value of `\epsilon`
2176
+
2177
+ TESTS::
2178
+
2179
+ sage: from sage.combinat.matrices.hadamard_matrix import RSHCD_324, is_hadamard_matrix
2180
+ sage: for e in [1,-1]: # long time
2181
+ ....: M = RSHCD_324(e)
2182
+ ....: print("{} {} {}".format(M==M.T,is_hadamard_matrix(M),all(M[i,i]==1 for i in range(324))))
2183
+ ....: print(list(set(sum(x) for x in M)))
2184
+ True True True
2185
+ [18]
2186
+ True True True
2187
+ [-18]
2188
+
2189
+ REFERENCE:
2190
+
2191
+ - [CP2016]_
2192
+ """
2193
+ from sage.graphs.generators.smallgraphs import JankoKharaghaniTonchevGraph as JKTG
2194
+ M = JKTG().adjacency_matrix()
2195
+ M = J(324) - 2*M
2196
+ if e == -1:
2197
+ M1 = M[:162].T
2198
+ M2 = M[162:].T
2199
+ M11 = M1[:162]
2200
+ M12 = M1[162:].T
2201
+ M21 = M2[:162].T
2202
+ M = block_matrix([[M12, -M11], [-M11.T, M21]])
2203
+ return M
2204
+
2205
+
2206
+ def _helper_payley_matrix(n, zero_position=True):
2207
+ r"""
2208
+ Return the matrix constructed in Lemma 1.19 page 291 of [SWW1972]_.
2209
+
2210
+ This function return a `n^2` matrix `M` whose rows/columns are indexed by
2211
+ the element of a finite field on `n` elements `x_1,...,x_n`. The value
2212
+ `M_{i,j}` is equal to `\chi(x_i-x_j)`.
2213
+
2214
+ The elements `x_1,...,x_n` are ordered in such a way that the matrix
2215
+ (respectively, its submatrix obtained by removing first row and first column in the case
2216
+ ``zero_position=False``) is symmetric with respect to its second diagonal.
2217
+ The matrix is symmetric if `n=4k+1`, and skew-symmetric otherwise.
2218
+
2219
+ INPUT:
2220
+
2221
+ - ``n`` -- an odd prime power
2222
+ - ``zero_position`` -- boolean (default: ``True``); if it is true, place 0
2223
+ of ``F_n`` in the middle, otherwise place it first
2224
+
2225
+ .. SEEALSO::
2226
+
2227
+ :func:`rshcd_from_close_prime_powers`
2228
+
2229
+ EXAMPLES::
2230
+
2231
+ sage: from sage.combinat.matrices.hadamard_matrix import _helper_payley_matrix
2232
+ sage: _helper_payley_matrix(5)
2233
+ [ 0 1 1 -1 -1]
2234
+ [ 1 0 -1 1 -1]
2235
+ [ 1 -1 0 -1 1]
2236
+ [-1 1 -1 0 1]
2237
+ [-1 -1 1 1 0]
2238
+
2239
+ TESTS::
2240
+
2241
+ sage: _helper_payley_matrix(11,zero_position=True)
2242
+ [ 0 -1 1 -1 -1 1 -1 1 1 1 -1]
2243
+ [ 1 0 -1 1 -1 -1 -1 -1 1 1 1]
2244
+ [-1 1 0 -1 1 1 -1 -1 -1 1 1]
2245
+ [ 1 -1 1 0 -1 1 1 -1 -1 -1 1]
2246
+ [ 1 1 -1 1 0 1 -1 1 -1 -1 -1]
2247
+ [-1 1 -1 -1 -1 0 1 1 1 -1 1]
2248
+ [ 1 1 1 -1 1 -1 0 -1 1 -1 -1]
2249
+ [-1 1 1 1 -1 -1 1 0 -1 1 -1]
2250
+ [-1 -1 1 1 1 -1 -1 1 0 -1 1]
2251
+ [-1 -1 -1 1 1 1 1 -1 1 0 -1]
2252
+ [ 1 -1 -1 -1 1 -1 1 1 -1 1 0]
2253
+ sage: _helper_payley_matrix(11,zero_position=False)
2254
+ [ 0 -1 1 -1 -1 -1 1 1 1 -1 1]
2255
+ [ 1 0 -1 1 -1 -1 -1 1 1 1 -1]
2256
+ [-1 1 0 -1 1 -1 -1 -1 1 1 1]
2257
+ [ 1 -1 1 0 -1 1 -1 -1 -1 1 1]
2258
+ [ 1 1 -1 1 0 -1 1 -1 -1 -1 1]
2259
+ [ 1 1 1 -1 1 0 -1 1 -1 -1 -1]
2260
+ [-1 1 1 1 -1 1 0 -1 1 -1 -1]
2261
+ [-1 -1 1 1 1 -1 1 0 -1 1 -1]
2262
+ [-1 -1 -1 1 1 1 -1 1 0 -1 1]
2263
+ [ 1 -1 -1 -1 1 1 1 -1 1 0 -1]
2264
+ [-1 1 -1 -1 -1 1 1 1 -1 1 0]
2265
+ """
2266
+ from sage.rings.finite_rings.finite_field_constructor import FiniteField
2267
+ K = FiniteField(n, prefix='x')
2268
+
2269
+ # Order the elements of K in K_list
2270
+ # so that K_list[i] = -K_list[n - i - 1]
2271
+ K_pairs = set(tuple(sorted([x, -x])) for x in K if x)
2272
+ K_list = [K.zero()] * n
2273
+ shift = 0 if zero_position else 1
2274
+
2275
+ for i, (x, y) in enumerate(sorted(K_pairs)):
2276
+ K_list[i + shift] = x
2277
+ K_list[-i - 1] = y
2278
+
2279
+ M = matrix(ZZ, n, n, [(1 if (x - y).is_square() else -1)
2280
+ for x in K_list for y in K_list])
2281
+ M -= I(n)
2282
+ assert (M * J(n)).is_zero()
2283
+ assert M * M.transpose() == n * I(n) - J(n)
2284
+ return M
2285
+
2286
+
2287
+ def rshcd_from_close_prime_powers(n):
2288
+ r"""
2289
+ Return a `(n^2,1)`-RSHCD when `n-1` and `n+1` are odd prime powers and `n=0\pmod{4}`.
2290
+
2291
+ The construction implemented here appears in Theorem 4.3 from [GS1970]_.
2292
+
2293
+ Note that the authors of [SWW1972]_ claim in Corollary 5.12 (page 342) to have
2294
+ proved the same result without the `n=0\pmod{4}` restriction with a *very*
2295
+ similar construction. So far, however, I (Nathann Cohen) have not been able
2296
+ to make it work.
2297
+
2298
+ INPUT:
2299
+
2300
+ - ``n`` -- integer congruent to `0\pmod{4}`
2301
+
2302
+ .. SEEALSO::
2303
+
2304
+ :func:`regular_symmetric_hadamard_matrix_with_constant_diagonal`
2305
+
2306
+ EXAMPLES::
2307
+
2308
+ sage: from sage.combinat.matrices.hadamard_matrix import rshcd_from_close_prime_powers
2309
+ sage: rshcd_from_close_prime_powers(4)
2310
+ [-1 -1 1 -1 1 -1 -1 1 -1 1 -1 -1 1 -1 1 -1]
2311
+ [-1 -1 -1 1 1 -1 -1 1 -1 -1 1 -1 -1 1 -1 1]
2312
+ [ 1 -1 -1 1 1 1 1 -1 -1 -1 -1 -1 -1 -1 1 -1]
2313
+ [-1 1 1 -1 1 1 -1 -1 -1 -1 -1 1 -1 -1 -1 1]
2314
+ [ 1 1 1 1 -1 -1 -1 -1 -1 -1 1 -1 1 -1 -1 -1]
2315
+ [-1 -1 1 1 -1 -1 1 -1 -1 1 -1 1 -1 1 -1 -1]
2316
+ [-1 -1 1 -1 -1 1 -1 -1 1 -1 1 -1 -1 1 1 -1]
2317
+ [ 1 1 -1 -1 -1 -1 -1 -1 -1 1 -1 -1 -1 1 1 1]
2318
+ [-1 -1 -1 -1 -1 -1 1 -1 -1 -1 1 1 1 -1 1 1]
2319
+ [ 1 -1 -1 -1 -1 1 -1 1 -1 -1 -1 1 1 1 -1 -1]
2320
+ [-1 1 -1 -1 1 -1 1 -1 1 -1 -1 -1 1 1 -1 -1]
2321
+ [-1 -1 -1 1 -1 1 -1 -1 1 1 -1 -1 1 -1 -1 1]
2322
+ [ 1 -1 -1 -1 1 -1 -1 -1 1 1 1 1 -1 -1 -1 -1]
2323
+ [-1 1 -1 -1 -1 1 1 1 -1 1 1 -1 -1 -1 -1 -1]
2324
+ [ 1 -1 1 -1 -1 -1 1 1 1 -1 -1 -1 -1 -1 -1 1]
2325
+ [-1 1 -1 1 -1 -1 -1 1 1 -1 -1 1 -1 -1 1 -1]
2326
+
2327
+ REFERENCE:
2328
+
2329
+ - [SWW1972]_
2330
+ """
2331
+ if n % 4:
2332
+ raise ValueError("n(={}) must be congruent to 0 mod 4")
2333
+
2334
+ a, b = sorted([n-1, n+1], key=lambda x: -x % 4)
2335
+ Sa = _helper_payley_matrix(a)
2336
+ Sb = _helper_payley_matrix(b)
2337
+ U = matrix(a, [[int(i+j == a-1) for i in range(a)] for j in range(a)])
2338
+
2339
+ K = (U*Sa).tensor_product(Sb) + U.tensor_product(J(b)-I(b)) - J(a).tensor_product(I(b))
2340
+
2341
+ F = lambda x: diagonal_matrix([-(-1)**i for i in range(x)])
2342
+ G = block_diagonal_matrix([J(1), I(a).tensor_product(F(b))])
2343
+ e = matrix(a*b, [1]*(a*b))
2344
+ H = block_matrix(2, [-J(1), e.transpose(), e, K])
2345
+
2346
+ HH = G*H*G
2347
+ assert len(set(map(sum, HH))) == 1
2348
+ assert HH**2 == n**2*I(n**2)
2349
+ return HH
2350
+
2351
+
2352
+ def williamson_goethals_seidel_skew_hadamard_matrix(a, b, c, d, check=True):
2353
+ r"""
2354
+ Williamson-Goethals-Seidel construction of a skew Hadamard matrix.
2355
+
2356
+ Given `n\times n` (anti)circulant matrices `A`, `B`, `C`, `D` with 1,-1 entries,
2357
+ and satisfying `A+A^\top = 2I`, `AA^\top + BB^\top + CC^\top + DD^\top = 4nI`,
2358
+ one can construct a skew Hadamard matrix of order `4n`, cf. [GS70s]_.
2359
+
2360
+ INPUT:
2361
+
2362
+ - ``a`` -- 1,-1 list; the 1st row of `A`
2363
+ - ``b`` -- 1,-1 list; the 1st row of `B`
2364
+ - ``d`` -- 1,-1 list; the 1st row of `C`
2365
+ - ``c`` -- 1,-1 list; the 1st row of `D`
2366
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the
2367
+ resulting matrix is skew Hadamard before returning it
2368
+
2369
+ EXAMPLES::
2370
+
2371
+ sage: from sage.combinat.matrices.hadamard_matrix import williamson_goethals_seidel_skew_hadamard_matrix as WGS
2372
+ sage: a = [ 1, 1, 1, -1, 1, -1, 1, -1, -1]
2373
+ sage: b = [ 1, -1, 1, 1, -1, -1, 1, 1, -1]
2374
+ sage: c = [-1, -1]+[1]*6+[-1]
2375
+ sage: d = [ 1, 1, 1, -1, 1, 1, -1, 1, 1]
2376
+ sage: M = WGS(a,b,c,d,check=True)
2377
+
2378
+ REFERENCES:
2379
+
2380
+ - [GS70s]_
2381
+
2382
+ - [Wall71]_
2383
+
2384
+ - [KoSt08]_
2385
+ """
2386
+ n = len(a)
2387
+ A, B, C, D = map(matrix.circulant, [a, b, c, d])
2388
+ if check:
2389
+ assert A*A.T+B*B.T+C*C.T+D*D.T == 4*n*I(n)
2390
+ assert A+A.T == 2*I(n)
2391
+
2392
+ M = _construction_goethals_seidel_matrix(A, B, C, D)
2393
+ if check:
2394
+ assert is_hadamard_matrix(M, normalized=False, skew=True)
2395
+ return M
2396
+
2397
+
2398
+ def skew_hadamard_matrix_spence_construction(n, check=True):
2399
+ r"""
2400
+ Construct skew Hadamard matrix of order `n` using Spence construction.
2401
+
2402
+ This function will construct skew Hadamard matrix of order `n=2(q+1)` where `q` is
2403
+ a prime power with `q = 5` (mod 8). The construction is taken from [Spe1977]_, and the
2404
+ relative difference sets are constructed using :func:`sage.combinat.designs.difference_family.relative_difference_set_from_homomorphism`.
2405
+
2406
+ INPUT:
2407
+
2408
+ - ``n`` -- positive integer
2409
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the
2410
+ resulting matrix is Hadamard before returning it
2411
+
2412
+ OUTPUT:
2413
+
2414
+ If ``n`` satisfies the requirements described above, the function returns a
2415
+ `n\times n` Hadamard matrix. Otherwise, an exception is raised.
2416
+
2417
+ EXAMPLES::
2418
+
2419
+ sage: from sage.combinat.matrices.hadamard_matrix import skew_hadamard_matrix_spence_construction
2420
+ sage: skew_hadamard_matrix_spence_construction(28)
2421
+ 28 x 28 dense matrix over Integer Ring...
2422
+
2423
+ TESTS::
2424
+
2425
+ sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix
2426
+ sage: is_hadamard_matrix(skew_hadamard_matrix_spence_construction(12, check=False), skew=True)
2427
+ True
2428
+ sage: is_hadamard_matrix(skew_hadamard_matrix_spence_construction(60, check=False), skew=True)
2429
+ True
2430
+ sage: skew_hadamard_matrix_spence_construction(31)
2431
+ Traceback (most recent call last):
2432
+ ...
2433
+ ValueError: The order 31 is not covered by the Spence construction.
2434
+ sage: skew_hadamard_matrix_spence_construction(16)
2435
+ Traceback (most recent call last):
2436
+ ...
2437
+ ValueError: The order 16 is not covered by the Spence construction.
2438
+ """
2439
+ q = n//2 - 1
2440
+ m = (q+1)//2
2441
+ if n % 4 != 0 or not is_prime_power(q) or q % 8 != 5:
2442
+ raise ValueError(f'The order {n} is not covered by the Spence construction.')
2443
+
2444
+ G, D = relative_difference_set_from_homomorphism(q, 2, (q-1)//4, check=False, return_group=True)
2445
+ D_fixed = get_fixed_relative_difference_set(G, D)
2446
+ D_union = D_fixed + [q+1+el for el in D_fixed]
2447
+ D_union = list({el % (4*(q+1)) for el in D_union})
2448
+
2449
+ def find_a(i):
2450
+ for a in range(8):
2451
+ if (a*(q+1)//2+i) % 8 == 0:
2452
+ return a
2453
+
2454
+ ai = [find_a(0), find_a(1), find_a(2), find_a(3)]
2455
+ P = PolynomialRing(ZZ, 'x')
2456
+
2457
+ Tm = 0
2458
+ for i in range(m):
2459
+ Tm += P.monomial(i)
2460
+
2461
+ Ds = [[] for i in range(4)]
2462
+
2463
+ for el in D_union:
2464
+ i = el % 8
2465
+ if i > 3:
2466
+ continue
2467
+ Ds[i].append(((el + ai[i] * m) // 8) % m)
2468
+
2469
+ psis = [0 for i in range(4)]
2470
+ for i in range(4):
2471
+ for el in Ds[i]:
2472
+ psis[i] += P.monomial(el)
2473
+
2474
+ diffs = [(2*psis[i] - Tm).mod(P.monomial(m)-1) for i in range(4)]
2475
+ a = [-el for el in diffs[1].coefficients()]
2476
+ b = diffs[0].coefficients()
2477
+ c = diffs[2].coefficients()
2478
+ d = diffs[3].coefficients()
2479
+
2480
+ return williamson_goethals_seidel_skew_hadamard_matrix(a, b, c, d, check=check)
2481
+
2482
+
2483
+ def skew_hadamard_matrix_spence_1975(n, existence=False, check=True):
2484
+ r"""
2485
+ Construct a skew Hadamard matrix of order `n = 4(1 + q + q^2)` using the
2486
+ Spence construction.
2487
+
2488
+ If `n = 4(1 + q + q^2)` where `q` is a prime power such that either
2489
+ `1 + q + q^2` is a prime congruent to `3, 5, 7 \mod 8` or `3 + 2q + 2q^2` is
2490
+ a prime power, then a skew Hadamard matrix of order `n` can be constructed using
2491
+ the Goethals Seidel array. The four matrices `A, B, C, D` plugged into the
2492
+ GS-array are created using complementary difference sets of order `1 + q + q^2`
2493
+ (which exist if `q` satisfies the conditions above), and a cyclic planar
2494
+ difference set with parameters `(1 + q^2 + q^4, 1 + q^2, 1)`.
2495
+ These are constructed by the functions :func:`sage.combinat.designs.difference_family.complementary_difference_sets`
2496
+ and :func:`sage.combinat.designs.difference_family.difference_family`.
2497
+
2498
+ For more details, see [Spe1975b]_.
2499
+
2500
+ INPUT:
2501
+
2502
+ - ``n`` -- positive integer; the order of the matrix to be constructed
2503
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only return
2504
+ whether the Hadamard matrix can be constructed
2505
+ - ``check`` -- boolean (default: ``True``); check that the result
2506
+ is a skew Hadamard matrix before returning it
2507
+
2508
+ OUTPUT:
2509
+
2510
+ If ``existence=False``, returns the skew Hadamard matrix of order `n`. It
2511
+ raises an error if `n` does not satisfy the required conditions.
2512
+ If ``existence=True``, returns a boolean representing whether the matrix
2513
+ can be constructed or not.
2514
+
2515
+ EXAMPLES::
2516
+
2517
+ sage: from sage.combinat.matrices.hadamard_matrix import skew_hadamard_matrix_spence_1975
2518
+ sage: skew_hadamard_matrix_spence_1975(52)
2519
+ 52 x 52 dense matrix over Integer Ring...
2520
+
2521
+ If ``existence`` is True, the function returns a boolean::
2522
+
2523
+ sage: skew_hadamard_matrix_spence_1975(52, existence=True)
2524
+ True
2525
+
2526
+ TESTS::
2527
+
2528
+ sage: from sage.combinat.matrices.hadamard_matrix import is_skew_hadamard_matrix
2529
+ sage: is_skew_hadamard_matrix(skew_hadamard_matrix_spence_1975(52, check=False))
2530
+ True
2531
+ sage: skew_hadamard_matrix_spence_1975(100, existence=True)
2532
+ False
2533
+ sage: skew_hadamard_matrix_spence_1975(31)
2534
+ Traceback (most recent call last):
2535
+ ...
2536
+ ValueError: n is not in the form 4*(1+q+q^2)
2537
+ sage: skew_hadamard_matrix_spence_1975(16)
2538
+ Traceback (most recent call last):
2539
+ ...
2540
+ ValueError: n is not in the form 4*(1+q+q^2)
2541
+ sage: skew_hadamard_matrix_spence_1975(292)
2542
+ Traceback (most recent call last):
2543
+ ...
2544
+ ValueError: q=8 is not a valid parameter for this construction
2545
+ """
2546
+ from sage.combinat.designs.difference_family import is_fixed_relative_difference_set, difference_family, complementary_difference_sets
2547
+
2548
+ q = None
2549
+ m = n // 4
2550
+ for i in range(m):
2551
+ if 1 + i + i**2 == m and is_prime_power(i):
2552
+ q = i
2553
+ break
2554
+
2555
+ if n % 4 != 0 or q is None:
2556
+ if existence:
2557
+ return False
2558
+ raise ValueError('n is not in the form 4*(1+q+q^2)')
2559
+
2560
+ is_valid = (is_prime(m) and m % 8 in [3, 5, 7]) or is_prime_power(3 + 2*q + 2*q**2)
2561
+ if existence:
2562
+ return is_valid
2563
+
2564
+ if not is_valid:
2565
+ raise ValueError(f'q={q} is not a valid parameter for this construction')
2566
+
2567
+ G, sets = difference_family(1 + q**2 + q**4, 1 + q**2, 1)
2568
+
2569
+ def get_fixed_set(s, G, q):
2570
+ for el in G:
2571
+ fixed_set = [el + x for x in s]
2572
+ if is_fixed_relative_difference_set(fixed_set, q):
2573
+ return fixed_set
2574
+ assert False
2575
+
2576
+ D = get_fixed_set(sets[0], G, q)
2577
+
2578
+ D1 = [d for d in D if d % (1 - q + q**2) == 0]
2579
+ Dnot = [el for el in D if el not in D1]
2580
+ D2 = []
2581
+
2582
+ indices = set()
2583
+ for i in range(len(Dnot)):
2584
+ if i in indices:
2585
+ continue
2586
+ indices.add(i)
2587
+ for j in range(i+1, len(Dnot)):
2588
+ if j not in indices and Dnot[i] % m == Dnot[j] % m:
2589
+ indices.add(j)
2590
+ D2.append(Dnot[i])
2591
+
2592
+ D2 = [d % m for d in D2]
2593
+ D1 = [d % m for d in D1]
2594
+
2595
+ G2, U, V = complementary_difference_sets(m, check=False)
2596
+ G2list = list(G2)
2597
+ p = [-1 if j in U else 1 for j in G2list]
2598
+ q = [-1 if j in V else 1 for j in G2list]
2599
+ r = [1 if j % m in D1 or j % m in D2 else -1 for j in range(m)]
2600
+ s = [1 if j % m in D2 else -1 for j in range(m)]
2601
+
2602
+ return williamson_goethals_seidel_skew_hadamard_matrix(p, q, r, s, check=check)
2603
+
2604
+
2605
+ def GS_skew_hadamard_smallcases(n, existence=False, check=True):
2606
+ r"""
2607
+ Data for Williamson-Goethals-Seidel construction of skew Hadamard matrices.
2608
+
2609
+ Here we keep the data for this construction.
2610
+ Namely, it needs 4 circulant matrices with extra properties, as described in
2611
+ :func:`sage.combinat.matrices.hadamard_matrix.williamson_goethals_seidel_skew_hadamard_matrix`
2612
+ Matrices are taken from:
2613
+
2614
+ * `n=36, 52`: [GS70s]_
2615
+ * `n=92`: [Wall71]_
2616
+ * `n=188`: [Djo2008a]_
2617
+ * `n=236`: [FKS2004]_
2618
+ * `n=276`: [Djo2023a]_
2619
+
2620
+ Additional data is obtained from skew supplementary difference sets contained in
2621
+ :func:`sage.combinat.designs.difference_family.skew_supplementary_difference_set`, using the
2622
+ construction described in [Djo1992a]_.
2623
+
2624
+ INPUT:
2625
+
2626
+ - ``n`` -- integer; the order of the matrix
2627
+ - ``existence`` -- boolean (default: ``True``); if ``True``, only check that
2628
+ we can do the construction
2629
+ - ``check`` -- boolean (default: ``False``); if ``True``, check the result
2630
+
2631
+ TESTS::
2632
+
2633
+ sage: from sage.combinat.matrices.hadamard_matrix import GS_skew_hadamard_smallcases, is_skew_hadamard_matrix
2634
+ sage: GS_skew_hadamard_smallcases(36)
2635
+ 36 x 36 dense matrix over Integer Ring...
2636
+ sage: GS_skew_hadamard_smallcases(52)
2637
+ 52 x 52 dense matrix over Integer Ring...
2638
+ sage: GS_skew_hadamard_smallcases(92)
2639
+ 92 x 92 dense matrix over Integer Ring...
2640
+ sage: GS_skew_hadamard_smallcases(100)
2641
+ sage: is_skew_hadamard_matrix(GS_skew_hadamard_smallcases(324, check=False))
2642
+ True
2643
+ sage: is_skew_hadamard_matrix(GS_skew_hadamard_smallcases(156, check=False))
2644
+ True
2645
+ """
2646
+ WGS = williamson_goethals_seidel_skew_hadamard_matrix
2647
+
2648
+ def pmtoZ(s):
2649
+ return [1 if x == '+' else -1 for x in s]
2650
+
2651
+ db = {
2652
+ 36: ['+++-+-+--', '+-++--++-', '--++++++-', '+++-++-++'],
2653
+ 52: ['++++-++--+---', '-+-++----++-+', '--+-+++++-+++', '--+-+++++-+++'],
2654
+ 92: ['+-------++-+-+--+++++++', '++--+--++++--++++--+--+', '++---+-+-+-++-+-+-+---+', '+----+--+--++--+--+----'],
2655
+ 188: ['+----+----++-+-+---++-++--+--+++-+-+--++++-++++',
2656
+ '++--+---+------++------++-+-++--+-+-+----+---++',
2657
+ '+-+-++---++-+---+++---++-++-++-++-+++++-+-+----',
2658
+ '+++-++-+-+---+-+++++--+-----++---+--+++++--++-+'],
2659
+ 236: ['+-+---+-+-++-++---+----++-----+++++--++++-+++--+--+-+-+++-+',
2660
+ '+-+---+-+-++-++---+----++-----+++++--++++-+++--+--+-+-+++-+',
2661
+ '+++-++----+++-+-+++--+--++------+---+-----+--+-+--+---+----',
2662
+ '++++++--+++--+---++-+-+-+---+-+----++++-++-+--++-+--+------'],
2663
+ 276: ['+--+++--+-+++--+---++-+++++-+++-++-+--+---+-----+--+++-++---+-++---++',
2664
+ '+-++--+-+----++-+---++++-+---+-++++++++-+---+-++++---+-++----+-+--++-',
2665
+ '--+--+-++---+--++--+-+-+++-+--++---++++-+-+-+--+-++-+++++++--+--+++++',
2666
+ '-+---+++-----++---+++-+++--+++++--+---+-+-++++-++++-++-++-+-+++++++++']
2667
+ }
2668
+
2669
+ if existence:
2670
+ return n in db or skew_supplementary_difference_set(n//4, existence=True)
2671
+
2672
+ if n in db:
2673
+ a, b, c, d = map(pmtoZ, db[n])
2674
+ return WGS(a, b, c, d, check=check)
2675
+
2676
+ if skew_supplementary_difference_set(n//4, existence=True):
2677
+ t = n//4
2678
+
2679
+ G, [S1, S2, S3, S4] = skew_supplementary_difference_set(t, check=False, return_group=True)
2680
+ Glist = list(G)
2681
+
2682
+ A = matrix([[-1 if y-x in S1 else +1 for y in Glist] for x in Glist])
2683
+ B = matrix([[-1 if y-x in S2 else +1 for y in Glist] for x in Glist])
2684
+ C = matrix([[-1 if y-x in S3 else +1 for y in Glist] for x in Glist])
2685
+ D = matrix([[-1 if y-x in S4 else +1 for y in Glist] for x in Glist])
2686
+
2687
+ H = _construction_goethals_seidel_matrix(A, B, C, D)
2688
+ if check:
2689
+ assert is_skew_hadamard_matrix(H)
2690
+ return H
2691
+
2692
+ return None
2693
+
2694
+
2695
+ def skew_hadamard_matrix_from_orthogonal_design(n, existence=False, check=True):
2696
+ r"""
2697
+ Construct skew Hadamard matrices of order `mn(n - 1)` if suitable orthogonal designs exist.
2698
+
2699
+ In [Seb1978]_ is proved that if amicable Hadamard matrices of order `n` and an orthogonal
2700
+ design of type `(1, m, mn - m - 1)` in order `mn` exist, then a skew Hadamard matrix
2701
+ of order `mn(n - 1)` can be constructed. The paper uses amicable orthogonal designs
2702
+ instead of amicable Hadamard matrices, but the two are equivalent (see [Seb2017]_).
2703
+
2704
+ Amicable Hadamard matrices are constructed using :func:`amicable_hadamard_matrices`,
2705
+ and the orthogonal designs are constructed using the Goethals-Seidel array,
2706
+ with data taken from [Seb2017]_.
2707
+
2708
+ INPUT:
2709
+
2710
+ - ``n`` -- positive integer; the order of the matrix to be constructed
2711
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only return
2712
+ whether the skew Hadamard matrix can be constructed
2713
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the result
2714
+ is a skew Hadamard matrix before returning it
2715
+
2716
+ OUTPUT:
2717
+
2718
+ If ``existence=False``, returns the skew Hadamard matrix of order `n`. It
2719
+ raises an error if a construction for order `n` is not yet implemented, or if
2720
+ `n` does not satisfy the constraint.
2721
+ If ``existence=True``, returns a boolean representing whether the matrix
2722
+ can be constructed or not.
2723
+
2724
+ EXAMPLES::
2725
+
2726
+ sage: from sage.combinat.matrices.hadamard_matrix import skew_hadamard_matrix_from_orthogonal_design
2727
+ sage: skew_hadamard_matrix_from_orthogonal_design(756)
2728
+ 756 x 756 dense matrix over Integer Ring...
2729
+
2730
+ If ``existence`` is True, the function returns a boolean::
2731
+
2732
+ sage: skew_hadamard_matrix_from_orthogonal_design(200, existence=True)
2733
+ False
2734
+
2735
+ TESTS::
2736
+
2737
+ sage: from sage.combinat.matrices.hadamard_matrix import is_skew_hadamard_matrix
2738
+ sage: is_skew_hadamard_matrix(skew_hadamard_matrix_from_orthogonal_design(756, check=False))
2739
+ True
2740
+ sage: skew_hadamard_matrix_from_orthogonal_design(31)
2741
+ Traceback (most recent call last):
2742
+ ...
2743
+ ValueError: n must be a multiple of 4
2744
+ sage: skew_hadamard_matrix_from_orthogonal_design(16)
2745
+ Traceback (most recent call last):
2746
+ ...
2747
+ NotImplementedError: orthogonal designs for matrix of order 16 not yet implemented
2748
+ """
2749
+ # We use value i to represent entries where variable x_i should be, and -i for -x_i
2750
+ orthogonal_designs = {
2751
+ (1, 1, 26): [[1, 3, 3, -3, 3, -3, -3], [2, 3, 3, -3, 3, -3, -3],
2752
+ [3, 3, 3, -3, 3, 3, 3], [3, 3, -3, -3, -3, 3, -3]]
2753
+ }
2754
+
2755
+ if n % 4 != 0:
2756
+ raise ValueError('n must be a multiple of 4')
2757
+
2758
+ m1, m2 = None, None
2759
+ for d in divisors(n)[1:-1]:
2760
+ if (n//d) % (d-1) != 0:
2761
+ continue
2762
+ d1 = n // (d*(d - 1))
2763
+ if (1, d1, d1*d - d1 - 1) in orthogonal_designs and amicable_hadamard_matrices(d, existence=True):
2764
+ m1 = d1
2765
+ m2 = d
2766
+
2767
+ if m2 is None or m1 is None:
2768
+ if existence:
2769
+ return False
2770
+ raise NotImplementedError(f'orthogonal designs for matrix of order {n} not yet implemented')
2771
+
2772
+ if existence:
2773
+ return True
2774
+
2775
+ M, N = amicable_hadamard_matrices(m2, check=False)
2776
+ M = normalise_hadamard(M, skew=True)
2777
+ N = normalise_hadamard(N)
2778
+
2779
+ P = M[1:, 1:] - I(m2 - 1)
2780
+ D = N[1:, 1:]
2781
+
2782
+ A1, A2, A3, A4 = map(matrix.circulant, orthogonal_designs[(1, m1, m1*m2 - m1 - 1)])
2783
+ OD = _construction_goethals_seidel_matrix(A1, A2, A3, A4)
2784
+
2785
+ blocks = {1: P, -1: -P, 2: J(m2 - 1), -2: -J(m2 - 1), 3: D, -3: -D}
2786
+ H = block_matrix([[blocks[el] for el in row] for row in OD]) + I(n)
2787
+
2788
+ if check:
2789
+ assert is_skew_hadamard_matrix(H)
2790
+
2791
+ return H
2792
+
2793
+
2794
+ def skew_hadamard_matrix_from_complementary_difference_sets(n, existence=False, check=True):
2795
+ r"""
2796
+ Construct a skew Hadamard matrix of order `n=4(m+1)` from complementary difference sets.
2797
+
2798
+ If `A, B` are complementary difference sets over a group of order `2m+1`, then
2799
+ they can be used to construct a skew Hadamard matrix, as described in [BS1969]_.
2800
+
2801
+ The complementary difference sets are constructed using the function
2802
+ :func:`sage.combinat.designs.difference_family.complementary_difference_sets`.
2803
+
2804
+ INPUT:
2805
+
2806
+ - ``n`` -- positive integer; the order of the matrix to be constructed
2807
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only return
2808
+ whether the skew Hadamard matrix can be constructed
2809
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the
2810
+ result is a skew Hadamard matrix before returning it
2811
+
2812
+ OUTPUT:
2813
+
2814
+ If ``existence=False``, returns the skew Hadamard matrix of order `n`. It
2815
+ raises an error if `n` does not satisfy the required conditions.
2816
+ If ``existence=True``, returns a boolean representing whether the matrix
2817
+ can be constructed or not.
2818
+
2819
+ EXAMPLES::
2820
+
2821
+ sage: from sage.combinat.matrices.hadamard_matrix import skew_hadamard_matrix_from_complementary_difference_sets
2822
+ sage: skew_hadamard_matrix_from_complementary_difference_sets(20)
2823
+ 20 x 20 dense matrix over Integer Ring...
2824
+ sage: skew_hadamard_matrix_from_complementary_difference_sets(52, existence=True)
2825
+ True
2826
+
2827
+ TESTS::
2828
+
2829
+ sage: from sage.combinat.matrices.hadamard_matrix import is_skew_hadamard_matrix
2830
+ sage: is_skew_hadamard_matrix(skew_hadamard_matrix_from_complementary_difference_sets(24, check=False))
2831
+ True
2832
+ sage: is_skew_hadamard_matrix(skew_hadamard_matrix_from_complementary_difference_sets(12, check=False))
2833
+ True
2834
+ sage: skew_hadamard_matrix_from_complementary_difference_sets(31)
2835
+ Traceback (most recent call last):
2836
+ ...
2837
+ ValueError: n must be 1, 2 or a multiple of four.
2838
+ sage: skew_hadamard_matrix_from_complementary_difference_sets(100)
2839
+ Traceback (most recent call last):
2840
+ ...
2841
+ NotImplementedError: hadamard matrix of order 100 from complementary difference sets is not implemented yet
2842
+ sage: skew_hadamard_matrix_from_complementary_difference_sets(100, existence=True)
2843
+ False
2844
+ """
2845
+
2846
+ if n <= 0 or (n > 2 and n % 4 != 0):
2847
+ raise ValueError('n must be 1, 2 or a multiple of four.')
2848
+
2849
+ m = n//4 - 1
2850
+
2851
+ if existence:
2852
+ return complementary_difference_sets(2*m+1, existence=True)
2853
+
2854
+ if not complementary_difference_sets(2*m+1, existence=True):
2855
+ raise NotImplementedError(f'hadamard matrix of order {n} from complementary difference sets is not implemented yet')
2856
+
2857
+ G, A, B = complementary_difference_sets(2*m+1, check=False)
2858
+
2859
+ m = n//4 - 1
2860
+ Glist = list(G)
2861
+
2862
+ S = [[0 for i in range(n)] for j in range(n)]
2863
+ for i in range(2*m + 1):
2864
+ for j in range(2*m + 1):
2865
+ S[2*m + 1 + i][2*m + 1 + j] = -1 if Glist[j] - Glist[i] in A else 1
2866
+ S[i][j] = -S[2*m + 1 + i][2*m + 1 + j]
2867
+ S[2*m + 1 + j][i] = -1 if Glist[j] - Glist[i] in B else 1
2868
+ S[i][2*m + 1 + j] = -S[2*m + 1 + j][i]
2869
+ S[4*m + 2][i] = -1
2870
+ S[4*m + 2][2*m + 1 + i] = 1
2871
+ S[i][4*m + 2] = 1
2872
+ S[i + 2*m + 1][4*m + 2] = -1
2873
+ for i in range(4*m + 3):
2874
+ S[4*m + 3][i] = 1
2875
+ S[i][4*m + 3] = -1
2876
+ for i in range(4*m + 4):
2877
+ S[i][i] = 1
2878
+
2879
+ H = matrix(S)
2880
+
2881
+ if check:
2882
+ assert is_hadamard_matrix(H, skew=True)
2883
+ return H
2884
+
2885
+
2886
+ def skew_hadamard_matrix_whiteman_construction(n, existence=False, check=True):
2887
+ r"""
2888
+ Construct a skew Hadamard matrix of order `n=2(q+1)` where `q=p^t` is a prime power with `p \cong 5 \mod 8` and `t \cong 2 \mod 4`.
2889
+
2890
+ Assuming `n` satisfies the conditions above, it is possible to construct two supplementary difference sets
2891
+ `A, B` (see [Whi1971]_), and these can be used to construct a skew Hadamard matrix, as described in [BS1969]_.
2892
+
2893
+ INPUT:
2894
+
2895
+ - ``n`` -- positive integer; the order of the matrix to be constructed
2896
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only return
2897
+ whether the Hadamard matrix can be constructed
2898
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the result
2899
+ is a skew Hadamard matrix before returning it
2900
+
2901
+ OUTPUT:
2902
+
2903
+ If ``existence=False``, returns the skew Hadamard matrix of order `n`. It
2904
+ raises an error if `n` does not satisfy the required conditions.
2905
+ If ``existence=True``, returns a boolean representing whether the matrix can
2906
+ be constructed or not.
2907
+
2908
+ EXAMPLES::
2909
+
2910
+ sage: from sage.combinat.matrices.hadamard_matrix import skew_hadamard_matrix_whiteman_construction
2911
+ sage: skew_hadamard_matrix_whiteman_construction(52)
2912
+ 52 x 52 dense matrix over Integer Ring...
2913
+ sage: skew_hadamard_matrix_whiteman_construction(52, existence=True)
2914
+ True
2915
+
2916
+ TESTS::
2917
+
2918
+ sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix
2919
+ sage: is_hadamard_matrix(skew_hadamard_matrix_whiteman_construction(52, check=False), skew=True)
2920
+ True
2921
+ sage: is_hadamard_matrix(skew_hadamard_matrix_whiteman_construction(340, check=False), skew=True)
2922
+ True
2923
+ sage: skew_hadamard_matrix_whiteman_construction(31)
2924
+ Traceback (most recent call last):
2925
+ ...
2926
+ ValueError: The order 31 is not covered by the Whiteman construction.
2927
+ sage: skew_hadamard_matrix_whiteman_construction(100)
2928
+ Traceback (most recent call last):
2929
+ ...
2930
+ ValueError: The order 100 is not covered by the Whiteman construction.
2931
+ sage: skew_hadamard_matrix_whiteman_construction(100, existence=True)
2932
+ False
2933
+
2934
+ .. NOTE::
2935
+
2936
+ A more general version of this construction is :func:`skew_hadamard_matrix_from_complementary_difference_sets`.
2937
+ """
2938
+
2939
+ q = n // 2 - 1
2940
+ p, t = is_prime_power(q, get_data=True)
2941
+
2942
+ is_order_valid = n % 4 == 0 and t > 0 and p % 8 == 5 and t % 4 == 2
2943
+ if existence:
2944
+ return is_order_valid
2945
+
2946
+ if not is_order_valid:
2947
+ raise ValueError(f'The order {n} is not covered by the Whiteman construction.')
2948
+
2949
+ from sage.rings.finite_rings.finite_field_constructor import GF
2950
+ G = GF(q)
2951
+ f = (q-1) // 8
2952
+ Cs = {i: [G.gen()**(8*s+i) for s in range(f)] for i in [0, 1, 2, 3, 6, 7]}
2953
+ A = Cs[0] + Cs[1] + Cs[2] + Cs[3]
2954
+ B = Cs[0] + Cs[1] + Cs[6] + Cs[7]
2955
+
2956
+ m = n//4 - 1
2957
+ Glist = list(G)
2958
+
2959
+ S = [[0 for i in range(n)] for j in range(n)]
2960
+ for i in range(2*m + 1):
2961
+ for j in range(2*m + 1):
2962
+ S[2*m + 1 + i][2*m + 1 + j] = -1 if Glist[j] - Glist[i] in A else 1
2963
+ S[i][j] = -S[2*m + 1 + i][2*m + 1 + j]
2964
+ S[2*m + 1 + j][i] = -1 if Glist[j] - Glist[i] in B else 1
2965
+ S[i][2*m + 1 + j] = -S[2*m + 1 + j][i]
2966
+ S[4*m + 2][i] = -1
2967
+ S[4*m + 2][2*m + 1 + i] = 1
2968
+ S[i][4*m + 2] = 1
2969
+ S[i + 2*m + 1][4*m + 2] = -1
2970
+ for i in range(4*m + 3):
2971
+ S[4*m + 3][i] = 1
2972
+ S[i][4*m + 3] = -1
2973
+ for i in range(4*m + 4):
2974
+ S[i][i] = 1
2975
+
2976
+ H = matrix(S)
2977
+
2978
+ if check:
2979
+ assert is_hadamard_matrix(H, skew=True)
2980
+ return H
2981
+
2982
+
2983
+ def skew_hadamard_matrix_from_good_matrices(a, b, c, d, check=True):
2984
+ r"""
2985
+ Construct skew Hadamard matrix from good matrices.
2986
+
2987
+ Given good matrices `A`, `B`, `C`, `D` (`A` circulant, `B, C, D` back-circulant) they can be used to construct
2988
+ a skew Hadamard matrix using the following block matrix (as described in [Sze1988]_):
2989
+
2990
+ .. MATH::
2991
+
2992
+ \left(\begin{array}{rrrr}
2993
+ A & B & C & D \\
2994
+ -B & A & D & -C \\
2995
+ -C & -D & A & B \\
2996
+ -D & C & -B & A
2997
+ \end{array}\right)
2998
+
2999
+ INPUT:
3000
+
3001
+ - ``a`` -- (1,-1) list; the 1st row of `A`
3002
+ - ``b`` -- (1,-1) list; the 1st row of `B`
3003
+ - ``d`` -- (1,-1) list; the 1st row of `C`
3004
+ - ``c`` -- (1,-1) list; the 1st row of `D`
3005
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the matrix
3006
+ is a skew Hadamard matrix before returning it
3007
+
3008
+ EXAMPLES::
3009
+
3010
+ sage: from sage.combinat.matrices.hadamard_matrix import skew_hadamard_matrix_from_good_matrices
3011
+ sage: a, b, c, d = ([1, 1, 1, -1, -1], [1, -1, 1, 1, -1], [1, -1, -1, -1, -1], [1, -1, -1, -1, -1])
3012
+ sage: skew_hadamard_matrix_from_good_matrices(a, b, c, d)
3013
+ 20 x 20 dense matrix over Integer Ring...
3014
+
3015
+ TESTS::
3016
+
3017
+ sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix
3018
+ sage: a, b, c, d = ([1, 1, 1, -1, -1], [1, -1, 1, 1, -1], [1, -1, -1, -1, -1], [1, -1, -1, -1, -1])
3019
+ sage: is_hadamard_matrix(skew_hadamard_matrix_from_good_matrices(a, b, c, d, check=False), skew=True)
3020
+ True
3021
+ sage: a, b, c, d = ([1, 1, 1, -1, -1], [1, -1, 1, 1, -1], [1, -1, -1, -1, -1], [1, -1, -1, -1, 1])
3022
+ sage: skew_hadamard_matrix_from_good_matrices(a, b, c, d)
3023
+ Traceback (most recent call last):
3024
+ ...
3025
+ AssertionError
3026
+ sage: a, b, c, d = ([1, 1, 1], [1, -1, 1, 1, -1], [1, -1, -1, -1, -1], [1, -1, -1, -1, -1])
3027
+ sage: skew_hadamard_matrix_from_good_matrices(a, b, c, d)
3028
+ Traceback (most recent call last):
3029
+ ...
3030
+ AssertionError
3031
+ """
3032
+ n = len(a)
3033
+ m = (n-1) // 2
3034
+
3035
+ assert len(a) == len(b) == len(c) == len(d)
3036
+ assert a[0] == 1 and b[0] == 1 and c[0] == 1 and d[0] == 1
3037
+ for i in range(1, m+1):
3038
+ assert a[i] == -a[n-i] and b[i] == b[n-i] and c[i] == c[n-i] and d[i] == d[n-i]
3039
+
3040
+ def back_circulant(row):
3041
+ length = len(row)
3042
+ return matrix([[row[(j+i) % length] for j in range(length)] for i in range(length)])
3043
+
3044
+ A = matrix.circulant(a)
3045
+ B = back_circulant(b)
3046
+ C = back_circulant(c)
3047
+ D = back_circulant(d)
3048
+
3049
+ H = block_matrix([[ A, B, C, D],
3050
+ [-B, A, D, -C],
3051
+ [-C, -D, A, B],
3052
+ [-D, C, -B, A]])
3053
+
3054
+ if check:
3055
+ assert is_hadamard_matrix(H, skew=True)
3056
+ return H
3057
+
3058
+
3059
+ def skew_hadamard_matrix_from_good_matrices_smallcases(n, existence=False, check=True):
3060
+ r"""
3061
+ Construct skew Hadamard matrices from good matrices for some small values of `n=4m`, with `m` odd.
3062
+
3063
+ The function stores good matrices of odd orders `\le 31`, taken from [Sze1988]_.
3064
+ These are used to create skew Hadamard matrices of order `4m`, `1 \le m \le 31` (`m` odd), using the function
3065
+ :func:`skew_hadamard_matrix_from_good_matrices`.
3066
+
3067
+ ALGORITHM:
3068
+
3069
+ Given four sequences (stored in ``E_sequences``) of length `m`, they can be used to construct four `E-sequences`
3070
+ of length `n=2m+1`, as follows:
3071
+
3072
+ .. MATH::
3073
+
3074
+ \begin{aligned}
3075
+ a &= 1, a_0, a_1, ..., a_m, -a_m, -a_{m-1}, ..., -a_0 \\
3076
+ b &= 1, b_0, b_1, ..., b_m, b_m, b_{m-1}, ..., b_0 \\
3077
+ c &= 1, c_0, c_1, ..., c_m, c_m, c_{m-1}, ..., c_0 \\
3078
+ d &= 1, d_0, d_1, ..., d_m, d_m, d_{m-1}, ..., d_0 \\
3079
+ \end{aligned}
3080
+
3081
+ These E-sequences will be the first rows of the four good matrices needed to construct a skew Hadamard matrix
3082
+ of order `4n`.
3083
+
3084
+ INPUT:
3085
+
3086
+ - ``n`` -- integer; the order of the skew Hadamard matrix to be constructed
3087
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only return
3088
+ whether the Hadamard matrix can be constructed
3089
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the matrix
3090
+ is a Hadamard matrix before returning it
3091
+
3092
+ OUTPUT:
3093
+
3094
+ If ``existence=False``, returns the skew Hadamard matrix of order `n`. It
3095
+ raises an error if no data is available to construct the matrix of the given
3096
+ order.
3097
+ If ``existence=True``, returns a boolean representing whether the matrix can
3098
+ be constructed or not.
3099
+
3100
+ EXAMPLES::
3101
+
3102
+ sage: from sage.combinat.matrices.hadamard_matrix import skew_hadamard_matrix_from_good_matrices_smallcases
3103
+ sage: skew_hadamard_matrix_from_good_matrices_smallcases(20)
3104
+ 20 x 20 dense matrix over Integer Ring...
3105
+ sage: skew_hadamard_matrix_from_good_matrices_smallcases(20, existence=True)
3106
+ True
3107
+
3108
+ TESTS::
3109
+
3110
+ sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix
3111
+ sage: is_hadamard_matrix(skew_hadamard_matrix_from_good_matrices_smallcases(28, check=False), skew=True)
3112
+ True
3113
+ sage: skew_hadamard_matrix_from_good_matrices_smallcases(140)
3114
+ Traceback (most recent call last):
3115
+ ...
3116
+ ValueError: The Good matrices of order 35 are not yet implemented.
3117
+ sage: skew_hadamard_matrix_from_good_matrices_smallcases(14)
3118
+ Traceback (most recent call last):
3119
+ ...
3120
+ ValueError: The skew Hadamard matrix of order 14 from good matrices does not exist.
3121
+ sage: skew_hadamard_matrix_from_good_matrices_smallcases(140, existence=True)
3122
+ False
3123
+ sage: skew_hadamard_matrix_from_good_matrices_smallcases(14, existence=True)
3124
+ False
3125
+ """
3126
+ E_sequences = {
3127
+ 0: ['', '', '', ''],
3128
+ 1: ['+', '-', '-', '+'],
3129
+ 2: ['++', '-+', '--', '--'],
3130
+ 3: ['++-', '++-', '+-+', '-++'],
3131
+ 4: ['+++-', '+-+-', '---+', '++-+'],
3132
+ 5: ['+-+--', '+++--', '-+++-', '---+-'],
3133
+ 6: ['+-+---', '---+++', '+-+--+', '----+-'],
3134
+ 7: ['+++++--', '-++--++', '----+-+', '-+---+-'],
3135
+ 8: ['+--++-+-', '+--+----', '++----+-', '+---+-+-'],
3136
+ 9: ['-+-----++', '+-+++++--', '-+----++-', '--+-+-++-'],
3137
+ 10: ['+--+++++++', '++--++++-+', '--++-+-+-+', '---+++-+-+'],
3138
+ 11: ['++-+-------', '+----+--+--', '+-+--++---+', '--++-+-+-++'],
3139
+ 12: ['+-----+-+---', '+-++++-+-++-', '---+--++++--', '--+-+++--+--'],
3140
+ 13: ['+---+-+--++-+', '+++---++-++-+', '+++-+++-++---', '+---++++-+-+-'],
3141
+ 14: ['+--+----+-+-++', '+---++++-++--+', '+-+----++-+--+', '++++++---+-+-+'],
3142
+ 15: ['+--++----+---+-', '-++-+---+-+++--', '++---+--+--+++-', '-++++++++--+-+-']
3143
+ }
3144
+
3145
+ def pm_to_good_matrix(s, sign=1):
3146
+ e1 = [1 if x == '+' else -1 for x in s]
3147
+ e2 = [sign * (1 if x == '+' else -1) for x in s]
3148
+ e2.reverse()
3149
+ return [1] + e1 + e2
3150
+
3151
+ if not (n % 4 == 0 and (n//4) % 2 == 1):
3152
+ if existence:
3153
+ return False
3154
+ raise ValueError("The skew Hadamard matrix of order %s from good matrices does not exist." % n)
3155
+
3156
+ m = n//4
3157
+ l = (m-1) // 2
3158
+
3159
+ if existence:
3160
+ return l in E_sequences
3161
+
3162
+ if l not in E_sequences:
3163
+ raise ValueError("The Good matrices of order %s are not yet implemented." % m)
3164
+
3165
+ e1, e2, e3, e4 = E_sequences[l]
3166
+ a = pm_to_good_matrix(e1, sign=-1)
3167
+ b = pm_to_good_matrix(e2)
3168
+ c = pm_to_good_matrix(e3)
3169
+ d = pm_to_good_matrix(e4)
3170
+ return skew_hadamard_matrix_from_good_matrices(a, b, c, d, check=check)
3171
+
3172
+
3173
+ _skew_had_cache = {}
3174
+
3175
+
3176
+ def skew_hadamard_matrix(n, existence=False, skew_normalize=True, check=True,
3177
+ construction_name=False):
3178
+ r"""
3179
+ Try to construct a skew Hadamard matrix.
3180
+
3181
+ A Hadamard matrix `H` is called skew if `H=S-I`, for `I` the identity matrix
3182
+ and `-S=S^\top`. Currently all orders `\le 1200` for which a construction is
3183
+ known are implemented. For `n > 1200`, only some orders are available.
3184
+
3185
+ INPUT:
3186
+
3187
+ - ``n`` -- integer; dimension of the matrix
3188
+ - ``existence`` -- boolean (default: ``False``); whether to build the matrix
3189
+ or merely query if a construction is available in Sage. When set to ``True``,
3190
+ the function returns:
3191
+
3192
+ - ``True`` -- meaning that Sage knows how to build the matrix
3193
+ - ``Unknown`` -- meaning that Sage does not know how to build the
3194
+ matrix, but that the design may exist (see :mod:`sage.misc.unknown`).
3195
+ - ``False`` -- meaning that the matrix does not exist
3196
+
3197
+ - ``skew_normalize`` -- boolean (default: ``True``); whether to make the 1st
3198
+ row all-one, and adjust the 1st column accordingly
3199
+ - ``check`` -- boolean (default: ``True``); whether to check that output is
3200
+ correct before returning it. As this is expected to be useless, you may
3201
+ want to disable it whenever you want speed.
3202
+ - ``construction_name`` -- boolean (default: ``False``); if it is ``True``,
3203
+ ``existence`` is ``True``, and a matrix exists, output the construction name.
3204
+ It has no effect if ``existence`` is set to ``False``.
3205
+
3206
+ EXAMPLES::
3207
+
3208
+ sage: from sage.combinat.matrices.hadamard_matrix import skew_hadamard_matrix
3209
+ sage: skew_hadamard_matrix(12).det()
3210
+ 2985984
3211
+ sage: 12^6
3212
+ 2985984
3213
+ sage: skew_hadamard_matrix(1)
3214
+ [1]
3215
+ sage: skew_hadamard_matrix(2)
3216
+ [ 1 1]
3217
+ [-1 1]
3218
+ sage: skew_hadamard_matrix(196, existence=True, construction_name=True)
3219
+ 'Williamson-Goethals-Seidel: 196'
3220
+
3221
+ TESTS::
3222
+
3223
+ sage: skew_hadamard_matrix(10,existence=True)
3224
+ False
3225
+ sage: skew_hadamard_matrix(12,existence=True)
3226
+ True
3227
+ sage: skew_hadamard_matrix(784,existence=True)
3228
+ True
3229
+ sage: skew_hadamard_matrix(10)
3230
+ Traceback (most recent call last):
3231
+ ...
3232
+ ValueError: A skew Hadamard matrix of order 10 does not exist
3233
+ sage: skew_hadamard_matrix(36)
3234
+ 36 x 36 dense matrix over Integer Ring...
3235
+ sage: skew_hadamard_matrix(36)==skew_hadamard_matrix(36,skew_normalize=False)
3236
+ False
3237
+ sage: skew_hadamard_matrix(52)
3238
+ 52 x 52 dense matrix over Integer Ring...
3239
+ sage: skew_hadamard_matrix(92)
3240
+ 92 x 92 dense matrix over Integer Ring...
3241
+ sage: skew_hadamard_matrix(816) # long time
3242
+ 816 x 816 dense matrix over Integer Ring...
3243
+ sage: skew_hadamard_matrix(356)
3244
+ Traceback (most recent call last):
3245
+ ...
3246
+ ValueError: A skew Hadamard matrix of order 356 is not yet implemented.
3247
+ sage: skew_hadamard_matrix(356,existence=True)
3248
+ Unknown
3249
+
3250
+ Check that :issue:`28526` is fixed::
3251
+
3252
+ sage: skew_hadamard_matrix(0)
3253
+ Traceback (most recent call last):
3254
+ ...
3255
+ ValueError: parameter n must be strictly positive
3256
+
3257
+ REFERENCES:
3258
+
3259
+ - [Ha83]_
3260
+ """
3261
+ if n < 1:
3262
+ raise ValueError("parameter n must be strictly positive")
3263
+
3264
+ def true(nam):
3265
+ _skew_had_cache[n] = nam
3266
+ if construction_name:
3267
+ return nam+": "+str(n)
3268
+ return True
3269
+ M = None
3270
+ name = ''
3271
+ if existence and n in _skew_had_cache:
3272
+ return true(_skew_had_cache[n])
3273
+ if not (n % 4 == 0) and (n > 2):
3274
+ if existence:
3275
+ return False
3276
+ raise ValueError("A skew Hadamard matrix of order %s does not exist" % n)
3277
+ if n == 2:
3278
+ if existence:
3279
+ return true(name)
3280
+ M = matrix([[1, 1], [-1, 1]])
3281
+ elif n == 1:
3282
+ if existence:
3283
+ return true(name)
3284
+ M = matrix([1])
3285
+ elif skew_hadamard_matrix_from_good_matrices_smallcases(n, existence=True):
3286
+ name = "good matrices small cases"
3287
+ if existence:
3288
+ return true(name)
3289
+ M = skew_hadamard_matrix_from_good_matrices_smallcases(n, check=False)
3290
+ elif is_prime_power(n - 1) and ((n - 1) % 4 == 3):
3291
+ name = "paleyI"
3292
+ if existence:
3293
+ return true(name)
3294
+ M = hadamard_matrix_paleyI(n, normalize=False)
3295
+ elif is_prime_power(n//2 - 1) and (n//2 - 1) % 8 == 5:
3296
+ name = "spence"
3297
+ if existence:
3298
+ return true(name)
3299
+ M = skew_hadamard_matrix_spence_construction(n, check=False)
3300
+ elif skew_hadamard_matrix_from_complementary_difference_sets(n, existence=True):
3301
+ name = "complementary DS"
3302
+ if existence:
3303
+ return true(name)
3304
+ M = skew_hadamard_matrix_from_complementary_difference_sets(n, check=False)
3305
+ elif skew_hadamard_matrix_spence_1975(n, existence=True):
3306
+ name = "spence [Spe1975b]"
3307
+ if existence:
3308
+ return true(name)
3309
+ M = skew_hadamard_matrix_spence_1975(n, check=False)
3310
+ elif skew_hadamard_matrix_from_orthogonal_design(n, existence=True):
3311
+ name = "orthogonal design"
3312
+ if existence:
3313
+ return true(name)
3314
+ M = skew_hadamard_matrix_from_orthogonal_design(n, check=False)
3315
+ elif n % 8 == 0:
3316
+ if skew_hadamard_matrix(n//2, existence=True) is True: # (Lemma 14.1.6 in [Ha83]_)
3317
+ name = "doubling"
3318
+ if existence:
3319
+ return true(name)
3320
+ H = skew_hadamard_matrix(n//2, check=False)
3321
+ M = block_matrix([[H, H], [-H.T, H.T]])
3322
+
3323
+ else: # try Williamson construction (Lemma 14.1.5 in [Ha83]_)
3324
+ for d in divisors(n)[2:-2]: # skip 1, 2, n/2, and n
3325
+ n1 = n//d
3326
+ if is_prime_power(d - 1) and (d % 4 == 0) and (n1 % 4 == 0)\
3327
+ and skew_hadamard_matrix(n1, existence=True) is True:
3328
+ from sage.arith.misc import factor
3329
+ name = "williamson - Lemma 14.1.5 [Ha83] ("+str(factor(d-1))+","+str(n1)+") "
3330
+ if existence:
3331
+ return true(name)
3332
+ H = skew_hadamard_matrix(n1, check=False)-I(n1)
3333
+ U = matrix(ZZ, d, lambda i, j: -1 if i == j == 0 else
3334
+ 1 if i == j == 1 or (i > 1 and j-1 == d-i)
3335
+ else 0)
3336
+ A = block_matrix([[matrix([0]), matrix(ZZ, 1, d-1, [1]*(d-1))],
3337
+ [matrix(ZZ, d-1, 1, [-1]*(d-1)),
3338
+ _helper_payley_matrix(d-1, zero_position=0)]])+I(d)
3339
+ M = A.tensor_product(I(n1))+(U*A).tensor_product(H)
3340
+ break
3341
+ if M is None: # try Williamson-Goethals-Seidel construction
3342
+ if GS_skew_hadamard_smallcases(n, existence=True) is True:
3343
+ name = "Williamson-Goethals-Seidel"
3344
+ if existence:
3345
+ return true(name)
3346
+ M = GS_skew_hadamard_smallcases(n)
3347
+
3348
+ else:
3349
+ if existence:
3350
+ return Unknown
3351
+ raise ValueError("A skew Hadamard matrix of order %s is not yet implemented." % n)
3352
+ if skew_normalize:
3353
+ M = normalise_hadamard(M, skew=True)
3354
+ if check:
3355
+ assert is_hadamard_matrix(M, normalized=skew_normalize, skew=True)
3356
+ _skew_had_cache[n] = name
3357
+ return M
3358
+
3359
+
3360
+ def symmetric_conference_matrix(n, check=True, existence=False):
3361
+ r"""
3362
+ Try to construct a symmetric conference matrix.
3363
+
3364
+ A conference matrix is an `n\times n` matrix `C` with 0s on the main diagonal
3365
+ and 1s and -1s elsewhere, satisfying `CC^\top=(n-1)I`.
3366
+ If `C=C^\top` then `n \cong 2 \mod 4` and `C` is Seidel adjacency matrix of
3367
+ a graph, whose descendent graphs are strongly regular graphs with parameters
3368
+ `(n-1,(n-2)/2,(n-6)/4,(n-2)/4)`, see Sec.10.4 of [BH2012]_. Thus we build `C`
3369
+ from the Seidel adjacency matrix of the latter by adding row and column of 1s.
3370
+
3371
+ INPUT:
3372
+
3373
+ - ``n`` -- integer; dimension of the matrix
3374
+ - ``check`` -- boolean (default: ``True``); whether to check that output is
3375
+ correct before returning it. As this is expected to be useless, you may
3376
+ want to disable it whenever you want speed.
3377
+ - ``existence`` -- boolean (default: ``False``); if true, only check that such
3378
+ a matrix exists.
3379
+
3380
+ EXAMPLES::
3381
+
3382
+ sage: from sage.combinat.matrices.hadamard_matrix import symmetric_conference_matrix
3383
+ sage: C = symmetric_conference_matrix(10); C # needs database_graphs
3384
+ [ 0 1 1 1 1 1 1 1 1 1]
3385
+ [ 1 0 -1 -1 1 -1 1 1 1 -1]
3386
+ [ 1 -1 0 -1 1 1 -1 -1 1 1]
3387
+ [ 1 -1 -1 0 -1 1 1 1 -1 1]
3388
+ [ 1 1 1 -1 0 -1 -1 1 -1 1]
3389
+ [ 1 -1 1 1 -1 0 -1 1 1 -1]
3390
+ [ 1 1 -1 1 -1 -1 0 -1 1 1]
3391
+ [ 1 1 -1 1 1 1 -1 0 -1 -1]
3392
+ [ 1 1 1 -1 -1 1 1 -1 0 -1]
3393
+ [ 1 -1 1 1 1 -1 1 -1 -1 0]
3394
+ sage: C^2 == 9*identity_matrix(10) and C == C.T # needs database_graphs
3395
+ True
3396
+ """
3397
+ from sage.graphs.strongly_regular_db import strongly_regular_graph as srg
3398
+ try:
3399
+ m = srg(n-1, (n-2)/2, (n-6)/4, (n-2)/4, existence=existence)
3400
+ except ValueError:
3401
+ raise
3402
+ if existence:
3403
+ return m
3404
+ C = matrix([0]+[1]*(n-1)).stack(matrix([1]*(n-1)).stack(m.seidel_adjacency_matrix()).T)
3405
+ if check:
3406
+ assert (C == C.T and C**2 == (n-1)*I(n))
3407
+ return C
3408
+
3409
+
3410
+ def szekeres_difference_set_pair(m, check=True):
3411
+ r"""
3412
+ Construct Szekeres `(2m+1,m,1)`-cyclic difference family.
3413
+
3414
+ Let `4m+3` be a prime power. Theorem 3 in [Sz1969]_ contains a construction of a pair
3415
+ of *complementary difference sets* `A`, `B` in the subgroup `G` of the quadratic
3416
+ residues in `F_{4m+3}^*`. Namely `|A|=|B|=m`, `a\in A` whenever `a-1\in G`, `b\in B`
3417
+ whenever `b+1 \in G`. See also Theorem 2.6 in [SWW1972]_ (there the formula for `B` is
3418
+ correct, as opposed to (4.2) in [Sz1969]_, where the sign before `1` is wrong.
3419
+
3420
+ In modern terminology, for `m>1` the sets `A` and `B` form a
3421
+ :func:`difference family<sage.combinat.designs.difference_family>` with parameters `(2m+1,m,1)`.
3422
+ I.e. each non-identity `g \in G` can be expressed uniquely as `xy^{-1}` for `x,y \in A` or `x,y \in B`.
3423
+ Other, specific to this construction, properties of `A` and `B` are: for `a` in `A` one has
3424
+ `a^{-1}` not in `A`, whereas for `b` in `B` one has `b^{-1}` in `B`.
3425
+
3426
+ INPUT:
3427
+
3428
+ - ``m`` -- integer; dimension of the matrix
3429
+ - ``check`` -- boolean (default: ``True``); whether to check `A` and `B` for
3430
+ correctness
3431
+
3432
+ EXAMPLES::
3433
+
3434
+ sage: from sage.combinat.matrices.hadamard_matrix import szekeres_difference_set_pair
3435
+ sage: G,A,B=szekeres_difference_set_pair(6)
3436
+ sage: G,A,B=szekeres_difference_set_pair(7)
3437
+
3438
+ REFERENCE:
3439
+
3440
+ - [Sz1969]_
3441
+ """
3442
+ from sage.rings.finite_rings.finite_field_constructor import GF
3443
+ F = GF(4*m+3)
3444
+ t = F.multiplicative_generator()**2
3445
+ G = F.cyclotomic_cosets(t, cosets=[F.one()])[0]
3446
+ sG = set(G)
3447
+ A = [a for a in G if a - F.one() in sG]
3448
+ B = [b for b in G if b + F.one() in sG]
3449
+ if check:
3450
+ from itertools import product, chain
3451
+ assert (len(A) == len(B) == m)
3452
+ if m > 1:
3453
+ assert (sG == {xy[0] / xy[1]
3454
+ for xy in chain(product(A, A), product(B, B))})
3455
+ assert all(F.one() / b + F.one() in sG for b in B)
3456
+ assert (not any(F.one() / a - F.one() in sG for a in A))
3457
+ return G, A, B
3458
+
3459
+
3460
+ def typeI_matrix_difference_set(G, A):
3461
+ r"""
3462
+ (1,-1)-incidence type I matrix of a difference set `A` in `G`.
3463
+
3464
+ Let `A` be a difference set in a group `G` of order `n`. Return `n\times n`
3465
+ matrix `M` with `M_{ij}=1` if `A_i A_j^{-1} \in A`, and `M_{ij}=-1` otherwise.
3466
+
3467
+ EXAMPLES::
3468
+
3469
+ sage: from sage.combinat.matrices.hadamard_matrix import szekeres_difference_set_pair
3470
+ sage: from sage.combinat.matrices.hadamard_matrix import typeI_matrix_difference_set
3471
+ sage: G,A,B=szekeres_difference_set_pair(2)
3472
+ sage: typeI_matrix_difference_set(G,A)
3473
+ [-1 1 -1 -1 1]
3474
+ [-1 -1 -1 1 1]
3475
+ [ 1 1 -1 -1 -1]
3476
+ [ 1 -1 1 -1 -1]
3477
+ [-1 -1 1 1 -1]
3478
+ """
3479
+ n = len(G)
3480
+ return matrix(n, n, lambda i, j: 1 if G[i]/G[j] in A else -1)
3481
+
3482
+
3483
+ def rshcd_from_prime_power_and_conference_matrix(n):
3484
+ r"""
3485
+ Return a `((n-1)^2,1)`-RSHCD if `n` is prime power, and symmetric `(n-1)`-conference matrix exists.
3486
+
3487
+ The construction implemented here is Theorem 16 (and Corollary 17) from [WW1972]_.
3488
+
3489
+ In [SWW1972]_ this construction (Theorem 5.15 and Corollary 5.16)
3490
+ is reproduced with a typo. Note that [WW1972]_ refers to [Sz1969]_ for the construction,
3491
+ provided by :func:`szekeres_difference_set_pair`,
3492
+ of complementary difference sets, and the latter has a typo.
3493
+
3494
+ From a :func:`symmetric_conference_matrix`, we only need the Seidel
3495
+ adjacency matrix of the underlying strongly regular conference (i.e. Paley
3496
+ type) graph, which we construct directly.
3497
+
3498
+ INPUT:
3499
+
3500
+ - ``n`` -- integer
3501
+
3502
+ .. SEEALSO::
3503
+
3504
+ :func:`regular_symmetric_hadamard_matrix_with_constant_diagonal`
3505
+
3506
+ EXAMPLES:
3507
+
3508
+ A 36x36 example ::
3509
+
3510
+ sage: # needs database_graphs
3511
+ sage: from sage.combinat.matrices.hadamard_matrix import rshcd_from_prime_power_and_conference_matrix
3512
+ sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix
3513
+ sage: H = rshcd_from_prime_power_and_conference_matrix(7); H
3514
+ 36 x 36 dense matrix over Integer Ring (use the '.str()' method to see the entries)
3515
+ sage: H == H.T and is_hadamard_matrix(H) and H.diagonal() == [1]*36 and list(sum(H)) == [6]*36
3516
+ True
3517
+
3518
+ Bigger examples, only provided by this construction ::
3519
+
3520
+ sage: # needs database_graphs
3521
+ sage: H = rshcd_from_prime_power_and_conference_matrix(27) # long time
3522
+ sage: H == H.T and is_hadamard_matrix(H) # long time
3523
+ True
3524
+ sage: H.diagonal() == [1]*676 and list(sum(H)) == [26]*676 # long time
3525
+ True
3526
+
3527
+ In this example the conference matrix is not Paley, as 45 is not a prime power ::
3528
+
3529
+ sage: H = rshcd_from_prime_power_and_conference_matrix(47) # not tested (long time), needs database_graphs
3530
+
3531
+ REFERENCE:
3532
+
3533
+ - [WW1972]_
3534
+ """
3535
+ from sage.graphs.strongly_regular_db import strongly_regular_graph as srg
3536
+ if is_prime_power(n) and 2 == (n-1) % 4:
3537
+ try:
3538
+ M = srg(n-2, (n-3)//2, (n-7)//4)
3539
+ except ValueError:
3540
+ return
3541
+ m = (n-3)//4
3542
+ Q, X, Y = szekeres_difference_set_pair(m)
3543
+ B = typeI_matrix_difference_set(Q, X)
3544
+ A = -typeI_matrix_difference_set(Q, Y) # must be symmetric
3545
+ W = M.seidel_adjacency_matrix()
3546
+ f = J(1, 4*m+1)
3547
+ e = J(1, 2*m+1)
3548
+ JJ = J(2*m+1, 2*m+1)
3549
+ II = I(n-2)
3550
+ Ib = I(2*m+1)
3551
+ J4m = J(4*m+1, 4*m+1)
3552
+ H34 = -(B+Ib).tensor_product(W)+Ib.tensor_product(J4m)+(Ib-JJ).tensor_product(II)
3553
+ A_t_W = A.tensor_product(W)
3554
+ e_t_f = e.tensor_product(f)
3555
+ H = block_matrix([
3556
+ [J(1, 1), f, e_t_f, -e_t_f],
3557
+ [f.T, J4m, e.tensor_product(W-II), e.tensor_product(W+II)],
3558
+ [ e_t_f.T, (e.T).tensor_product(W-II), A_t_W+JJ.tensor_product(II), H34],
3559
+ [-e_t_f.T, (e.T).tensor_product(W+II), H34.T, -A_t_W+JJ.tensor_product(II)]])
3560
+ return H
3561
+
3562
+
3563
+ def are_amicable_hadamard_matrices(M, N, verbose=False):
3564
+ r"""
3565
+ Check if ``M`` and ``N`` are amicable Hadamard matrices.
3566
+
3567
+ Two matrices `M` and `N` of order `n` are called amicable if they
3568
+ satisfy the following conditions (see [Seb2017]_):
3569
+
3570
+ * `M` is a skew Hadamard matrix
3571
+ * `N` is a symmetric Hadamard matrix
3572
+ * `MN^T = NM^T`
3573
+
3574
+ INPUT:
3575
+
3576
+ - ``M`` -- a square matrix
3577
+ - ``N`` -- a square matrix
3578
+ - ``verbose`` -- boolean (default: ``False``); whether to be verbose when the
3579
+ matrices are not amicable Hadamard matrices
3580
+
3581
+ EXAMPLES::
3582
+
3583
+ sage: from sage.combinat.matrices.hadamard_matrix import are_amicable_hadamard_matrices
3584
+ sage: M = matrix([[1, 1], [-1, 1]])
3585
+ sage: N = matrix([[1, 1], [1, -1]])
3586
+ sage: are_amicable_hadamard_matrices(M, N)
3587
+ True
3588
+
3589
+ If ``verbose`` is true, the function will be verbose when returning False::
3590
+
3591
+ sage: N = matrix([[1, 1], [1, 1]])
3592
+ sage: are_amicable_hadamard_matrices(M, N, verbose=True)
3593
+ The second matrix is not Hadamard
3594
+ False
3595
+
3596
+ TESTS::
3597
+
3598
+ sage: N = matrix.hadamard(12)
3599
+ sage: are_amicable_hadamard_matrices(M, N)
3600
+ False
3601
+ """
3602
+ if not is_skew_hadamard_matrix(M):
3603
+ if verbose:
3604
+ print('The first matrix is not skew Hadamard')
3605
+ return False
3606
+
3607
+ if not is_hadamard_matrix(N):
3608
+ if verbose:
3609
+ print('The second matrix is not Hadamard')
3610
+ return False
3611
+
3612
+ if not N.is_symmetric():
3613
+ if verbose:
3614
+ print('The second matrix is not symmetric')
3615
+ return False
3616
+
3617
+ if len(M[0]) != len(N[0]):
3618
+ if verbose:
3619
+ print('M*N.transpose() is not equal to N*M.transpose()')
3620
+ return False
3621
+
3622
+ return True
3623
+
3624
+
3625
+ def amicable_hadamard_matrices_wallis(n, check=True):
3626
+ r"""
3627
+ Construct amicable Hadamard matrices of order `n = q + 1` where `q` is a prime power.
3628
+
3629
+ If `q` is a prime power `\equiv 3 \mod 4`, then amicable Hadamard matrices
3630
+ of order `q+1` can be constructed as described in [Wal1970b]_.
3631
+
3632
+ INPUT:
3633
+
3634
+ - ``n`` -- integer; the order of the matrices to be constructed
3635
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the
3636
+ resulting matrices are amicable Hadamard before returning them
3637
+
3638
+ OUTPUT:
3639
+
3640
+ The function returns two amicable Hadamard matrices, or raises an error if such
3641
+ matrices cannot be created using this construction.
3642
+
3643
+ EXAMPLES::
3644
+
3645
+ sage: from sage.combinat.matrices.hadamard_matrix import amicable_hadamard_matrices_wallis
3646
+ sage: M, N = amicable_hadamard_matrices_wallis(28)
3647
+
3648
+ TESTS::
3649
+
3650
+ sage: from sage.combinat.matrices.hadamard_matrix import are_amicable_hadamard_matrices
3651
+ sage: M, N = amicable_hadamard_matrices_wallis(20, check=False)
3652
+ sage: are_amicable_hadamard_matrices(M, N)
3653
+ True
3654
+ sage: amicable_hadamard_matrices_wallis(18)
3655
+ Traceback (most recent call last):
3656
+ ...
3657
+ ValueError: n must be a positive multiple of 4
3658
+ sage: amicable_hadamard_matrices_wallis(16)
3659
+ Traceback (most recent call last):
3660
+ ...
3661
+ ValueError: q = n-1 must be a prime power
3662
+ """
3663
+ if n % 4 != 0 or n < 0:
3664
+ raise ValueError('n must be a positive multiple of 4')
3665
+
3666
+ q = n - 1
3667
+ if not is_prime_power(q):
3668
+ raise ValueError('q = n-1 must be a prime power')
3669
+
3670
+ from sage.rings.finite_rings.finite_field_constructor import GF
3671
+
3672
+ G = GF(q)
3673
+
3674
+ ls1, ls2 = [], []
3675
+ elements_added = set()
3676
+ for el in G:
3677
+ if el == 0 or el in elements_added:
3678
+ continue
3679
+ elements_added.add(el)
3680
+ ls1.append(el)
3681
+ elements_added.add(-el)
3682
+ ls2 = [-el] + ls2
3683
+ Glist = [0] + ls1 + ls2
3684
+
3685
+ squares = []
3686
+ for el in Glist:
3687
+ squares.append(el*el)
3688
+
3689
+ def chi(el):
3690
+ if el == 0:
3691
+ return 0
3692
+ if el in squares:
3693
+ return 1
3694
+ return -1
3695
+
3696
+ S = matrix([[chi(Glist[i] - Glist[j]) for j in range(q)] for i in range(q)])
3697
+ R = matrix([[1 if (i, j) == (0, 0) else 1 if j == q-i else 0 for j in range(q)] for i in range(q)])
3698
+
3699
+ P = S + I(q)
3700
+ D = R + R*S
3701
+
3702
+ e = matrix([1 for _ in range(q)])
3703
+ one = matrix([1])
3704
+
3705
+ M = block_matrix([[ one, e],
3706
+ [-e.T, P]])
3707
+ N = block_matrix([[-one, -e],
3708
+ [-e.T, D]])
3709
+
3710
+ if check:
3711
+ assert are_amicable_hadamard_matrices(M, N)
3712
+ return M, N
3713
+
3714
+
3715
+ def amicable_hadamard_matrices(n, existence=False, check=True):
3716
+ r"""
3717
+ Construct amicable Hadamard matrices of order ``n`` using the available methods.
3718
+
3719
+ INPUT:
3720
+
3721
+ - ``n`` -- positive integer; the order of the amicable Hadamard matrices
3722
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only return
3723
+ whether amicable Hadamard matrices of order `n` can be constructed
3724
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the
3725
+ matrices are amicable Hadamard matrices before returning them
3726
+
3727
+ OUTPUT:
3728
+
3729
+ If ``existence`` is true, the function returns a boolean representing whether
3730
+ amicable Hadamard matrices of order `n` can be constructed.
3731
+ If ``existence`` is false, returns two amicable Hadamard matrices, or raises
3732
+ an error if the matrices cannot be constructed.
3733
+
3734
+ EXAMPLES::
3735
+
3736
+ sage: from sage.combinat.matrices.hadamard_matrix import amicable_hadamard_matrices
3737
+ sage: amicable_hadamard_matrices(2)
3738
+ (
3739
+ [ 1 1] [ 1 1]
3740
+ [-1 1], [ 1 -1]
3741
+ )
3742
+
3743
+ If ``existence`` is true, the function returns a boolean::
3744
+
3745
+ sage: amicable_hadamard_matrices(16, existence=True)
3746
+ False
3747
+
3748
+ TESTS::
3749
+
3750
+ sage: M, N = amicable_hadamard_matrices(20)
3751
+ sage: amicable_hadamard_matrices(18)
3752
+ Traceback (most recent call last):
3753
+ ...
3754
+ ValueError: Hadamard matrices of order 18 do not exist
3755
+ sage: amicable_hadamard_matrices(16)
3756
+ Traceback (most recent call last):
3757
+ ...
3758
+ NotImplementedError: construction for amicable Hadamard matrices of order 16 not yet implemented
3759
+ """
3760
+ if not ((n % 4 == 0 and n > 2) or n in [1, 2]):
3761
+ if existence:
3762
+ return False
3763
+ raise ValueError(f"Hadamard matrices of order {n} do not exist")
3764
+
3765
+ M = None
3766
+ N = None
3767
+ if n == 1:
3768
+ if existence:
3769
+ return True
3770
+ M = matrix([1])
3771
+ N = matrix([1])
3772
+ elif n == 2:
3773
+ if existence:
3774
+ return True
3775
+ M = matrix([[1, 1], [-1, 1]])
3776
+ N = matrix([[1, 1], [1, -1]])
3777
+ elif is_prime_power(n-1):
3778
+ if existence:
3779
+ return True
3780
+ M, N = amicable_hadamard_matrices_wallis(n, check=False)
3781
+
3782
+ if existence:
3783
+ return False
3784
+
3785
+ if M is None:
3786
+ raise NotImplementedError(f'construction for amicable Hadamard matrices of order {n} not yet implemented')
3787
+
3788
+ if check:
3789
+ assert are_amicable_hadamard_matrices(M, N)
3790
+ return M, N