passagemath-combinat 10.6.42__cp314-cp314-musllinux_1_2_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_combinat/__init__.py +3 -0
- passagemath_combinat-10.6.42.dist-info/METADATA +160 -0
- passagemath_combinat-10.6.42.dist-info/RECORD +400 -0
- passagemath_combinat-10.6.42.dist-info/WHEEL +5 -0
- passagemath_combinat-10.6.42.dist-info/top_level.txt +3 -0
- passagemath_combinat.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
- passagemath_combinat.libs/libsymmetrica-81fe8739.so.3.0.0 +0 -0
- sage/algebras/affine_nil_temperley_lieb.py +263 -0
- sage/algebras/all.py +24 -0
- sage/algebras/all__sagemath_combinat.py +35 -0
- sage/algebras/askey_wilson.py +935 -0
- sage/algebras/associated_graded.py +345 -0
- sage/algebras/cellular_basis.py +350 -0
- sage/algebras/cluster_algebra.py +2766 -0
- sage/algebras/down_up_algebra.py +860 -0
- sage/algebras/free_algebra.py +1698 -0
- sage/algebras/free_algebra_element.py +345 -0
- sage/algebras/free_algebra_quotient.py +405 -0
- sage/algebras/free_algebra_quotient_element.py +295 -0
- sage/algebras/free_zinbiel_algebra.py +885 -0
- sage/algebras/hall_algebra.py +783 -0
- sage/algebras/hecke_algebras/all.py +4 -0
- sage/algebras/hecke_algebras/ariki_koike_algebra.py +1796 -0
- sage/algebras/hecke_algebras/ariki_koike_specht_modules.py +475 -0
- sage/algebras/hecke_algebras/cubic_hecke_algebra.py +3520 -0
- sage/algebras/hecke_algebras/cubic_hecke_base_ring.py +1473 -0
- sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py +1079 -0
- sage/algebras/iwahori_hecke_algebra.py +3095 -0
- sage/algebras/jordan_algebra.py +1773 -0
- sage/algebras/lie_conformal_algebras/abelian_lie_conformal_algebra.py +113 -0
- sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py +156 -0
- sage/algebras/lie_conformal_algebras/all.py +18 -0
- sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra.py +134 -0
- sage/algebras/lie_conformal_algebras/examples.py +43 -0
- sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra.py +131 -0
- sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py +139 -0
- sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py +174 -0
- sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py +167 -0
- sage/algebras/lie_conformal_algebras/freely_generated_lie_conformal_algebra.py +107 -0
- sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py +135 -0
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py +353 -0
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +236 -0
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py +78 -0
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py +328 -0
- sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra.py +117 -0
- sage/algebras/lie_conformal_algebras/neveu_schwarz_lie_conformal_algebra.py +86 -0
- sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra.py +82 -0
- sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra.py +205 -0
- sage/algebras/nil_coxeter_algebra.py +191 -0
- sage/algebras/q_commuting_polynomials.py +673 -0
- sage/algebras/q_system.py +608 -0
- sage/algebras/quantum_clifford.py +959 -0
- sage/algebras/quantum_groups/ace_quantum_onsager.py +693 -0
- sage/algebras/quantum_groups/all.py +9 -0
- sage/algebras/quantum_groups/fock_space.py +2219 -0
- sage/algebras/quantum_groups/q_numbers.py +207 -0
- sage/algebras/quantum_groups/quantum_group_gap.py +2695 -0
- sage/algebras/quantum_groups/representations.py +591 -0
- sage/algebras/quantum_matrix_coordinate_algebra.py +1006 -0
- sage/algebras/quantum_oscillator.py +623 -0
- sage/algebras/quaternion_algebra.py +20 -0
- sage/algebras/quaternion_algebra_element.py +55 -0
- sage/algebras/rational_cherednik_algebra.py +525 -0
- sage/algebras/schur_algebra.py +670 -0
- sage/algebras/shuffle_algebra.py +1011 -0
- sage/algebras/splitting_algebra.py +779 -0
- sage/algebras/tensor_algebra.py +709 -0
- sage/algebras/yangian.py +1082 -0
- sage/algebras/yokonuma_hecke_algebra.py +1018 -0
- sage/all__sagemath_combinat.py +35 -0
- sage/combinat/SJT.py +255 -0
- sage/combinat/affine_permutation.py +2405 -0
- sage/combinat/algebraic_combinatorics.py +55 -0
- sage/combinat/all.py +53 -0
- sage/combinat/all__sagemath_combinat.py +195 -0
- sage/combinat/alternating_sign_matrix.py +2063 -0
- sage/combinat/baxter_permutations.py +346 -0
- sage/combinat/bijectionist.py +3220 -0
- sage/combinat/binary_recurrence_sequences.py +1180 -0
- sage/combinat/blob_algebra.py +685 -0
- sage/combinat/catalog_partitions.py +27 -0
- sage/combinat/chas/all.py +23 -0
- sage/combinat/chas/fsym.py +1180 -0
- sage/combinat/chas/wqsym.py +2601 -0
- sage/combinat/cluster_complex.py +326 -0
- sage/combinat/colored_permutations.py +2039 -0
- sage/combinat/colored_permutations_representations.py +964 -0
- sage/combinat/composition_signed.py +142 -0
- sage/combinat/composition_tableau.py +855 -0
- sage/combinat/constellation.py +1729 -0
- sage/combinat/core.py +751 -0
- sage/combinat/counting.py +12 -0
- sage/combinat/crystals/affine.py +742 -0
- sage/combinat/crystals/affine_factorization.py +518 -0
- sage/combinat/crystals/affinization.py +331 -0
- sage/combinat/crystals/alcove_path.py +2013 -0
- sage/combinat/crystals/all.py +22 -0
- sage/combinat/crystals/bkk_crystals.py +141 -0
- sage/combinat/crystals/catalog.py +115 -0
- sage/combinat/crystals/catalog_elementary_crystals.py +18 -0
- sage/combinat/crystals/catalog_infinity_crystals.py +33 -0
- sage/combinat/crystals/catalog_kirillov_reshetikhin.py +18 -0
- sage/combinat/crystals/crystals.py +257 -0
- sage/combinat/crystals/direct_sum.py +260 -0
- sage/combinat/crystals/elementary_crystals.py +1251 -0
- sage/combinat/crystals/fast_crystals.py +441 -0
- sage/combinat/crystals/fully_commutative_stable_grothendieck.py +1205 -0
- sage/combinat/crystals/generalized_young_walls.py +1076 -0
- sage/combinat/crystals/highest_weight_crystals.py +436 -0
- sage/combinat/crystals/induced_structure.py +695 -0
- sage/combinat/crystals/infinity_crystals.py +730 -0
- sage/combinat/crystals/kac_modules.py +863 -0
- sage/combinat/crystals/kirillov_reshetikhin.py +4196 -0
- sage/combinat/crystals/kyoto_path_model.py +497 -0
- sage/combinat/crystals/letters.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/crystals/letters.pxd +79 -0
- sage/combinat/crystals/letters.pyx +3056 -0
- sage/combinat/crystals/littelmann_path.py +1518 -0
- sage/combinat/crystals/monomial_crystals.py +1262 -0
- sage/combinat/crystals/multisegments.py +462 -0
- sage/combinat/crystals/mv_polytopes.py +467 -0
- sage/combinat/crystals/pbw_crystal.py +511 -0
- sage/combinat/crystals/pbw_datum.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/crystals/pbw_datum.pxd +4 -0
- sage/combinat/crystals/pbw_datum.pyx +487 -0
- sage/combinat/crystals/polyhedral_realization.py +372 -0
- sage/combinat/crystals/spins.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/crystals/spins.pxd +21 -0
- sage/combinat/crystals/spins.pyx +756 -0
- sage/combinat/crystals/star_crystal.py +290 -0
- sage/combinat/crystals/subcrystal.py +464 -0
- sage/combinat/crystals/tensor_product.py +1177 -0
- sage/combinat/crystals/tensor_product_element.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/crystals/tensor_product_element.pxd +35 -0
- sage/combinat/crystals/tensor_product_element.pyx +1870 -0
- sage/combinat/crystals/virtual_crystal.py +420 -0
- sage/combinat/cyclic_sieving_phenomenon.py +204 -0
- sage/combinat/debruijn_sequence.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/debruijn_sequence.pyx +355 -0
- sage/combinat/decorated_permutation.py +270 -0
- sage/combinat/degree_sequences.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/degree_sequences.pyx +588 -0
- sage/combinat/derangements.py +527 -0
- sage/combinat/descent_algebra.py +1008 -0
- sage/combinat/diagram.py +1551 -0
- sage/combinat/diagram_algebras.py +5886 -0
- sage/combinat/dyck_word.py +4349 -0
- sage/combinat/e_one_star.py +1623 -0
- sage/combinat/enumerated_sets.py +123 -0
- sage/combinat/expnums.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/expnums.pyx +148 -0
- sage/combinat/fast_vector_partitions.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/fast_vector_partitions.pyx +346 -0
- sage/combinat/fqsym.py +1977 -0
- sage/combinat/free_dendriform_algebra.py +954 -0
- sage/combinat/free_prelie_algebra.py +1141 -0
- sage/combinat/fully_commutative_elements.py +1077 -0
- sage/combinat/fully_packed_loop.py +1523 -0
- sage/combinat/gelfand_tsetlin_patterns.py +1409 -0
- sage/combinat/gray_codes.py +311 -0
- sage/combinat/grossman_larson_algebras.py +667 -0
- sage/combinat/growth.py +4352 -0
- sage/combinat/hall_polynomial.py +188 -0
- sage/combinat/hillman_grassl.py +866 -0
- sage/combinat/integer_matrices.py +329 -0
- sage/combinat/integer_vectors_mod_permgroup.py +1238 -0
- sage/combinat/k_tableau.py +4564 -0
- sage/combinat/kazhdan_lusztig.py +215 -0
- sage/combinat/key_polynomial.py +885 -0
- sage/combinat/knutson_tao_puzzles.py +2286 -0
- sage/combinat/lr_tableau.py +311 -0
- sage/combinat/matrices/all.py +24 -0
- sage/combinat/matrices/hadamard_matrix.py +3790 -0
- sage/combinat/matrices/latin.py +2912 -0
- sage/combinat/misc.py +401 -0
- sage/combinat/multiset_partition_into_sets_ordered.py +3541 -0
- sage/combinat/ncsf_qsym/all.py +21 -0
- sage/combinat/ncsf_qsym/combinatorics.py +317 -0
- sage/combinat/ncsf_qsym/generic_basis_code.py +1427 -0
- sage/combinat/ncsf_qsym/ncsf.py +5637 -0
- sage/combinat/ncsf_qsym/qsym.py +4053 -0
- sage/combinat/ncsf_qsym/tutorial.py +447 -0
- sage/combinat/ncsym/all.py +21 -0
- sage/combinat/ncsym/bases.py +855 -0
- sage/combinat/ncsym/dual.py +593 -0
- sage/combinat/ncsym/ncsym.py +2076 -0
- sage/combinat/necklace.py +551 -0
- sage/combinat/non_decreasing_parking_function.py +634 -0
- sage/combinat/nu_dyck_word.py +1474 -0
- sage/combinat/output.py +861 -0
- sage/combinat/parallelogram_polyomino.py +4326 -0
- sage/combinat/parking_functions.py +1602 -0
- sage/combinat/partition_algebra.py +1998 -0
- sage/combinat/partition_kleshchev.py +1982 -0
- sage/combinat/partition_shifting_algebras.py +584 -0
- sage/combinat/partition_tuple.py +3114 -0
- sage/combinat/path_tableaux/all.py +13 -0
- sage/combinat/path_tableaux/catalog.py +29 -0
- sage/combinat/path_tableaux/dyck_path.py +380 -0
- sage/combinat/path_tableaux/frieze.py +476 -0
- sage/combinat/path_tableaux/path_tableau.py +728 -0
- sage/combinat/path_tableaux/semistandard.py +510 -0
- sage/combinat/perfect_matching.py +779 -0
- sage/combinat/plane_partition.py +3300 -0
- sage/combinat/q_bernoulli.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/q_bernoulli.pyx +128 -0
- sage/combinat/quickref.py +81 -0
- sage/combinat/recognizable_series.py +2051 -0
- sage/combinat/regular_sequence.py +4316 -0
- sage/combinat/regular_sequence_bounded.py +543 -0
- sage/combinat/restricted_growth.py +81 -0
- sage/combinat/ribbon.py +20 -0
- sage/combinat/ribbon_shaped_tableau.py +489 -0
- sage/combinat/ribbon_tableau.py +1180 -0
- sage/combinat/rigged_configurations/all.py +46 -0
- sage/combinat/rigged_configurations/bij_abstract_class.py +548 -0
- sage/combinat/rigged_configurations/bij_infinity.py +370 -0
- sage/combinat/rigged_configurations/bij_type_A.py +163 -0
- sage/combinat/rigged_configurations/bij_type_A2_dual.py +338 -0
- sage/combinat/rigged_configurations/bij_type_A2_even.py +218 -0
- sage/combinat/rigged_configurations/bij_type_A2_odd.py +199 -0
- sage/combinat/rigged_configurations/bij_type_B.py +900 -0
- sage/combinat/rigged_configurations/bij_type_C.py +267 -0
- sage/combinat/rigged_configurations/bij_type_D.py +771 -0
- sage/combinat/rigged_configurations/bij_type_D_tri.py +392 -0
- sage/combinat/rigged_configurations/bij_type_D_twisted.py +576 -0
- sage/combinat/rigged_configurations/bij_type_E67.py +402 -0
- sage/combinat/rigged_configurations/bijection.py +143 -0
- sage/combinat/rigged_configurations/kleber_tree.py +1475 -0
- sage/combinat/rigged_configurations/kr_tableaux.py +1898 -0
- sage/combinat/rigged_configurations/rc_crystal.py +461 -0
- sage/combinat/rigged_configurations/rc_infinity.py +540 -0
- sage/combinat/rigged_configurations/rigged_configuration_element.py +2403 -0
- sage/combinat/rigged_configurations/rigged_configurations.py +1918 -0
- sage/combinat/rigged_configurations/rigged_partition.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/rigged_configurations/rigged_partition.pxd +15 -0
- sage/combinat/rigged_configurations/rigged_partition.pyx +680 -0
- sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +499 -0
- sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py +428 -0
- sage/combinat/rsk.py +3438 -0
- sage/combinat/schubert_polynomial.py +508 -0
- sage/combinat/set_partition.py +3318 -0
- sage/combinat/set_partition_iterator.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/set_partition_iterator.pyx +136 -0
- sage/combinat/set_partition_ordered.py +1590 -0
- sage/combinat/sf/abreu_nigro.py +346 -0
- sage/combinat/sf/all.py +52 -0
- sage/combinat/sf/character.py +576 -0
- sage/combinat/sf/classical.py +319 -0
- sage/combinat/sf/dual.py +996 -0
- sage/combinat/sf/elementary.py +549 -0
- sage/combinat/sf/hall_littlewood.py +1028 -0
- sage/combinat/sf/hecke.py +336 -0
- sage/combinat/sf/homogeneous.py +464 -0
- sage/combinat/sf/jack.py +1428 -0
- sage/combinat/sf/k_dual.py +1458 -0
- sage/combinat/sf/kfpoly.py +447 -0
- sage/combinat/sf/llt.py +789 -0
- sage/combinat/sf/macdonald.py +2019 -0
- sage/combinat/sf/monomial.py +525 -0
- sage/combinat/sf/multiplicative.py +113 -0
- sage/combinat/sf/new_kschur.py +1786 -0
- sage/combinat/sf/ns_macdonald.py +964 -0
- sage/combinat/sf/orthogonal.py +246 -0
- sage/combinat/sf/orthotriang.py +355 -0
- sage/combinat/sf/powersum.py +963 -0
- sage/combinat/sf/schur.py +880 -0
- sage/combinat/sf/sf.py +1653 -0
- sage/combinat/sf/sfa.py +7053 -0
- sage/combinat/sf/symplectic.py +253 -0
- sage/combinat/sf/witt.py +721 -0
- sage/combinat/shifted_primed_tableau.py +2735 -0
- sage/combinat/shuffle.py +830 -0
- sage/combinat/sidon_sets.py +146 -0
- sage/combinat/similarity_class_type.py +1721 -0
- sage/combinat/sine_gordon.py +618 -0
- sage/combinat/six_vertex_model.py +784 -0
- sage/combinat/skew_partition.py +2053 -0
- sage/combinat/skew_tableau.py +2989 -0
- sage/combinat/sloane_functions.py +8935 -0
- sage/combinat/specht_module.py +1403 -0
- sage/combinat/species/all.py +48 -0
- sage/combinat/species/characteristic_species.py +321 -0
- sage/combinat/species/composition_species.py +273 -0
- sage/combinat/species/cycle_species.py +284 -0
- sage/combinat/species/empty_species.py +155 -0
- sage/combinat/species/functorial_composition_species.py +148 -0
- sage/combinat/species/generating_series.py +673 -0
- sage/combinat/species/library.py +148 -0
- sage/combinat/species/linear_order_species.py +169 -0
- sage/combinat/species/misc.py +83 -0
- sage/combinat/species/partition_species.py +290 -0
- sage/combinat/species/permutation_species.py +268 -0
- sage/combinat/species/product_species.py +423 -0
- sage/combinat/species/recursive_species.py +476 -0
- sage/combinat/species/set_species.py +192 -0
- sage/combinat/species/species.py +820 -0
- sage/combinat/species/structure.py +539 -0
- sage/combinat/species/subset_species.py +243 -0
- sage/combinat/species/sum_species.py +225 -0
- sage/combinat/subword.py +564 -0
- sage/combinat/subword_complex.py +2122 -0
- sage/combinat/subword_complex_c.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/subword_complex_c.pyx +119 -0
- sage/combinat/super_tableau.py +821 -0
- sage/combinat/superpartition.py +1154 -0
- sage/combinat/symmetric_group_algebra.py +3774 -0
- sage/combinat/symmetric_group_representations.py +1830 -0
- sage/combinat/t_sequences.py +877 -0
- sage/combinat/tableau.py +9506 -0
- sage/combinat/tableau_residues.py +860 -0
- sage/combinat/tableau_tuple.py +5353 -0
- sage/combinat/tiling.py +2432 -0
- sage/combinat/triangles_FHM.py +777 -0
- sage/combinat/tutorial.py +1857 -0
- sage/combinat/vector_partition.py +337 -0
- sage/combinat/words/abstract_word.py +1722 -0
- sage/combinat/words/all.py +59 -0
- sage/combinat/words/alphabet.py +268 -0
- sage/combinat/words/finite_word.py +7201 -0
- sage/combinat/words/infinite_word.py +113 -0
- sage/combinat/words/lyndon_word.py +652 -0
- sage/combinat/words/morphic.py +351 -0
- sage/combinat/words/morphism.py +3878 -0
- sage/combinat/words/paths.py +2932 -0
- sage/combinat/words/shuffle_product.py +278 -0
- sage/combinat/words/suffix_trees.py +1873 -0
- sage/combinat/words/word.py +769 -0
- sage/combinat/words/word_char.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/words/word_char.pyx +847 -0
- sage/combinat/words/word_datatypes.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/words/word_datatypes.pxd +4 -0
- sage/combinat/words/word_datatypes.pyx +1067 -0
- sage/combinat/words/word_generators.py +2026 -0
- sage/combinat/words/word_infinite_datatypes.py +1218 -0
- sage/combinat/words/word_options.py +99 -0
- sage/combinat/words/words.py +2396 -0
- sage/data_structures/all__sagemath_combinat.py +1 -0
- sage/databases/all__sagemath_combinat.py +13 -0
- sage/databases/findstat.py +4897 -0
- sage/databases/oeis.py +2058 -0
- sage/databases/sloane.py +393 -0
- sage/dynamics/all__sagemath_combinat.py +14 -0
- sage/dynamics/cellular_automata/all.py +7 -0
- sage/dynamics/cellular_automata/catalog.py +34 -0
- sage/dynamics/cellular_automata/elementary.py +612 -0
- sage/dynamics/cellular_automata/glca.py +477 -0
- sage/dynamics/cellular_automata/solitons.py +1463 -0
- sage/dynamics/finite_dynamical_system.py +1249 -0
- sage/dynamics/finite_dynamical_system_catalog.py +382 -0
- sage/games/all.py +7 -0
- sage/games/hexad.py +704 -0
- sage/games/quantumino.py +591 -0
- sage/games/sudoku.py +889 -0
- sage/games/sudoku_backtrack.cpython-314-x86_64-linux-musl.so +0 -0
- sage/games/sudoku_backtrack.pyx +189 -0
- sage/groups/all__sagemath_combinat.py +1 -0
- sage/groups/indexed_free_group.py +489 -0
- sage/libs/all__sagemath_combinat.py +6 -0
- sage/libs/lrcalc/__init__.py +1 -0
- sage/libs/lrcalc/lrcalc.py +525 -0
- sage/libs/symmetrica/__init__.py +7 -0
- sage/libs/symmetrica/all.py +101 -0
- sage/libs/symmetrica/kostka.pxi +168 -0
- sage/libs/symmetrica/part.pxi +193 -0
- sage/libs/symmetrica/plet.pxi +42 -0
- sage/libs/symmetrica/sab.pxi +196 -0
- sage/libs/symmetrica/sb.pxi +332 -0
- sage/libs/symmetrica/sc.pxi +192 -0
- sage/libs/symmetrica/schur.pxi +956 -0
- sage/libs/symmetrica/symmetrica.cpython-314-x86_64-linux-musl.so +0 -0
- sage/libs/symmetrica/symmetrica.pxi +1172 -0
- sage/libs/symmetrica/symmetrica.pyx +39 -0
- sage/monoids/all.py +13 -0
- sage/monoids/automatic_semigroup.py +1054 -0
- sage/monoids/free_abelian_monoid.py +315 -0
- sage/monoids/free_abelian_monoid_element.cpython-314-x86_64-linux-musl.so +0 -0
- sage/monoids/free_abelian_monoid_element.pxd +16 -0
- sage/monoids/free_abelian_monoid_element.pyx +397 -0
- sage/monoids/free_monoid.py +335 -0
- sage/monoids/free_monoid_element.py +431 -0
- sage/monoids/hecke_monoid.py +65 -0
- sage/monoids/string_monoid.py +817 -0
- sage/monoids/string_monoid_element.py +547 -0
- sage/monoids/string_ops.py +143 -0
- sage/monoids/trace_monoid.py +972 -0
- sage/rings/all__sagemath_combinat.py +2 -0
- sage/sat/all.py +4 -0
- sage/sat/boolean_polynomials.py +405 -0
- sage/sat/converters/__init__.py +6 -0
- sage/sat/converters/anf2cnf.py +14 -0
- sage/sat/converters/polybori.py +611 -0
- sage/sat/solvers/__init__.py +5 -0
- sage/sat/solvers/cryptominisat.py +287 -0
- sage/sat/solvers/dimacs.py +783 -0
- sage/sat/solvers/picosat.py +228 -0
- sage/sat/solvers/sat_lp.py +156 -0
- sage/sat/solvers/satsolver.cpython-314-x86_64-linux-musl.so +0 -0
- sage/sat/solvers/satsolver.pxd +3 -0
- 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
|