def _helper_payley_matrix(n): r""" Return the marix constructed in Lemma 1.19 page 291 of [SWW72]_. This function return a `n^2` matrix `M` whose rows/columns are indexed by the element of a finite field on `n` elements `x_1,...,x_n`. The value `M_{i,j}` is equal to `\chi(x_i-x_j)`. Note that `n` must be an odd prime power. The elements `x_1,...,x_n` are ordered in such a way that the matrix is symmetric with respect to its second diagonal. The matrix is symmetric if n==4k+1, and skew-symmetric if n=4k-1. INPUT: - ``n`` -- a prime power .. SEEALSO:: :func:`rshcd_from_close_prime_powers` EXAMPLE:: sage: from sage.combinat.matrices.hadamard_matrix import _helper_payley_matrix sage: _helper_payley_matrix(5) [ 0 1 -1 -1 1] [ 1 0 1 -1 -1] [-1 1 0 1 -1] [-1 -1 1 0 1] [ 1 -1 -1 1 0] """ from sage.rings.finite_rings.constructor import FiniteField as GF K = GF(n,conway=True,prefix='x') # Order the elements of K in K_list # so that K_list[i] = -K_list[n-i-1] K_pairs = set(frozenset([x,-x]) for x in K) K_pairs.discard(frozenset([0])) K_list = [None]*n for i,(x,y) in enumerate(K_pairs): K_list[i] = x K_list[-i-1] = y K_list[n//2] = K(0) M = matrix(n,[[2*((x-y).is_square())-1 for x in K_list] for y in K_list]) M = M-I(n) assert (M*J(n)).is_zero() assert (M*M.transpose()) == n*I(n)-J(n) return M
def RSHCD_324(e): r""" Return a size 324x324 Regular Symmetric Hadamard Matrix with Constant Diagonal. We build the matrix `M` for the case `n=324`, `\epsilon=1` directly from :meth:`JankoKharaghaniTonchevGraph <sage.graphs.graph_generators.GraphGenerators.JankoKharaghaniTonchevGraph>` and for the case `\epsilon=-1` from the "twist" `M'` of `M`, using Lemma 11 in [HX10]_. Namely, it turns out that the matrix .. MATH:: M'=\begin{pmatrix} M_{12} & M_{11}\\ M_{11}^\top & M_{21} \end{pmatrix}, \quad\text{where}\quad M=\begin{pmatrix} M_{11} & M_{12}\\ M_{21} & M_{22} \end{pmatrix}, and the `M_{ij}` are 162x162-blocks, also RSHCD, its diagonal blocks having zero row sums, as needed by [loc.cit.]. Interestingly, the corresponding `(324,152,70,72)`-strongly regular graph has a vertex-transitive automorphism group of order 2592, twice the order of the (intransitive) automorphism group of the graph corresponding to `M`. Cf. [CP16]_. INPUT: - ``e`` -- one of `-1` or `+1`, equal to the value of `\epsilon` TESTS:: sage: from sage.combinat.matrices.hadamard_matrix import RSHCD_324, is_hadamard_matrix sage: for e in [1,-1]: # long time ....: M = RSHCD_324(e) ....: print("{} {} {}".format(M==M.T,is_hadamard_matrix(M),all([M[i,i]==1 for i in range(324)]))) ....: print(set(map(sum,M))) True True True set([18]) True True True set([-18]) REFERENCE: .. [CP16] \N. Cohen, D. Pasechnik, Implementing Brouwer's database of strongly regular graphs, Designs, Codes, and Cryptography, 2016 :doi:`10.1007/s10623-016-0264-x` """ from sage.graphs.generators.smallgraphs import JankoKharaghaniTonchevGraph as JKTG M = JKTG().adjacency_matrix() M = J(324) - 2 * M if e == -1: M1 = M[:162].T M2 = M[162:].T M11 = M1[:162] M12 = M1[162:].T M21 = M2[:162].T M = block_matrix([[M12, -M11], [-M11.T, M21]]) return M
def rshcd_from_close_prime_powers(n): r""" Return a `(n^2,1)`-RSHCD when `n-1` and `n+1` are odd prime powers and `n=0\pmod{4}`. The construction implemented here appears in Theorem 4.3 from [GS70]_. Note that the authors of [SWW72]_ claim in Corollary 5.12 (page 342) to have proved the same result without the `n=0\pmod{4}` restriction with a *very* similar construction. So far, however, I (Nathann Cohen) have not been able to make it work. INPUT: - ``n`` -- an integer congruent to `0\pmod{4}` .. SEEALSO:: :func:`regular_symmetric_hadamard_matrix_with_constant_diagonal` EXAMPLES:: sage: from sage.combinat.matrices.hadamard_matrix import rshcd_from_close_prime_powers sage: rshcd_from_close_prime_powers(4) [-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 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 -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 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 -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 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 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 -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 1 -1 1 1 -1 -1 -1 -1] [-1 1 -1 1 -1 -1 -1 -1 1 1 -1 1 1 -1 -1 -1] REFERENCE: .. [SWW72] A Street, W. Wallis, J. Wallis, Combinatorics: Room squares, sum-free sets, Hadamard matrices. Lecture notes in Mathematics 292 (1972). """ if n % 4: raise ValueError("n(={}) must be congruent to 0 mod 4") a, b = sorted([n - 1, n + 1], key=lambda x: -x % 4) Sa = _helper_payley_matrix(a) Sb = _helper_payley_matrix(b) U = matrix(a, [[int(i + j == a - 1) for i in range(a)] for j in range(a)]) K = (U * Sa).tensor_product(Sb) + U.tensor_product(J(b) - I(b)) - J( a).tensor_product(I(b)) F = lambda x: diagonal_matrix([-(-1)**i for i in range(x)]) G = block_diagonal_matrix([J(1), I(a).tensor_product(F(b))]) e = matrix(a * b, [1] * (a * b)) H = block_matrix(2, [-J(1), e.transpose(), e, K]) HH = G * H * G assert len(set(map(sum, HH))) == 1 assert HH**2 == n**2 * I(n**2) return HH
def _helper_payley_matrix(n, zero_position=True): r""" Return the matrix constructed in Lemma 1.19 page 291 of [SWW72]_. This function return a `n^2` matrix `M` whose rows/columns are indexed by the element of a finite field on `n` elements `x_1,...,x_n`. The value `M_{i,j}` is equal to `\chi(x_i-x_j)`. The elements `x_1,...,x_n` are ordered in such a way that the matrix (respectively, its submatrix obtained by removing first row and first column in the case ``zero_position=False``) is symmetric with respect to its second diagonal. The matrix is symmetric if `n=4k+1`, and skew-symmetric otherwise. INPUT: - ``n`` -- an odd prime power. - ``zero_position`` -- if it is true (default), place 0 of ``F_n`` in the middle, otherwise place it first. .. SEEALSO:: :func:`rshcd_from_close_prime_powers` EXAMPLES:: sage: from sage.combinat.matrices.hadamard_matrix import _helper_payley_matrix sage: _helper_payley_matrix(5) [ 0 1 -1 -1 1] [ 1 0 1 -1 -1] [-1 1 0 1 -1] [-1 -1 1 0 1] [ 1 -1 -1 1 0] TESTS:: sage: _helper_payley_matrix(11,zero_position=True) [ 0 -1 1 -1 -1 -1 1 1 1 -1 1] [ 1 0 -1 -1 1 -1 1 -1 1 1 -1] [-1 1 0 1 -1 -1 -1 -1 1 1 1] [ 1 1 -1 0 1 -1 -1 1 -1 -1 1] [ 1 -1 1 -1 0 1 -1 -1 -1 1 1] [ 1 1 1 1 -1 0 1 -1 -1 -1 -1] [-1 -1 1 1 1 -1 0 1 -1 1 -1] [-1 1 1 -1 1 1 -1 0 1 -1 -1] [-1 -1 -1 1 1 1 1 -1 0 -1 1] [ 1 -1 -1 1 -1 1 -1 1 1 0 -1] [-1 1 -1 -1 -1 1 1 1 -1 1 0] sage: _helper_payley_matrix(11,zero_position=False) [ 0 1 1 1 1 -1 1 -1 -1 -1 -1] [-1 0 -1 1 -1 -1 1 1 1 -1 1] [-1 1 0 -1 -1 1 1 -1 1 1 -1] [-1 -1 1 0 1 -1 -1 -1 1 1 1] [-1 1 1 -1 0 1 -1 1 -1 -1 1] [ 1 1 -1 1 -1 0 -1 -1 -1 1 1] [-1 -1 -1 1 1 1 0 1 -1 1 -1] [ 1 -1 1 1 -1 1 -1 0 1 -1 -1] [ 1 -1 -1 -1 1 1 1 -1 0 -1 1] [ 1 1 -1 -1 1 -1 -1 1 1 0 -1] [ 1 -1 1 -1 -1 -1 1 1 -1 1 0] """ from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF K = GF(n, prefix='x') # Order the elements of K in K_list # so that K_list[i] = -K_list[n-i-1] K_pairs = set(frozenset([x, -x]) for x in K) K_pairs.discard(frozenset([0])) K_list = [None] * n if zero_position: zero_position = n // 2 shift = 0 else: shift = 1 for i, (x, y) in enumerate(K_pairs): K_list[i + shift] = x K_list[-i - 1] = y K_list[zero_position] = K(0) M = matrix(n, [[2 * ((x - y).is_square()) - 1 for x in K_list] for y in K_list]) M = M - I(n) assert (M * J(n)).is_zero() assert (M * M.transpose()) == n * I(n) - J(n) return M
def regular_symmetric_hadamard_matrix_with_constant_diagonal( n, e, existence=False): r""" Return a Regular Symmetric Hadamard Matrix with Constant Diagonal. A Hadamard matrix is said to be *regular* if its rows all sum to the same value. For `\epsilon\in\{-1,+1\}`, we say that `M` is a `(n,\epsilon)-RSHCD` if `M` is a regular symmetric Hadamard matrix with constant diagonal `\delta\in\{-1,+1\}` and row sums all equal to `\delta \epsilon \sqrt(n)`. For more information, see [HX10]_ or 10.5.1 in [BH12]_. For the case `n=324`, see :func:`RSHCD_324` and [CP16]_. INPUT: - ``n`` (integer) -- side of the matrix - ``e`` -- one of `-1` or `+1`, equal to the value of `\epsilon` EXAMPLES:: sage: from sage.combinat.matrices.hadamard_matrix import regular_symmetric_hadamard_matrix_with_constant_diagonal sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(4,1) [ 1 1 1 -1] [ 1 1 -1 1] [ 1 -1 1 1] [-1 1 1 1] sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(4,-1) [ 1 -1 -1 -1] [-1 1 -1 -1] [-1 -1 1 -1] [-1 -1 -1 1] Other hardcoded values:: sage: for n,e in [(36,1),(36,-1),(100,1),(100,-1),(196, 1)]: # long time ....: print(repr(regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e))) 36 x 36 dense matrix over Integer Ring 36 x 36 dense matrix over Integer Ring 100 x 100 dense matrix over Integer Ring 100 x 100 dense matrix over Integer Ring 196 x 196 dense matrix over Integer Ring sage: for n,e in [(324,1),(324,-1)]: # not tested - long time, tested in RSHCD_324 ....: print(repr(regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e))) 324 x 324 dense matrix over Integer Ring 324 x 324 dense matrix over Integer Ring From two close prime powers:: sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(64,-1) 64 x 64 dense matrix over Integer Ring (use the '.str()' method to see the entries) From a prime power and a conference matrix:: sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(676,1) # long time 676 x 676 dense matrix over Integer Ring (use the '.str()' method to see the entries) Recursive construction:: sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(144,-1) 144 x 144 dense matrix over Integer Ring (use the '.str()' method to see the entries) REFERENCE: .. [BH12] \A. Brouwer and W. Haemers, Spectra of graphs, Springer, 2012, http://homepages.cwi.nl/~aeb/math/ipm/ipm.pdf .. [HX10] \W. Haemers and Q. Xiang, Strongly regular graphs with parameters `(4m^4,2m^4+m^2,m^4+m^2,m^4+m^2)` exist for all `m>1`, European Journal of Combinatorics, Volume 31, Issue 6, August 2010, Pages 1553-1559, :doi:`10.1016/j.ejc.2009.07.009` """ if existence and (n, e) in _rshcd_cache: return _rshcd_cache[n, e] from sage.graphs.strongly_regular_db import strongly_regular_graph def true(): _rshcd_cache[n, e] = True return True M = None if abs(e) != 1: raise ValueError sqn = None if is_square(n): sqn = int(sqrt(n)) if n < 0: if existence: return False raise ValueError elif n == 4: if existence: return true() if e == 1: M = J(4) - 2 * matrix(4, [[int(i + j == 3) for i in range(4)] for j in range(4)]) else: M = -J(4) + 2 * I(4) elif n == 36: if existence: return true() if e == 1: M = strongly_regular_graph(36, 15, 6, 6).adjacency_matrix() M = J(36) - 2 * M else: M = strongly_regular_graph(36, 14, 4, 6).adjacency_matrix() M = -J(36) + 2 * M + 2 * I(36) elif n == 100: if existence: return true() if e == -1: M = strongly_regular_graph(100, 44, 18, 20).adjacency_matrix() M = 2 * M - J(100) + 2 * I(100) else: M = strongly_regular_graph(100, 45, 20, 20).adjacency_matrix() M = J(100) - 2 * M elif n == 196 and e == 1: if existence: return true() M = strongly_regular_graph(196, 91, 42, 42).adjacency_matrix() M = J(196) - 2 * M elif n == 324: if existence: return true() M = RSHCD_324(e) elif (e == 1 and n % 16 == 0 and not sqn is None and is_prime_power(sqn - 1) and is_prime_power(sqn + 1)): if existence: return true() M = -rshcd_from_close_prime_powers(sqn) elif (e == 1 and not sqn is None and sqn % 4 == 2 and True == strongly_regular_graph(sqn - 1, (sqn - 2) // 2, (sqn - 6) // 4, existence=True) and is_prime_power(ZZ(sqn + 1))): if existence: return true() M = rshcd_from_prime_power_and_conference_matrix(sqn + 1) # Recursive construction: the kronecker product of two RSHCD is a RSHCD else: from itertools import product for n1, e1 in product(divisors(n)[1:-1], [-1, 1]): e2 = e1 * e n2 = n // n1 if (regular_symmetric_hadamard_matrix_with_constant_diagonal( n1, e1, existence=True) and regular_symmetric_hadamard_matrix_with_constant_diagonal( n2, e2, existence=True)): if existence: return true() M1 = regular_symmetric_hadamard_matrix_with_constant_diagonal( n1, e1) M2 = regular_symmetric_hadamard_matrix_with_constant_diagonal( n2, e2) M = M1.tensor_product(M2) break if M is None: from sage.misc.unknown import Unknown _rshcd_cache[n, e] = Unknown if existence: return Unknown raise ValueError("I do not know how to build a {}-RSHCD".format( (n, e))) assert M * M.transpose() == n * I(n) assert set(map(sum, M)) == {ZZ(e * sqn)} return M
def rshcd_from_prime_power_and_conference_matrix(n): r""" Return a `((n-1)^2,1)`-RSHCD if `n` is prime power, and symmetric `(n-1)`-conference matrix exists The construction implemented here is Theorem 16 (and Corollary 17) from [WW72]_. In [SWW72]_ this construction (Theorem 5.15 and Corollary 5.16) is reproduced with a typo. Note that [WW72]_ refers to [Sz69]_ for the construction, provided by :func:`szekeres_difference_set_pair`, of complementary difference sets, and the latter has a typo. From a :func:`symmetric_conference_matrix`, we only need the Seidel adjacency matrix of the underlying strongly regular conference (i.e. Paley type) graph, which we construct directly. INPUT: - ``n`` -- an integer .. SEEALSO:: :func:`regular_symmetric_hadamard_matrix_with_constant_diagonal` EXAMPLES: A 36x36 example :: sage: from sage.combinat.matrices.hadamard_matrix import rshcd_from_prime_power_and_conference_matrix sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix sage: H = rshcd_from_prime_power_and_conference_matrix(7); H 36 x 36 dense matrix over Integer Ring (use the '.str()' method to see the entries) sage: H==H.T and is_hadamard_matrix(H) and H.diagonal()==[1]*36 and list(sum(H))==[6]*36 True Bigger examples, only provided by this construction :: sage: H = rshcd_from_prime_power_and_conference_matrix(27) # long time sage: H == H.T and is_hadamard_matrix(H) # long time True sage: H.diagonal()==[1]*676 and list(sum(H))==[26]*676 # long time True In this example the conference matrix is not Paley, as 45 is not a prime power :: sage: H = rshcd_from_prime_power_and_conference_matrix(47) # not tested (long time) REFERENCE: .. [WW72] \J. Wallis and A.L. Whiteman, Some classes of Hadamard matrices with constant diagonal, Bull. Austral. Math. Soc. 7(1972), 233-249 """ from sage.graphs.strongly_regular_db import strongly_regular_graph as srg if is_prime_power(n) and 2 == (n - 1) % 4: try: M = srg(n - 2, (n - 3) // 2, (n - 7) // 4) except ValueError: return m = (n - 3) // 4 Q, X, Y = szekeres_difference_set_pair(m) B = typeI_matrix_difference_set(Q, X) A = -typeI_matrix_difference_set(Q, Y) # must be symmetric W = M.seidel_adjacency_matrix() f = J(1, 4 * m + 1) e = J(1, 2 * m + 1) JJ = J(2 * m + 1, 2 * m + 1) II = I(n - 2) Ib = I(2 * m + 1) J4m = J(4 * m + 1, 4 * m + 1) H34 = -(B + Ib).tensor_product(W) + Ib.tensor_product(J4m) + ( Ib - JJ).tensor_product(II) A_t_W = A.tensor_product(W) e_t_f = e.tensor_product(f) H = block_matrix( [[J(1, 1), f, e_t_f, -e_t_f], [f.T, J4m, e.tensor_product(W - II), e.tensor_product(W + II)], [ e_t_f.T, (e.T).tensor_product(W - II), A_t_W + JJ.tensor_product(II), H34 ], [ -e_t_f.T, (e.T).tensor_product(W + II), H34.T, -A_t_W + JJ.tensor_product(II) ]]) return H