From 992bf2ef53e26442b8e1003929c664d2a5b1ca6a Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Tue, 6 Dec 2022 10:47:50 +0100 Subject: [PATCH 01/19] Add construction for m-sequences --- src/doc/en/reference/references/index.rst | 7 ++ .../combinat/designs/difference_family.py | 101 ++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 0ebf28466b1..3160f3f6b23 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -4418,6 +4418,9 @@ REFERENCES: .. [MirMor2009] \R. Miranda, D.R. Morrison, "Embeddings of Integral Quadratic Forms" http://www.math.ucsb.edu/~drm/manuscripts/eiqf.pdf . +.. [Mit2008] \A. Mitra. *On the construction of M-sequences via primitive polynomials with a fast identification method*, + International Journal of Electronics and Communication Engineering 2(9) (2008): 1991-1996. + .. [MKO1998] Hans Munthe--Kaas and Brynjulf Owren. *Computations in a free Lie algebra*. (1998). `Downloadable from Munthe-Kaas's website @@ -6233,6 +6236,10 @@ REFERENCES: Non-Cayley Graphs of Order 8p*. The Electronic Journal of Combinatorics, 19(1), P53, 2012. :doi:`10.37236/2087` +.. [Zie1959] \N. Zierler. *Linear Recurring Sequences*. + Journal of the Society for Industrial and Applied Mathematics 7(1) + (1959): 31-48. :doi:`10.1137/0107003` + .. [Zie1998] \G. M. Ziegler. *Shelling polyhedral 3-balls and 4-polytopes*. Discrete Comput. Geom. 19 (1998), 159-174. :doi:`10.1007/PL00009339` diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index 529d4908c15..0474c8c2345 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -48,6 +48,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** +from sage.arith.misc import is_prime, is_prime_power from sage.misc.cachefunc import cached_function from sage.categories.sets_cat import EmptySetError @@ -55,6 +56,7 @@ from sage.misc.unknown import Unknown from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing def group_law(G): @@ -1263,6 +1265,105 @@ def turyn_1965_3x3xK(k=4): return G, [[G(v + k) for l, k in zip(L, K) for v in l]] +def _is_periodic_sequence(seq, period): + """Check if the sequence is periodic with correct period. + + The sequence should have length at least twice the period, so that + periodicity can be checked. + + INPUT: + + - ``seq`` -- the sequence to be tested (must have length at least twice the period). + + - ``period`` -- integer, the period that the sequence should have. + + EXAMPLES: + + sage: from sage.combinat.designs.difference_family import _is_periodic_sequence + sage: _is_periodic_sequence([0, 1, 2, 3, 0, 1, 2, 3], 4) + True + sage: _is_periodic_sequence([0, 1, 0, 1, 0, 1, 0, 1], 4) + False + sage: _is_periodic_sequence([0, 1, 1, 1, 0, 1, 2, 1], 4) + False + """ + assert len(seq) >= 2*period + + for per in range(1, period): + first = seq[:per] + periodic = True + for j in range(1, len(seq)//per): + if seq[j*per:(j+1)*per] != first: + periodic = False + break + if periodic: + return False + if seq[:period] != seq[period:2*period]: + return False + return True + +def _create_m_sequence(q, n, check=True): + """Create an m-sequence over GF(q) with period `q^n-1`. + + Given a prime power `q`, the m-sequence is created as described by [Zie1959]_ + from a primitive function over the finite field `GF(q)`. + + Given a primitive function `f=c_0+c_1x+...+c_nx^n` over `K=GF(q)` of degree `n`, + the recurrence is given by: `a_i = -c_0^{-1}(c_1a_{i-1}+...+c_na{i-n}). + The first n elements will be `0,0,...,0,1` and these will give a maximal length recurrence sequence + as shown in [Mit2008]_. + + INPUT: + + - ``q`` -- a prime power. + + - ``n`` -- a nonnegative number. + + - ``check`` -- boolean (dafault True): if true, check that the result is a seqauence with correct period. + Setting it to false may speed up considerably the computation. + + EXAMPLES: + sage: from sage.combinat.designs.difference_family import _create_m_sequence + sage: _create_m_sequence(3, 2) #random + [1, 0, 1, 2, 2, 0, 2, 1] + sage: _create_m_sequence(4, 2, check=False) #random + [1, 0, a, a + 1, a, a, 0, a + 1, 1, a + 1, a + 1, 0, 1, a, 1] + sage: _create_m_sequence(6, 2) + Traceback (most recent call last): + ... + ValueError: q must be a prime power + + """ + from sage.rings.finite_rings.finite_field_constructor import GF + + if not is_prime_power(q): + raise ValueError('q must be a prime power') + if n < 0: + raise ValueError('n cannot be negative') + + K = GF(q, 'a') + + T = PolynomialRing(K, 'x') + primitive = T.irreducible_element(n, algorithm='random') + while not primitive.is_primitive(): + primitive = T.irreducible_element(n, algorithm='random') + coeffs = primitive.coefficients() + exps = primitive.exponents() + + period = q**n - 1 + seq_len = period*2 if check else period + seq = [1]+[0]*(n-1) + + while len(seq) < seq_len: + nxt = 0 + for i, coeff in zip(exps[1:], coeffs[1:]): + nxt += coeff*seq[-i] + seq.append(-coeffs[0].inverse()*nxt) + + if check: + assert _is_periodic_sequence(seq, period) + return seq[:period] + def difference_family(v, k, l=1, existence=False, explain_construction=False, check=True): r""" Return a (``k``, ``l``)-difference family on an Abelian group of cardinality ``v``. From 5d76f849f6c6500deccc4002f06ca49d1b512af0 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Tue, 6 Dec 2022 12:24:29 +0100 Subject: [PATCH 02/19] Add construction for relative difference sets from m-sequences --- src/doc/en/reference/references/index.rst | 5 + .../combinat/designs/difference_family.py | 179 +++++++++++++++++- 2 files changed, 179 insertions(+), 5 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 3160f3f6b23..946bb33b85d 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -2182,6 +2182,11 @@ REFERENCES: .. [Early2017] Nick Early. *Canonical bases for permutohedral plates*. Preprint (2017). :arxiv:`1712.08520v3`. +.. [EB1966] \J. Elliot and A. Butson. + *Relative difference sets*, + Illinois Journal of Mathematics 10(3) (1966): 517-531. + :doi:`10.1215/ijm/1256055004` + .. [Eb1989] \W. Eberly, "Computations for algebras and group representations". Ph.D. Thesis, University of Toronto, 1989. http://www.cpsc.ucalgary.ca/~eberly/Research/Papers/phdthesis.pdf diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index 0474c8c2345..32cff0146bf 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -48,7 +48,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.arith.misc import is_prime, is_prime_power +from sage.arith.misc import is_prime_power from sage.misc.cachefunc import cached_function from sage.categories.sets_cat import EmptySetError @@ -1266,7 +1266,7 @@ def turyn_1965_3x3xK(k=4): def _is_periodic_sequence(seq, period): - """Check if the sequence is periodic with correct period. + r"""Check if the sequence is periodic with correct period. The sequence should have length at least twice the period, so that periodicity can be checked. @@ -1277,7 +1277,7 @@ def _is_periodic_sequence(seq, period): - ``period`` -- integer, the period that the sequence should have. - EXAMPLES: + EXAMPLES:: sage: from sage.combinat.designs.difference_family import _is_periodic_sequence sage: _is_periodic_sequence([0, 1, 2, 3, 0, 1, 2, 3], 4) @@ -1303,7 +1303,7 @@ def _is_periodic_sequence(seq, period): return True def _create_m_sequence(q, n, check=True): - """Create an m-sequence over GF(q) with period `q^n-1`. + r"""Create an m-sequence over GF(q) with period `q^n-1`. Given a prime power `q`, the m-sequence is created as described by [Zie1959]_ from a primitive function over the finite field `GF(q)`. @@ -1322,7 +1322,8 @@ def _create_m_sequence(q, n, check=True): - ``check`` -- boolean (dafault True): if true, check that the result is a seqauence with correct period. Setting it to false may speed up considerably the computation. - EXAMPLES: + EXAMPLES:: + sage: from sage.combinat.designs.difference_family import _create_m_sequence sage: _create_m_sequence(3, 2) #random [1, 0, 1, 2, 2, 0, 2, 1] @@ -1364,6 +1365,174 @@ def _create_m_sequence(q, n, check=True): assert _is_periodic_sequence(seq, period) return seq[:period] +def _get_submodule_of_order(G, order): + r"""Construct a submodule of the given order from group `G`. + + This method tries to construct submodules from various elements of `G` until + a submodule of the correct order is found. + + INPUT: + + - ``G`` --an additive abelian group. + + - ``order`` -- integer, the order of the desired syubmodule. + + TESTS: + + sage: from sage.combinat.designs.difference_family import _get_submodule_of_order + sage: G = AdditiveAbelianGroup([48]) + sage: _get_submodule_of_order(G, 6).order() + 6 + sage: G = AdditiveAbelianGroup([13^2-1]) + sage: _get_submodule_of_order(G, 12).order() + 12 + """ + for el in G: + H = G.submodule([el]) + if H.order() == order: + return H + return None + +def relative_difference_set_from_m_sequence(q, N, check=True): + r"""Construct `R((q^N-1)/(q-1), q-1, q^{N-1}, q^{N-2})` where q is a prime power and `N\ge 2`. + + The relative difference set is constructed over the set of additive integers modulo `q^N-1`, + as described in Theorem 5.1 of [EB1966]_. Given an m-sequence `(a_i)` of period `q^N-1`, the + set is: `R=\{i | 0 \le i \le q^{N-1}, a_i=1\}`. + + INPUT: + + - ``q`` -- a prime power. + + - ``N`` -- a nonegative number. + + - ``check`` -- boolean (default True). If true, check that the result is a relative difference + set before returning it. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import relative_difference_set_from_m_sequence + sage: relative_difference_set_from_m_sequence(2, 4) #random + [(0), (4), (5), (6), (7), (9), (11), (12)] + sage: relative_difference_set_from_m_sequence(8, 2, check=False) #random + [(0), (6), (30), (40), (41), (44), (56), (61)] + sage: relative_difference_set_from_m_sequence(6, 2) + Traceback (most recent call last): + ... + ValueError: q must be a prime power + + TESTS:: + + sage: from sage.combinat.designs.difference_family import is_relative_difference_set, _get_submodule_of_order + sage: q, N = 5, 3 + sage: G = AdditiveAbelianGroup([q^N-1]) + sage: H = _get_submodule_of_order(G, q-1) + sage: is_relative_difference_set(relative_difference_set_from_m_sequence(q, N), G, H, ((q^N-1)//(q-1), q-1, q^(N-1), q^(N-2))) + True + sage: q, N = 13, 2 + sage: G = AdditiveAbelianGroup([q^N-1]) + sage: H = _get_submodule_of_order(G, q-1) + sage: is_relative_difference_set(relative_difference_set_from_m_sequence(q, N), G, H, ((q^N-1)//(q-1), q-1, q^(N-1), q^(N-2))) + True + """ + from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup + + if not is_prime_power(q): + raise ValueError('q must be a prime power') + if N < 2: + raise ValueError('N must be at least 2') + + m_seq = _create_m_sequence(q, N, check=False) + period = q**N-1 + G = AdditiveAbelianGroup([period]) + + set1 = [i for i in G if m_seq[i[0]] == 1] + + if check: + H = _get_submodule_of_order(G, q-1) + assert is_relative_difference_set(set1, G, H, (period//(q-1), q-1, q**(N-1), q**(N-2))) + return set1 + +def is_relative_difference_set(R, G, H, params, verbose =False): + r"""Check if `R` is a difference set of `G` relative to `H`, with the given parameters. + + This function checks that `G`, `H` and `R` have the orders specified in the parameters, and + that R satisfies the definition of relative difference set (from [EB1966]_): the collection of + differences `r-s`, `r,s \in R`, `r \neq s` contains only elements of `G` which are not in `H`, and contains + every such element exactly `d` times. + + INPUT: + + - ``R`` -- list, the relative diffeence set of length `k`. + + - ``G`` -- an additive abelian group of order `mn`. + + - ``H`` -- a submodule of ``G`` of order `n`. + + - ``params`` -- a tuple in the form `(m, n, k, d)`. + + - ``verbose`` -- boolean (default False). If true the function will be verbose + when the sequences do not satisfy the contraints. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import _get_submodule_of_order, relative_difference_set_from_m_sequence, is_relative_difference_set + sage: q, N = 5, 2 + sage: G = AdditiveAbelianGroup([q^N-1]) + sage: H = _get_submodule_of_order(G, q-1) + sage: params = ((q^N-1)//(q-1), q-1, q^(N-1), q^(N-2)) + sage: R = relative_difference_set_from_m_sequence(q, N) + sage: is_relative_difference_set(R, G, H, params) + True + + If we pass the ``verbose`` argument, the function will explain why it failed:: + + sage: R2 = [G[1], G[2], G[3], G[5], G[6]] + sage: is_relative_difference_set(R2, G, H, params, verbose=True) + There is a value in the difference set which is not repeated d times + False + """ + m, n, k, d = params + if G.order() != m*n: + if verbose: + print('Incorrect order of G:', G.order()) + return False + + if H.order() != n: + if verbose: + print('Incorect order of H:', H.order()) + + if len(R) != k: + if verbose: + print('Length of R not correct:', len(R)) + return False + + diff_set = {} + for el1 in R: + for el2 in R: + if el1 != el2: + idx = el1-el2 + if idx not in diff_set: + diff_set[idx] = 0 + diff_set[idx] += 1 + values = [diff_set[x] for x in diff_set] + if max(values) != d or min(values) != d: + if verbose: + print('There is a value in the difference set which is not repeated d times') + return False + + for el in G: + if el in H and el in diff_set: + if verbose: + print('An element of G is present in both the difference set and in H') + return False + if el not in H and el not in diff_set: + if verbose: + print('An element of G is not present in either one of H or the difference set') + return False + + return True + def difference_family(v, k, l=1, existence=False, explain_construction=False, check=True): r""" Return a (``k``, ``l``)-difference family on an Abelian group of cardinality ``v``. From 3d56ffe9cbb71fe25a3870d91adf6a5a851fdd45 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Tue, 6 Dec 2022 13:24:49 +0100 Subject: [PATCH 03/19] Add construction for relative difference sets from homomorphism --- .../combinat/designs/difference_family.py | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index 32cff0146bf..79535370208 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -1453,6 +1453,73 @@ def relative_difference_set_from_m_sequence(q, N, check=True): assert is_relative_difference_set(set1, G, H, (period//(q-1), q-1, q**(N-1), q**(N-2))) return set1 +def relative_difference_set_from_homomorphism(q, N, d, check=True): + r"""Construct `R((q^N-1)/(q-1), n, q^{N-1}, q^{N-2}*d)` where `nd = q-1`. + + Given a prime power `q`, a number `N \ge 2` and integers `d` such that `d | q-1` we create the + relative difference set using the construction from Corollary 5.1.1 of [EB1966]_. + + INPUT: + + - ``q`` -- a prime power. + + - ``N`` -- an integer greater than 1. + + - ``d`` -- an integer which divides `q-1`. + + - ``check`` -- boolean (default True). If true, check that the result is a relative difference + set before returning it. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import relative_difference_set_from_homomorphism + sage: relative_difference_set_from_homomorphism(7, 2, 3) #random + [(0), (3), (4), (2), (13), (7), (14)] + sage: relative_difference_set_from_homomorphism(9, 2, 4, check=False) #random + [(0), (4), (6), (13), (7), (12), (15), (8), (9)] + sage: relative_difference_set_from_homomorphism(9, 2, 5) + Traceback (most recent call last): + ... + ValueError: q-1 must be a multiple of d + + TESTS:: + + sage: from sage.combinat.designs.difference_family import is_relative_difference_set, _get_submodule_of_order + sage: q, N, d = 11, 2, 5 + sage: G = AdditiveAbelianGroup([(q^N-1)//d]) + sage: H = _get_submodule_of_order(G, (q-1)//d) + sage: is_relative_difference_set(relative_difference_set_from_homomorphism(q, N, d), G, H, ((q**N-1)//(q-1), (q-1)//d, q**(N-1), q**(N-2)*d)) + True + sage: q, N, d = 9, 2, 4 + sage: G = AdditiveAbelianGroup([(q^N-1)//d]) + sage: H = _get_submodule_of_order(G, (q-1)//d) + sage: is_relative_difference_set(relative_difference_set_from_homomorphism(q, N, d), G, H, ((q**N-1)//(q-1), (q-1)//d, q**(N-1), q**(N-2)*d)) + True + """ + from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup + + if not is_prime_power(q): + raise ValueError('q must be a prime power') + if N < 2: + raise ValueError('N must be at least 2') + if (q-1)%d != 0: + raise ValueError('q-1 must be a multiple of d') + + G = AdditiveAbelianGroup([q**N-1]) + K = _get_submodule_of_order(G, d) + assert K is not None, 'Could not find kernel' + + G2 = G/K + + theta = G.hom([G2.gen(0)], G2) + diff_set = relative_difference_set_from_m_sequence(q, N, check=False) + second_diff_set = [theta(x) for x in diff_set] + + if check: + H = _get_submodule_of_order(G2, (q-1)//d) + assert is_relative_difference_set(second_diff_set, G2, H, ((q**N-1)//(q-1), (q-1)//d, q**(N-1), q**(N-2)*d)) + return second_diff_set + def is_relative_difference_set(R, G, H, params, verbose =False): r"""Check if `R` is a difference set of `G` relative to `H`, with the given parameters. From d7d89fb873e6e3921d558a84095215eaf05742ce Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 7 Dec 2022 10:26:25 +0100 Subject: [PATCH 04/19] Add construction for supplementary difference sets --- src/doc/en/reference/references/index.rst | 5 + .../combinat/designs/difference_family.py | 329 ++++++++++++++++++ 2 files changed, 334 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 946bb33b85d..4d059644671 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -5581,6 +5581,11 @@ REFERENCES: matrices, and characteristic polynomials without division* :doi:`10.1023/A:1021878507303` +.. [Spe1975] \E. Spence. + *Hadamard matrices from relative difference sets*, + Journal of Combinatorial Theory, Series A 19(3) (1975): 287-300. + :doi:`10.1016/0097-3165(75)90054-0` + .. [ST1981] \J. J. Seidel and D. E. Taylor, *Two-graphs, a second survey*. Algebraic methods in graph theory, Vol. I, II (Szeged, 1978), diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index 79535370208..cfa4ce061b0 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -1600,6 +1600,335 @@ def is_relative_difference_set(R, G, H, params, verbose =False): return True +def is_supplementary_difference_set(Ks, v, lmbda): + r"""Check that the sets in ``Ks`` are `n-\{v; k_1,...,k_n; \lambda \} supplementary difference sets. + + From the definition in [Spe1975]_: let `S_1, S_2, ..., S_n` be n subsets of an additive abelian group `G` of order `v` + such that `|S_i|\eq k_i`. If, for each `g\in G`, `g \neq 0`, the total number of solutions of `a_i-a'_i \eq g`, with + `a_i,a'_i \in S_i` is `\lambda`, then `S_1, S_2, ..., S_n` are `n-\{v; k_1,...,k_n;\lambda\} supplementary difference sets. + + INPUT: + + - ``Ks`` -- a list of sets to be checked. + + - ``v`` -- integer, the parameter `v` of the supplementary difference sets. + + - ``lmbda`` -- integer, the parameter `\lambda` of the supplementary difference sets. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import supplementary_difference_set, is_supplementary_difference_set + sage: S1, S2, S3, S4 = supplementary_difference_set(17) + sage: is_supplementary_difference_set([S1, S2, S3, S4], 16, 16) + True + sage: is_supplementary_difference_set([S1, S2, S3, S4], 16, 14) + False + sage: is_supplementary_difference_set([S1, S2, S3, S4], 20, 16) + False + + .. SEEALSO:: + + :func: `supplementary_difference_set` + """ + from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup + + G = AdditiveAbelianGroup([v]) + differences_counter = {} + for K in Ks: + for el1 in K: + for el2 in K: + diff = G[el1]-G[el2] + + if diff not in differences_counter: + differences_counter[diff] = 0 + differences_counter[diff] += 1 + + for el in G: + if el == 0: + continue + elif el not in differences_counter or lmbda != differences_counter[el]: + return False + + return True + +def supplementary_difference_set(q, existence=False, check=True): + r"""Construct `4-\{2v; v, v+1, v, v; 2v\}` supplementary difference sets where `q=2v+1`. + + The sets are created from relative difference sets as detailed in Theorem 3.3 of [Spe1975]_. this construction + requires that q is an odd prime power and that there exists `s \ge 0` such that `(q-(2^{s+1}+1))/2^{s+1}` is + an odd prime power. + + Note that the construction from [Spe1975]_ states that the resulting sets are `4-\{2v; v+1, v, v, v; 2v\}` + supplementary difference sets. However, the implementation of that construction returns + `4-\{2v; v, v+1, v, v; 2v\}` supplementary difference sets. This is not important, since the supplementary + difference sets are not ordered. + + INPUT: + + - ``q`` -- an odd prime power. + + - ``existence`` -- boolean (dafault False). If true, only check whether the supplementary difference sets + can be constructed. + + - ``check`` -- boolean (default True). If true, check that the sets are supplementary difference sets + before returning them. + + OUTPUT: + + If ``existence`` is false, the function returns the 4 sets (containing integers), or raises an + error if ``q`` does not satify the constraints. + If ``existence`` is true, the function returns a boolean representing whether supplementary difference + sets can be constructed. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import supplementary_difference_set + sage: supplementary_difference_set(17) #random + ([0, 2, 5, 6, 8, 10, 13, 14], + [0, 1, 2, 6, 7, 9, 10, 14, 15], + [0, 1, 2, 6, 11, 12, 13, 15], + [0, 2, 6, 9, 11, 12, 13, 15]) + + If existence is ``True``, the function returns a boolean:: + + sage: supplementary_difference_set(7, existence=True) + False + sage: supplementary_difference_set(17, existence=True) + True + + TESTS:: + + sage: from sage.combinat.designs.difference_family import is_supplementary_difference_set + sage: is_supplementary_difference_set(supplementary_difference_set(17), 16, 16) + True + sage: is_supplementary_difference_set(supplementary_difference_set(9), 8, 8) + True + sage: supplementary_difference_set(7) + Traceback (most recent call last): + ... + ValueError: There is no s for which m-1 is an odd prime power + sage: supplementary_difference_set(8) + Traceback (most recent call last): + ... + ValueError: q must be an odd prime power + sage: supplementary_difference_set(8, existence=True) + False + sage: supplementary_difference_set(7, existence=True) + False + sage: supplementary_difference_set(1, existence=True) + False + + .. SEEALSO:: + + :func: `is_supplementary_difference_set` + + """ + s = 0 + m = -1 + + while q > 2**(s+1) and (q-1) % 2**(s+1) == 0: + prime_pow = (q-1)//2**(s+1)-1 + if is_prime_power(prime_pow) and prime_pow % 2 == 1: + m = (q - (2**(s+1) + 1)) // 2**(s+1) + 1 + break + s += 1 + + if existence: + return is_prime_power(q) and q % 2 == 1 and m != -1 + + if not is_prime_power(q) or q % 2 != 1: + raise ValueError('q must be an odd prime power') + if m == -1: + raise ValueError('There is no s for which m-1 is an odd prime power') + + set1 = relative_difference_set_from_homomorphism(m-1, 2, (m-2)//2, check=False) + + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + P = PolynomialRing(ZZ, 'x') + + #Compute psi3, psi4 + hall = 0 + for d in set1: + hall += P.monomial(d[0]) + + T_2m = 0 + for i in range(2*m): + T_2m += P.monomial(i) + + modulo = P.monomial(2*m)-1 + + diff = T_2m - (1+P.monomial(m))*hall + diff = diff.mod(modulo) + exp1, exp2 = diff.exponents() + a = (exp1+exp2-m)//2 + + alfa3 = (P.monomial(a) + hall).mod(modulo) + alfa4 = (P.monomial(a+m) + hall).mod(modulo) + + psi3 = alfa3 + psi4 = alfa4 + for i in range(s): + psi3 = (alfa3(P.monomial(2))+P.monomial(1)*alfa4(P.monomial(2))).mod(P.monomial(4*m)-1) + psi4 = (alfa3(P.monomial(2)) + P.monomial(1)*(T_2m(P.monomial(2)) - alfa4(P.monomial(2)))).mod(P.monomial(4*m)-1) + + # Construction of psi1, psi2 + set2 = relative_difference_set_from_m_sequence(q, 2, check=False) + s3 = _get_fixed_relative_difference_set(set2) + + phi_exps = [] + for i in range(len(s3)): + for j in range(i+1, len(s3)): + diff = (s3[i]-s3[j]) + if diff%(q-1) == 0 and diff%(q**2-1) != 0: + phi_exps.append(s3[i]) + + exps1 = [(x+1)//2 for x in phi_exps if x%2 == 1] + exps2 = [x//2 for x in phi_exps if x%2 == 0] + + theta1 = 0 + for exp in exps1: + theta1 += P.monomial(exp) + theta1 = theta1.mod(P.monomial(q-1)-1) + + theta2 = 0 + for exp in exps2: + theta2 += P.monomial(exp) + theta2 = theta2.mod(P.monomial(q-1)-1) + + phi1 = 0 + phi2 = 0 + for exp in phi_exps: + if exp%2 == 0: + phi2 += P.monomial(exp) + else: + phi1 += P.monomial(exp) + + psi1 = ((1 + P.monomial((q-1)//2)) * theta1).mod(P.monomial(q-1) - 1) + psi2 = (1 + (1 + P.monomial((q-1)//2)) * theta2).mod(P.monomial(q-1) - 1) + + + K1 = list(map(Integer, psi1.exponents())) + K2 = list(map(Integer, psi2.exponents())) + K3 = list(map(Integer, psi3.exponents())) + K4 = list(map(Integer, psi4.exponents())) + if check: + assert is_supplementary_difference_set([K1, K2, K3, K4], q-1, q-1) + + return K1, K2, K3, K4 + +def _get_fixed_relative_difference_set(rel_diff_set, as_elements=False): + r"""Contruct an equivalent relative difference set fixed by the size of the set. + + Given a relative difference set `R(q+1, q-1, q, 1)`, it is possible to find a translation + of this set fixed by `q` (see Section 3 of [Spe1975]_). We say that a set is fixed by `t` if + `\{td | d\in R\}\eq R`. + + In addition, the set returned by this function will contain the element `0`. This is needed in the + construction of supplementary difference sets (see :func:`supplementary_difference_set`). + + INPUT: + + - ``rel_diff_set`` -- the relative difference set. + + - ``as_elements`` -- boolean (default False). If true, the list returned will contain elements of the + abelian group (this may slow down the computation considerably). + + OUTPUT: + + By default, this function returns the set as a list of integers. However, if ``as_elements`` + is set to true it will return the set as a list containing elements of the abelian group. + If no such set can be found, the function will raise an error. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import relative_difference_set_from_m_sequence, _get_fixed_relative_difference_set + sage: s1 = relative_difference_set_from_m_sequence(5, 2) + sage: _get_fixed_relative_difference_set(s1) #random + [2, 10, 19, 23, 0] + + If ``as_elements`` is true, the reuslt will contain elements of the group:: + + sage: _get_fixed_relative_difference_set(s1, as_elements=True) #random + [(2), (10), (19), (23), (0)] + + TESTS:: + + sage: from sage.combinat.designs.difference_family import _is_fixed_relative_difference_set + sage: s1 = relative_difference_set_from_m_sequence(5, 2) + sage: s2 = _get_fixed_relative_difference_set(s1, as_elements=True) + sage: _is_fixed_relative_difference_set(s2, len(s2)) + True + sage: s1 = relative_difference_set_from_m_sequence(9, 2) + sage: s2 = _get_fixed_relative_difference_set(s1, as_elements=True) + sage: _is_fixed_relative_difference_set(s2, len(s2)) + True + sage: type(s2[0]) + + sage: s2 = _get_fixed_relative_difference_set(s1) + sage: type(s2[0]) + + """ + G = rel_diff_set[0].parent() + q = len(rel_diff_set) + + s2 = None + for el in G: + fixed_set = [el+x for x in rel_diff_set] + if _is_fixed_relative_difference_set(fixed_set, q): + s2 = fixed_set + break + assert s2 is not None, 'Cannot find fixed translation of the set' + + s3 = None + for i in range(G.order()): + temp = [((q+1)*i+x[0])%G.order() for x in s2] + if 0 in temp: + s3 = temp + break + assert s3 is not None, 'Cannot find fixed set containing 0' + + if as_elements: + return [G[x] for x in s3] + return s3 + +def _is_fixed_relative_difference_set(R, q): + r"""Check if the relative difference set `R` is fixed by `q`. + + A relative difference set `R` is fixed by `q` if `\{qd | d\in R\}\eq R` (see Section 3 of [Spe1975]_). + + INPUT: + + - ``R`` -- the relative difference sets, as a list containing elements of the abelian group. + + - ``q`` -- an integer. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import relative_difference_set_from_m_sequence, _get_fixed_relative_difference_set, _is_fixed_relative_difference_set + sage: s1 = relative_difference_set_from_m_sequence(7, 2) + sage: s2 = _get_fixed_relative_difference_set(s1, as_elements=True) + sage: _is_fixed_relative_difference_set(s2, len(s2)) + True + sage: G = AdditiveAbelianGroup([15]) + sage: s3 = [G[1], G[2], G[3], G[4]] + sage: _is_fixed_relative_difference_set(s3, len(s3)) + False + + If the relative difference set does not contain elements of the group, the method returns false:: + + sage: s1 = relative_difference_set_from_m_sequence(7, 2) + sage: s2 = _get_fixed_relative_difference_set(s1, as_elements=False) + sage: _is_fixed_relative_difference_set(s2, len(s2)) + False + + + """ + for el in R: + if q*el not in R: + return False + return True + + def difference_family(v, k, l=1, existence=False, explain_construction=False, check=True): r""" Return a (``k``, ``l``)-difference family on an Abelian group of cardinality ``v``. From 2d65191dd22facdd520654188c2156cad23bcb35 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 7 Dec 2022 11:09:10 +0100 Subject: [PATCH 05/19] Add Spence construction for Hadamard matrices --- src/sage/combinat/matrices/hadamard_matrix.py | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 4fea4bfdf2e..a5888bf823a 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -1088,6 +1088,94 @@ def turyn_type_hadamard_matrix_smallcases(n, existence=False, check=True): return False raise ValueError("The Turyn type construction for Hadamard matrices of order %s is not yet implemented." % n) +def hadamard_matrix_spence_construction(n, existence=False, check=True): + r"""Create an Hadamard matrix of order `n` using Spence construction. + + This construction (detailed in [Spe1975]_), uses supplementary difference sets implemented in + :func:`sage.combinat.designs.difference_family.supplementary_difference_set` to create the + desired matrix. + + INPUT: + + - ``n`` -- integer, the order of the matrix to be constructed. + + - ``existence`` -- boolean (default False): if True, only check if matrix exists. + + - ``check`` -- bolean: if True (default), check the the matrix is an Hadamard matrix before returning. + + OUTPUT: + + If ``existence`` is true, returns a boolean representing whether the Hadamard matrix can + be constructed. Otherwise, returns the Hadamard matrix, or raises an error if it cannot be constructed. + + EXAMPLES:: + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_spence_construction + sage: hadamard_matrix_spence_construction(36) + 36 x 36 dense matrix over Integer Ring... + + If ``existence`` is ``True``, the function returns a boolean :: + + sage: hadamard_matrix_spence_construction(52, existence=True) + True + + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix + sage: is_hadamard_matrix(hadamard_matrix_spence_construction(100)) + True + sage: hadamard_matrix_spence_construction(48, existence=True) + False + sage: hadamard_matrix_spence_construction(48) + Traceback (most recent call last): + ... + ValueError: The order 48 is not covered by Spence construction. + sage: hadamard_matrix_spence_construction(5) + Traceback (most recent call last): + ... + AssertionError + sage: hadamard_matrix_spence_construction(0) + Traceback (most recent call last): + ... + AssertionError + """ + from sage.combinat.designs.difference_family import supplementary_difference_set + + assert n%4 == 0 and n > 0 + + q = n//4 + + if existence: + return supplementary_difference_set(q, existence=True) + + if not supplementary_difference_set(q, existence=True): + raise ValueError(f'The order {n} is not covered by Spence construction.') + + S1, S2, S3, S4 = supplementary_difference_set(q, check=False) + + A1 = matrix.circulant([1 if j in S1 else -1 for j in range(q-1)]) + A2 = matrix.circulant([1 if j in S4 else -1 for j in range(q-1)]) + A3 = matrix.circulant([1 if j in S3 else -1 for j in range(q-1)]) + A4 = matrix.circulant([1 if j in S2 else -1 for j in range(q-1)]) + + P = matrix(ZZ, [[1 if (i + j)%(q-1) == 0 else 0 for i in range(1, q)] for j in range(1, q)]) + + e = matrix([1]*(q-1)) + m1 = matrix([-1]) + p1 = matrix([1]) + H = block_matrix([[ p1, m1, p1, p1, e, e, e, e], + [ p1, p1, m1, p1, -e, e, -e, e], + [ m1, p1, p1, p1, -e, e, e, -e], + [ m1, m1, m1, p1, -e, -e, e, e], + [-e.T, e.T, e.T, -e.T, A1, A2*P, A3*P, A4*P], + [-e.T, -e.T, e.T, e.T, -A2*P, A1, -A4.T*P, A3.T*P], + [-e.T, -e.T, -e.T, -e.T, -A3*P, A4.T*P, A1, -A2.T*P], + [ e.T, -e.T, e.T, -e.T, -A4*P, -A3.T*P, A2.T*P, A1]]) + if check: + assert is_hadamard_matrix(H, verbose=True) + + return H + def is_hadamard_matrix(M, normalized=False, skew=False, verbose=False): r""" Test if `M` is a Hadamard matrix. From 2e4db8bd2821b28c56d7b464aa98132c1c70f36e Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 7 Dec 2022 11:21:41 +0100 Subject: [PATCH 06/19] Add spence construction to general construction function --- src/sage/combinat/matrices/hadamard_matrix.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index a5888bf823a..79f06d3a4a4 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -1284,8 +1284,7 @@ def is_hadamard_matrix(M, normalized=False, skew=False, verbose=False): @matrix_method def hadamard_matrix(n,existence=False, check=True): r""" - Tries to construct a Hadamard matrix using a combination of Paley - and Sylvester constructions. + Tries to construct a Hadamard matrix using the available methods. INPUT: @@ -1417,6 +1416,10 @@ def hadamard_matrix(n,existence=False, check=True): if existence: return True M = turyn_type_hadamard_matrix_smallcases(n, check=False) + elif hadamard_matrix_spence_construction(n ,existence=True): + if existence: + return True + M = hadamard_matrix_spence_construction(n, check=False) elif skew_hadamard_matrix(n, existence=True) is True: if existence: return True From 7ac17e47e5930c5334670df661c865a9ce13657c Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 7 Dec 2022 12:39:44 +0100 Subject: [PATCH 07/19] Add construction for base sequences --- src/sage/combinat/t_sequences.py | 145 +++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/src/sage/combinat/t_sequences.py b/src/sage/combinat/t_sequences.py index 70198a02b50..b1b67b571f0 100644 --- a/src/sage/combinat/t_sequences.py +++ b/src/sage/combinat/t_sequences.py @@ -537,3 +537,148 @@ def T_sequences_smallcases(t, existence=False, check=True): if existence: return False raise ValueError(f'T Sequences of length {t} not yet implemented.') + + +def base_sequences_construction(turyn_type_seqs, check=True): + r"""Construct base sequences of length `2n-1, 2n-1, n, n` from Turyn type sequences of length `n,n,n,n-1`. + + Given Turyn type sequences `X, Y, Z, W` of length `n,n,n,n-1`, Theorem 1 of [KTR2004]_ shows that the + following are base sequences of length `2n-1, 2n-1, n, n`: + + .. MATH:: + + \begin{aligned} + A &= Z;W \\ + B &= Z; -W \\ + C &= X \\ + D &= Y + \end{aligned} + + INPUT: + + - ``turyn_type_seqs`` -- The list of 4 Turyn type sequences that should be used to construct the base sequences. + + - ``check`` -- boolean, if True (default) check that the resulting sequences are base sequences + before returning them. + + OUTPUT: A list containing the four base sequences. + + EXAMPLES:: + + sage: from sage.combinat.t_sequences import base_sequences_construction + sage: X = [1,1,-1,1,-1,1,-1,1] + sage: Y = [1,-1,-1,-1,-1,-1,-1,1] + sage: Z = [1,-1,-1,1,1,1,1,-1] + sage: W = [1,1,1,-1,1,1,-1] + sage: base_sequences_construction([X, Y, Z, W]) + [[1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1], + [1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1], + [1, 1, -1, 1, -1, 1, -1, 1], + [1, -1, -1, -1, -1, -1, -1, 1]] + + TESTS:: + + sage: base_sequences_construction([[1, -1], [1], [1], [-1]]) + Traceback (most recent call last): + ... + AssertionError + + .. SEALSO:: + + :func:`is_base_sequences_tuple` + """ + assert len(turyn_type_seqs) == 4 + X, Y, Z, W = turyn_type_seqs + + assert len(X) == len(Y) == len(Z) == len(W)+1 + + A = Sequence(Z + W) + B = Sequence(Z + [-el for el in W]) + C = X + D = Y + + if check: + assert is_base_sequences_tuple([A, B, C, D]) + return [A, B, C, D] + + +def is_base_sequences_tuple(base_sequences, verbose=False): + r"""Check if the given sequences are base sequences. + + Four (-1, +1) sequences `A, B, C, D` of length `n+p, n+p, n, n` are called base sequences if + for all `j \ge 1`: + + .. MATH:: + + N_A(j)+N_B(j)+N_C(j)+N_D(j) \eq 0 + + where `N_X(j)` is the nonperiodic autocorrelation (See definition in [KTR2004]_). + + INPUT: + + - ``base_sequences`` -- The list of 4 sequences that should be checked. + + - ``verbose`` -- a boolean (default false). If true the function will be verbose + when the sequences do not satisfy the contraints. + + EXAMPLES:: + + sage: from sage.combinat.t_sequences import is_base_sequences_tuple + sage: seqs = [[1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1],[1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1],[1, 1, -1, 1, -1, 1, -1, 1],[1, -1, -1, -1, -1, -1, -1, 1]] + sage: is_base_sequences_tuple(seqs) + True + + If verbose is true, the function will be verbose :: + + sage: seqs = [[1, -1], [1, 1], [-1], [2]] + sage: is_base_sequences_tuple(seqs, verbose=True) + Base sequences should only contiain -1, +1, found 2 + False + + TESTS: + + sage: seqs = [[1, -1], [1], [-1]] + sage: is_base_sequences_tuple(seqs) + False + sage: seqs = [[1, -1], [1, -1], [-1], [1]] + sage: is_base_sequences_tuple(seqs) + False + sage: seqs = [[1, -1], [1, 1], [-1], [2]] + sage: is_base_sequences_tuple(seqs) + False + sage: seqs = [[1, -1], [1], [-1], [1]] + sage: is_base_sequences_tuple(seqs) + False + + .. SEALSO:: + + :func:`base_sequences_construction` + """ + if len(base_sequences) != 4: + if verbose: + print(f'Base sequences should be 4, found {len(base_sequences)}') + return False + A, B, C, D = base_sequences + n = len(C) + p = len(A) - len(C) + if not (len(A) == len(B) == len(C)+p == len(D)+p): + if verbose: + print(f'Base sequences should have length n+p, n+p, n, n, found {len(A)}, {len(B)}, {len(C)}, {len(D)}') + return False + + for seq in base_sequences: + for el in seq: + if abs(el) != 1: + if verbose: + print(f'Base sequences should only contiain -1, +1, found {el}') + return False + + + for j in range(1, n+p): + autocorr = _nonperiodic_autocorrelation(A, j) + _nonperiodic_autocorrelation(B, j) + _nonperiodic_autocorrelation(C, j) + _nonperiodic_autocorrelation(D, j) + if autocorr != 0: + if verbose: + print(f"Nonperiodic autocorrelation should always be zero, found {autocorr} for parameter {j}") + return False + + return True \ No newline at end of file From b2f65152628430f25b72a5e0cce4d70fe709ddab Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 7 Dec 2022 13:08:40 +0100 Subject: [PATCH 08/19] Generalize construction of T-sequences to work with base sequences --- src/doc/en/reference/references/index.rst | 5 ++ src/sage/combinat/t_sequences.py | 83 ++++++++++++----------- 2 files changed, 47 insertions(+), 41 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 4d059644671..ffaf860c128 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -3789,6 +3789,11 @@ REFERENCES: .. [KT2013] \K. Tsukazaki, Explicit Isogenies of Elliptic Curves, PhD thesis, University of Warwick, 2013. +.. [KTR2005] \H. Kharaghani and B. Tayfeh-Rezaie. + *A Hadamard matrix of order 428*, + Journal of Combinatorial Designs 13(6) (2005): 435-440. + :doi:`10.1002/jcd.20043` + .. [KTT2006] \A. Kuniba, T. Takagi, and A. Takenouchi, *Bethe ansatz and inverse scattering transform in a periodic box-ball system*, Nuclear Phys. B **747**, no. 3 (2006), 354--397. diff --git a/src/sage/combinat/t_sequences.py b/src/sage/combinat/t_sequences.py index b1b67b571f0..715041be880 100644 --- a/src/sage/combinat/t_sequences.py +++ b/src/sage/combinat/t_sequences.py @@ -297,33 +297,33 @@ def turyn_sequences_smallcases(l, existence=False): return list(map(Sequence, db[l])) -def T_sequences_construction_I(turyn_sequences, check=True): +def T_sequences_construction_from_base_sequences(base_sequences, check=True): r""" - Construct T-sequences of length `2l-1` from Turyn sequences of length `l`. + Construct T-sequences of length `2n-p` from base sequences of length `n+p, n+p, n, n`. - Given Turyn sequences `X, U, Y, V`, the T-sequences are constructed as described in - Theorem 7.7 of [Seb2017]_: + Given base sequences `A, B, C, D`, the T-sequences are constructed as described in + [KTR2005]_: .. MATH:: \begin{aligned} - T_1 &= \frac{1}{2}(X+U); 0_{l-1} \\ - T_2 &= \frac{1}{2}(X-U); 0_{l-1} \\ - T_3 &= 0_{l} + \frac{1}{2}(Y+V) \\ - T_4 &= 0_{l} + \frac{1}{2}(Y-V) + T_1 &= \frac{1}{2}(A+B); 0_{n} \\ + T_2 &= \frac{1}{2}(A-B); 0_{n} \\ + T_3 &= 0_{n+p} + \frac{1}{2}(C+D) \\ + T_4 &= 0_{n+p} + \frac{1}{2}(C-D) \end{aligned} INPUT: - - ``turyn_sequences`` -- the Turyn sequences that should be used to construct the T-sequences. + - ``base_sequences`` -- the base sequences that should be used to construct the T-sequences. - ``check`` -- boolean, if true (default) checks that the sequences created are T-sequences before returning them. EXAMPLES:: - sage: from sage.combinat.t_sequences import turyn_sequences_smallcases, T_sequences_construction_I, is_T_sequences_set + sage: from sage.combinat.t_sequences import turyn_sequences_smallcases, T_sequences_construction_from_base_sequences sage: seqs = turyn_sequences_smallcases(4) - sage: T_sequences_construction_I(seqs) + sage: T_sequences_construction_from_base_sequences(seqs) [[1, 1, -1, 0, 0, 0, 0], [0, 0, 0, -1, 0, 0, 0], [0, 0, 0, 0, 1, 0, 1], @@ -331,29 +331,30 @@ def T_sequences_construction_I(turyn_sequences, check=True): TESTS:: + sage: from sage.combinat.t_sequences import base_sequences_construction, is_T_sequences_set sage: seqs = turyn_sequences_smallcases(4) - sage: is_T_sequences_set(T_sequences_construction_I(seqs)) + sage: is_T_sequences_set(T_sequences_construction_from_base_sequences(seqs)) True - sage: T_sequences_construction_I([[1, -1], [-1, 1], [1]]) + sage: T_sequences_construction_from_base_sequences([[1, -1], [-1, 1], [1]]) Traceback (most recent call last): ... AssertionError - - .. NOTE:: - - The construction detailed in [Seb2017]_ contains a typo. The first two sequences are - defined as `T_1 = \frac{1}{2}(X+Y); 0_{l-1}` and `T_2 = \frac{1}{2}(X-Y); 0_{l-1}`, - but the correct formulas are `T_1 = \frac{1}{2}(X+U); 0_{l-1}` and - `T_2 = \frac{1}{2}(X-U); 0_{l-1}`. A very similar typo can be seen in the proof of - Theorem 9 in [CRSKKY1989]_ as well. + sage: X = [1,1,-1,1,-1,1,-1,1] + sage: Y = [1,-1,-1,-1,-1,-1,-1,1] + sage: Z = [1,-1,-1,1,1,1,1,-1] + sage: W = [1,1,1,-1,1,1,-1] + sage: base_seqs = base_sequences_construction([X, Y, Z, W]) + sage: is_T_sequences_set(T_sequences_construction_from_base_sequences(base_seqs)) + True """ - assert len(turyn_sequences) == 4 + assert len(base_sequences) == 4 - X, U, Y, V = turyn_sequences - l = len(X) + A, B, C, D = base_sequences + n = len(C) + p = len(A)-n - assert len(X) == len(U) == len(Y)+1 == len(V)+1 + assert len(A) == len(B) == len(C)+p == len(D)+p def seq_sum(seq1, seq2): return [(a+b)//2 for (a, b) in zip(seq1, seq2)] @@ -364,17 +365,17 @@ def seq_subtract(seq1, seq2): def zero_seq(n): return [0 for _ in range(n)] - X1 = Sequence(seq_sum(X, U) + zero_seq(l-1)) - X2 = Sequence(seq_subtract(X, U) + zero_seq(l-1)) - X3 = Sequence(zero_seq(l) + seq_sum(Y, V)) - X4 = Sequence(zero_seq(l) + seq_subtract(Y, V)) + X1 = Sequence(seq_sum(A, B) + zero_seq(n)) + X2 = Sequence(seq_subtract(A, B) + zero_seq(n)) + X3 = Sequence(zero_seq(n+p) + seq_sum(C, D)) + X4 = Sequence(zero_seq(n+p) + seq_subtract(C, D)) res = [X1, X2, X3, X4] if check: assert is_T_sequences_set(res) return res -def T_sequences_construction_II(turyn_sequences, check=True): +def T_sequences_construction_from_turyn_sequences(turyn_sequences, check=True): r""" Construct T-sequences of length `4l-1` from Turyn sequences of length `l`. @@ -398,23 +399,23 @@ def T_sequences_construction_II(turyn_sequences, check=True): EXAMPLES:: - sage: from sage.combinat.t_sequences import turyn_sequences_smallcases, T_sequences_construction_II, is_T_sequences_set + sage: from sage.combinat.t_sequences import turyn_sequences_smallcases, T_sequences_construction_from_turyn_sequences, is_T_sequences_set sage: seqs = turyn_sequences_smallcases(4) - sage: T_sequences_construction_II(seqs) + sage: T_sequences_construction_from_turyn_sequences(seqs) [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 1, 1, 1, -1, 1, -1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, -1, 0, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 1, 0]] TESTS:: + sage: seqs = turyn_sequences_smallcases(4) - sage: is_T_sequences_set(T_sequences_construction_II(seqs)) + sage: is_T_sequences_set(T_sequences_construction_from_turyn_sequences(seqs)) True - sage: T_sequences_construction_II([[1, -1], [-1, 1], [1]]) + sage: T_sequences_construction_from_turyn_sequences([[1, -1], [-1, 1], [1]]) Traceback (most recent call last): ... AssertionError - """ assert len(turyn_sequences) == 4 @@ -451,8 +452,8 @@ def T_sequences_smallcases(t, existence=False, check=True): Construct T-sequences for some small values of `t`. This method will try to use the constructions defined in - :func:`sage.combinat.t_sequences.T_sequences_construction_I` and - :func:`sage.combinat.t_sequences.T_sequences_construction_II` + :func:`sage.combinat.t_sequences.T_sequences_construction_from_base_sequences` and + :func:`sage.combinat.t_sequences.T_sequences_construction_from_turyn_sequences` together with the Turyn sequences stored in :func:`sage.combinat.t_sequences.turyn_sequences_smallcases`, or some T-sequences taken directly from [CRSKKY1989]_. @@ -526,13 +527,13 @@ def T_sequences_smallcases(t, existence=False, check=True): if existence: return True turyn_seqs = turyn_sequences_smallcases((t+1)//2) - return T_sequences_construction_I(turyn_seqs, check=check) + return T_sequences_construction_from_base_sequences(turyn_seqs, check=check) if (t+1)%4 == 0 and turyn_sequences_smallcases((t+1)//4, existence=True): if existence: return True turyn_seqs = turyn_sequences_smallcases((t+1)//4) - return T_sequences_construction_II(turyn_seqs, check=check) + return T_sequences_construction_from_turyn_sequences(turyn_seqs, check=check) if existence: return False @@ -542,7 +543,7 @@ def T_sequences_smallcases(t, existence=False, check=True): def base_sequences_construction(turyn_type_seqs, check=True): r"""Construct base sequences of length `2n-1, 2n-1, n, n` from Turyn type sequences of length `n,n,n,n-1`. - Given Turyn type sequences `X, Y, Z, W` of length `n,n,n,n-1`, Theorem 1 of [KTR2004]_ shows that the + Given Turyn type sequences `X, Y, Z, W` of length `n,n,n,n-1`, Theorem 1 of [KTR2005]_ shows that the following are base sequences of length `2n-1, 2n-1, n, n`: .. MATH:: @@ -612,7 +613,7 @@ def is_base_sequences_tuple(base_sequences, verbose=False): N_A(j)+N_B(j)+N_C(j)+N_D(j) \eq 0 - where `N_X(j)` is the nonperiodic autocorrelation (See definition in [KTR2004]_). + where `N_X(j)` is the nonperiodic autocorrelation (See definition in [KTR2005]_). INPUT: From 0e53ff40199262b68acd208e8bafe33ab680bbde Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 7 Dec 2022 14:37:47 +0100 Subject: [PATCH 09/19] Add Turyn type sequences smallcases --- src/doc/en/reference/references/index.rst | 5 ++ src/sage/combinat/t_sequences.py | 93 ++++++++++++++++++++++- 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index ffaf860c128..64682911d3c 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -620,6 +620,11 @@ REFERENCES: and variants*. :arxiv:`1810.00789` +.. [BDKR2013] \D. Best, D.Z. Dokovic, H. Kharaghani and H. Ramp. + *Turyn-Type Sequences: Classification, Enumeration, and Construction*, + Journal of Combinatorial Designs 21(1) (2013): 24-35. + :doi:`10.1002/jcd.21318` + .. [BDLV2006] \S. Brlek, S. Dulucq, A. Ladouceur, L. Vuillon, *Combinatorial properties of smooth infinite words*, Theoret. Comput. Sci. 352 (2006) 306--317. diff --git a/src/sage/combinat/t_sequences.py b/src/sage/combinat/t_sequences.py index 715041be880..0b02df4cd41 100644 --- a/src/sage/combinat/t_sequences.py +++ b/src/sage/combinat/t_sequences.py @@ -682,4 +682,95 @@ def is_base_sequences_tuple(base_sequences, verbose=False): print(f"Nonperiodic autocorrelation should always be zero, found {autocorr} for parameter {j}") return False - return True \ No newline at end of file + return True + +def turyn_type_sequences_smallcases(n, existence=False): + r""" + Construction of Turyn type sequences for small values of `n`. + + The data is taken from [KTR2005]_ for `n\eq 36`, and from [BDKR2013]_ for `n\le 32`. + + INPUT: + + - ``n`` -- integer, the length of the Turyn type sequences. + + - ``existence`` -- boolean (default False). If true, only return whether the + Turyn type sequences are available for the given length. + + EXAMPLES: + + By default, this method returns the four Turyn type sequences :: + + sage: from sage.combinat.t_sequences import turyn_type_sequences_smallcases + sage: turyn_type_sequences_smallcases(4) + [[1, 1, 1, 1], [1, 1, -1, 1], [1, 1, -1, -1], [1, -1, 1]] + + If we pass the ``existence`` flag, the method will return a boolean :: + + sage: turyn_type_sequences_smallcases(4, existence=True) + True + + TESTS:: + + sage: turyn_type_sequences_smallcases(17) + Traceback (most recent call last): + ... + ValueError: Turyn type sequences of length 17 are not implemented yet. + sage: turyn_type_sequences_smallcases(17, existence=True) + False + + ALGORITHM: + + The Turyn type sequences are stored in hexadecimal format. + Given `n` hexadecimal digits `h_1, h_2,...,h_n`, it is possible to get the Turyn type sequences + by converting each `h_i` (`1 \le i \le n-1`) into a four digits binary number. Then, the j-th binary digit is + `0` if the i-th number in the j-th sequence is `1`, and it is `1` if the number in the sequence is -1. + + For the n-th digit, it should be converted to a 3 digits binary number, and then the same mapping + as before can be used (see also [BDKR2013]_). + """ + def convertLists(hexstring): + seqs = [Sequence([]), Sequence([]), Sequence([]), Sequence([])] + for c in hexstring[:-1]: + binary = bin(int(c, 16))[2:].zfill(4) + for i in range(4): + if binary[i] == '0': + seqs[i].append(1) + else: + seqs[i].append(-1) + last = bin(int(hexstring[-1], 16))[2:].zfill(3) + for i in range(3): + if last[i] == '0': + seqs[i].append(1) + else: + seqs[i].append(-1) + return seqs + + db = { + 2: '01', + 4: '0161', + 6: '006d61', + 8: '06e5c4d1', + 10: '0001f4a961', + 12: '0004f90bc961', + 14: '00036ac71c7651', + 16: '0000778e52de5561', + 18: '00006758b30d1e9a51', + 20: '000038e2739c7a0b6951', + 22: '00000f702c71a9ad565961', + 24: '00000b7c2cb2bc4b6cd9a961', + 26: '000000ff0f846f1ca5a5aa9551', + 28: '0000067cde3e50639ab46135aa51', + 30: '000000f70b106f9d427a25e9a96951', + 32: '00000138f64f1c1e77844f26d95a5961', + 36: '060989975b685d8fc80750b21c0212eceb26', + } + + if existence: + return n in db + + if n not in db: + raise ValueError(f"Turyn type sequences of length {n} are not implemented yet.") + + return convertLists(db[n]) + \ No newline at end of file From e231e5e03e5ef43b2d7d922d0b1cb192a9a8befb Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 7 Dec 2022 15:25:52 +0100 Subject: [PATCH 10/19] Add construction for small cases of base sequences --- src/sage/combinat/t_sequences.py | 65 +++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/t_sequences.py b/src/sage/combinat/t_sequences.py index 0b02df4cd41..2cca608735c 100644 --- a/src/sage/combinat/t_sequences.py +++ b/src/sage/combinat/t_sequences.py @@ -299,7 +299,7 @@ def turyn_sequences_smallcases(l, existence=False): def T_sequences_construction_from_base_sequences(base_sequences, check=True): r""" - Construct T-sequences of length `2n-p` from base sequences of length `n+p, n+p, n, n`. + Construct T-sequences of length `2n+p` from base sequences of length `n+p, n+p, n, n`. Given base sequences `A, B, C, D`, the T-sequences are constructed as described in [KTR2005]_: @@ -773,4 +773,65 @@ def convertLists(hexstring): raise ValueError(f"Turyn type sequences of length {n} are not implemented yet.") return convertLists(db[n]) - \ No newline at end of file + +def base_sequences_smallcases(n, p, existence=False, check=True): + r"""Construct base sequences of length `n+p, n+p, n, n` from available data. + + The function uses the function :func:`base_sequences_construction`, together with + base sequences from :func:`turyn_type_sequences_smallcases` to construct base sequences with `p \eq n-1`. + + INPUT: + + - ``n`` -- integer, the length of the last two base sequences. + + - ``p`` -- integer, `n+p` will be the length of the first two base sequences. + + - ``existence`` -- boolean (default False). If True, the function will only check whether the base + sequences can be constructed. + + - ``check`` -- boolean, if True (default) check that the resulting sequences are base sequences + before returning them. + + OUTPUT: + + If ``existence`` is ``False``, the function returns a list containing the four base sequences, or raises + an error if the base sequences cannot be constructed. If ``existence`` is ``True``, the function returns a + boolean, which is ``True`` if the base sequences can be constructed and ``False`` otherwise. + + EXAMPLES:: + + sage: from sage.combinat.t_sequences import base_sequences_smallcases + sage: base_sequences_smallcases(8, 7) + [[1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1], + [1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1], + [1, 1, -1, 1, -1, 1, -1, 1], + [1, -1, -1, -1, -1, -1, -1, 1]] + + If ``existence`` is ``True``, the function returns a boolean :: + + sage: base_sequences_smallcases(8, 7, existence=True) + True + sage: base_sequences_smallcases(7, 5, existence=True) + False + + TESTS:: + + sage: base_sequences_smallcases(7, 5) + Traceback (most recent call last): + ... + ValueError: Data is only present for base sequences of the form 2n-1, 2n-1, n, n + sage: seqs = base_sequences_smallcases(16, 15) + sage: len(seqs[0]) == len(seqs[1]) == 16+15 + True + sage: len(seqs[2]) == len(seqs[3]) == 16 + True + """ + + if existence: + return p == n-1 and turyn_type_sequences_smallcases(n, existence=True) + + if p != n-1: + raise ValueError('Data is only present for base sequences of the form 2n-1, 2n-1, n, n') + + turyn_type_seqs = turyn_type_sequences_smallcases(n) + return base_sequences_construction(turyn_type_seqs, check=check) \ No newline at end of file From fcdcbb7a2d0b2dfea86fda40309b88507f1aef4f Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 7 Dec 2022 15:34:36 +0100 Subject: [PATCH 11/19] Use base sequences in general T-sequences construction --- src/sage/combinat/t_sequences.py | 36 +++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/t_sequences.py b/src/sage/combinat/t_sequences.py index 2cca608735c..e40ef3142b0 100644 --- a/src/sage/combinat/t_sequences.py +++ b/src/sage/combinat/t_sequences.py @@ -452,10 +452,12 @@ def T_sequences_smallcases(t, existence=False, check=True): Construct T-sequences for some small values of `t`. This method will try to use the constructions defined in - :func:`sage.combinat.t_sequences.T_sequences_construction_from_base_sequences` and - :func:`sage.combinat.t_sequences.T_sequences_construction_from_turyn_sequences` - together with the Turyn sequences stored in :func:`sage.combinat.t_sequences.turyn_sequences_smallcases`, - or some T-sequences taken directly from [CRSKKY1989]_. + :func:`T_sequences_construction_from_base_sequences` and + :func:`T_sequences_construction_from_turyn_sequences` + together with the Turyn sequences stored in :func:`turyn_sequences_smallcases`, + or base sequences created by :func:`base_sequences_smallcases`. + + This function contains also some T-sequences taken directly from [CRSKKY1989]_. INPUT: @@ -534,7 +536,15 @@ def T_sequences_smallcases(t, existence=False, check=True): return True turyn_seqs = turyn_sequences_smallcases((t+1)//4) return T_sequences_construction_from_turyn_sequences(turyn_seqs, check=check) - + + for p in range(1, t): + n = (t-p)//2 + if (t-p)%2 == 0 and base_sequences_smallcases(n, p, existence=True): + if existence: + return True + base_seqs = base_sequences_smallcases(n, p, check=False) + return T_sequences_construction_from_base_sequences(base_seqs, check=check) + if existence: return False raise ValueError(f'T Sequences of length {t} not yet implemented.') @@ -830,8 +840,14 @@ def base_sequences_smallcases(n, p, existence=False, check=True): if existence: return p == n-1 and turyn_type_sequences_smallcases(n, existence=True) - if p != n-1: - raise ValueError('Data is only present for base sequences of the form 2n-1, 2n-1, n, n') - - turyn_type_seqs = turyn_type_sequences_smallcases(n) - return base_sequences_construction(turyn_type_seqs, check=check) \ No newline at end of file + if p == n-1 and turyn_type_sequences_smallcases(n, existence=True): + if existence: + return True + turyn_type_seqs = turyn_type_sequences_smallcases(n) + return base_sequences_construction(turyn_type_seqs, check=check) + if p == 1 and turyn_sequences_smallcases(n+p, existence=True): + if existence: + return True + return turyn_sequences_smallcases(n+p) + + raise ValueError(f'Base sequences of order {n+p}, {n+p}, {n}, {n} not yet implemented.') From df544a0ad8fb6a4d7556bec73cef35fc72f11f68 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 7 Dec 2022 17:15:12 +0100 Subject: [PATCH 12/19] Add williamson type matrices of order 7 --- src/sage/combinat/matrices/hadamard_matrix.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 79f06d3a4a4..2a1425336fa 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -333,6 +333,10 @@ def williamson_type_quadruples_smallcases(n, existence=False): """ db = { 1: ([1], [1], [1], [1]), + 7: ([1, -1, -1, 1, 1, -1, -1], + [1, -1, 1, -1, -1, 1, -1], + [1, 1, -1, -1, -1, -1, 1], + [1, -1, -1, -1, -1, -1, -1]), 29: ([1, 1, 1,-1,-1,-1, 1, 1,-1,-1, 1,-1, 1,-1,-1,-1,-1, 1,-1, 1,-1,-1, 1, 1,-1,-1,-1, 1, 1], [1,-1, 1,-1,-1,-1, 1, 1,-1,-1, 1,-1, 1, 1, 1, 1, 1, 1,-1, 1,-1,-1, 1, 1,-1,-1,-1, 1,-1], [1, 1, 1, 1,-1, 1, 1,-1, 1,-1,-1,-1, 1, 1, 1, 1, 1, 1,-1,-1,-1, 1,-1, 1, 1,-1, 1, 1, 1], From 35fffdabe13661c6629e866172cf8d96e426a500 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 7 Dec 2022 17:21:06 +0100 Subject: [PATCH 13/19] Add williamson type matrices of order 9 --- src/sage/combinat/matrices/hadamard_matrix.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 2a1425336fa..59374abee63 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -337,6 +337,10 @@ def williamson_type_quadruples_smallcases(n, existence=False): [1, -1, 1, -1, -1, 1, -1], [1, 1, -1, -1, -1, -1, 1], [1, -1, -1, -1, -1, -1, -1]), + 9: ([1, -1, -1, -1, 1, 1, -1, -1, -1], + [1, -1, -1, 1, -1, -1, 1, -1, -1], + [1, -1, 1, -1, -1, -1, -1, 1, -1], + [1, 1, -1, -1, -1, -1, -1, -1, 1]), 29: ([1, 1, 1,-1,-1,-1, 1, 1,-1,-1, 1,-1, 1,-1,-1,-1,-1, 1,-1, 1,-1,-1, 1, 1,-1,-1,-1, 1, 1], [1,-1, 1,-1,-1,-1, 1, 1,-1,-1, 1,-1, 1, 1, 1, 1, 1, 1,-1, 1,-1,-1, 1, 1,-1,-1,-1, 1,-1], [1, 1, 1, 1,-1, 1, 1,-1, 1,-1,-1,-1, 1, 1, 1, 1, 1, 1,-1,-1,-1, 1,-1, 1, 1,-1, 1, 1, 1], @@ -1361,7 +1365,7 @@ def hadamard_matrix(n,existence=False, check=True): False sage: matrix.hadamard(12,existence=True) True - sage: matrix.hadamard(476,existence=True) + sage: matrix.hadamard(668,existence=True) Unknown sage: matrix.hadamard(10) Traceback (most recent call last): From 809783006e148c1cd13e72527ff369b94fbcf37f Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Thu, 8 Dec 2022 17:56:49 +0100 Subject: [PATCH 14/19] Add construction of skew supplementary difference sets --- src/doc/en/reference/references/index.rst | 9 + .../combinat/designs/difference_family.py | 199 ++++++++++++++++++ 2 files changed, 208 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 64682911d3c..6a0d6e28a46 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -2032,6 +2032,15 @@ REFERENCES: and some constructions of de Luca and Rauzy*, Theoret. Comput. Sci. 255 (2001) 539--553. +.. [Djo1992] \D. Đoković. + *Construction of some new Hadamard matrices*, + Bulletin of the Australian Mathematical Society 45(2) (1992): 327-332. + :doi:`10.1017/S0004972700030185` + +.. [Djo1994] \D. Đoković. + *Five New Orders for Hadamard Matrices of Skew Type*, + Australasian Journal of Combinatorics 10 (1994): 259-264. + .. [DK2013] John R. Doyle and David Krumm, *Computing algebraic numbers of bounded height*, :arxiv:`1111.4963v4` (2013). diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index cfa4ce061b0..0c9af9cc871 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -54,6 +54,7 @@ from sage.categories.sets_cat import EmptySetError import sage.arith.all as arith from sage.misc.unknown import Unknown +from sage.rings.finite_rings.integer_mod_ring import Zmod from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -1929,6 +1930,204 @@ def _is_fixed_relative_difference_set(R, q): return True +def skew_supplementary_difference_set(n, existence=False, check=True): + r"""Construct `4-\{n; n_1, n_2, n_3, n_4; \lambda\}` supplementary difference sets where `S_1` is skew and `n_1+n_2+n_3+n_4\eq n+\lambda`. + + These sets are constructed from available data, as described in [Djo1994]_. The set `S_1 \subset G` is + always skew, i.e. `S_1 \cap (-S_1) \eq \emptyset` and `S_1 \cup (-S_1) \eq G\\\{0\}`. + + The data for `n\eq 103, 151` is taken from [Djo1994]_ and the data for `n\eq 67, 113, 127, 157, 163, 181, 241` + is taken from [Djo1992]_. + + INPUT: + + - ``n`` -- integer, the parameter of the supplementary difference set. + + - ``existence`` -- boolean (dafault False). If true, only check whether the supplementary difference sets + can be constructed. + + - ``check`` -- boolean (default True). If true, check that the sets are supplementary difference sets with + `S_1` skew before returning them. Setting this parameter to False may speed up the computation considerably. + + + OUTPUT: + + If ``existence`` is false, the function returns the 4 sets (containing integers modulo `n`), or raises an + error if data for the given ``n`` is not available. + If ``existence`` is true, the function returns a boolean representing whether supplementary difference + sets can be constructed. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import skew_supplementary_difference_set + sage: S1, S2, S3, S4 = skew_supplementary_difference_set(103) + + If existence is ``True``, the function returns a boolean :: + + sage: skew_supplementary_difference_set(103, existence=True) + True + sage: skew_supplementary_difference_set(17, existence=True) + False + + TESTS:: + + sage: from sage.combinat.designs.difference_family import is_supplementary_difference_set, _is_skew_set + sage: S1, S2, S3, S4 = skew_supplementary_difference_set(113, check=False) + sage: is_supplementary_difference_set([S1, S2, S3, S4], 113, len(S1)+len(S2)+len(S3)+len(S4)-113) + True + sage: _is_skew_set(S1, 113) + True + sage: S1, S2, S3, S4 = skew_supplementary_difference_set(67, check=False) + sage: is_supplementary_difference_set([S1, S2, S3, S4], 67, len(S1)+len(S2)+len(S3)+len(S4)-67) + True + sage: _is_skew_set(S1, 67) + True + sage: skew_supplementary_difference_set(7) + Traceback (most recent call last): + ... + ValueError: Skew SDS of order 7 not yet implemented. + sage: skew_supplementary_difference_set(7, existence=True) + False + sage: skew_supplementary_difference_set(127, existence=True) + True + """ + + + indices = { + 67: [[0,3,5,6,9,10,13,14,17,18,20], + [0,2,4,9,11,12,13,16,19,21], + [1,3,6,10,11,13,14,16,20,21], + [2,4,6,8,9,11,14,17,19]], + 103: [[1,3,4,6,8,11,12,14,17,18,20,22,25,27,28,30,32], + [2,9,10,12,13,14,15,16,20,21,22,23,24,26,28,29,30], + [0,1,2,3,4,11,12,13,16,17,19,20,21,24,25,26,28,30,31], + [0,1,2,3,4,5,6,13,15,18,19,20,23,24,25,26,27,28,29,31]], + 113: [[0,3,4,6,8,10,13,14], + [1,3,8,9,10,11,12,13], + [0,2,3,5,6,7,12], + [1,2,3,5,8,9,15]], + 127: [[0,3,5,7,8,10,12,14,16], + [0,1,3,6,7,9,10,12,14,15], + [0,1,3,4,5,7,8,9,15,16], + [1,4,5,6,9,10,13,14,15,16]], + 151: [[0,3,5,6,8,11,13,14,16,19,21,23,25,27,28], + [2,3,6,13,16,17,20,23,25,26,27,28,29], + [0,1,2,3,4,6,7,8,9,10,11,12,23,24,27,28], + [1,4,5,10,11,12,13,14,16,18,19,22,25,26,27,28]], + 157:[[0,2,5,7,8,11], + [0,4,5,6,9,11], + [6,7,8,9,10,11], + [0,5,6,7,8,10,11]], + 163: [[0,2,5,6,9,10,13,14,17], + [0,1,7,10,12,15,16,17], + [0,1,3,5,8,13,15,16,17], + [3,6,7,8,11,12,13,14,16,17]], + 181: [[0,3,5,6,8,10,13,15,16,19], + [4,5,7,8,11,14,15,16,18,19], + [0,4,10,11,13,15,16,18,19], + [2,4,5,7,11,13,15,17,19]], + 241: [[0,2,4,6,8,11,12,14], + [1,3,4,6,7,13,14,15], + [6,8,9,10,12,13,14,15], + [3,4,5,9,10,13,14]], + } + + cosets_gens = { + 67: [1,2,3,4,5,6,8,10,12,15,17], + 103: [1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 15, 17, 19, 21, 23, 30], + 113: [1, 2, 3, 5, 6, 9, 10, 13], + 127: [1, 3, 5, 7, 9, 11, 13, 19, 21], + 151: [1, 2, 3 ,4, 5, 6, 9, 10, 11, 12, 15, 22, 27, 29, 30], + 157: [1, 2, 3, 5, 9, 15], + 163: [1, 2, 3, 5, 6, 9, 10, 15, 18], + 181: [1, 2, 3, 4, 6, 7, 8, 12, 13, 24], + 241: [1, 2, 4, 5, 7, 13, 19, 35], + } + + H_db = { + 67: [1, 29, 37], + 103: [1, 46, 56], + 113: [1,16,28,30,49,106,109], + 127: [1,2,4,8,16,32,64], + 151: [1, 8,19,59, 64], + 157: [1,14,16,39,46,67,75,93,99,101,108,130,153], + 163: [1,38,40,53,58,85,104,133,140], + 181: [1,39,43,48,62,65,73,80,132], + 241: [1,15,24,54,87,91,94,98,100,119,160,183,205,225,231], + } + + def generate_set(index_set, cosets): + S = [] + for idx in index_set: + S += cosets[idx] + return S + + + if existence: + return n in indices + + if n not in indices: + raise ValueError(f'Skew SDS of order {n} not yet implemented.') + + Z = Zmod(n) + H = list(map(Z, H_db[n])) + + cosets = [] + for el in cosets_gens[n]: + even_coset = [] + odd_coset = [] + for x in H: + even_coset.append(x*el) + odd_coset.append(-x*el) + cosets.append(even_coset) + cosets.append(odd_coset) + + S1 = generate_set(indices[n][0], cosets) + S2 = generate_set(indices[n][1], cosets) + S3 = generate_set(indices[n][2], cosets) + S4 = generate_set(indices[n][3], cosets) + + if check: + lmbda = len(S1)+len(S2)+len(S3)+len(S4) - n + assert is_supplementary_difference_set([S1, S2, S3, S4], n, lmbda) + assert _is_skew_set(S1, n) + + return S1, S2, S3, S4 + +def _is_skew_set(S, n): + r"""Check if `S` is a skew set over the set of integers modulo `n`. + + From [Djo1994]_, a set `S \subset G` (where `G` is a finite abelian group of order `n`) is of skew + type if `S_1 \cap (-S_1) \eq \emptyset` and `S_1 \cup (-S_1) \eq G\\ \{0\}`. + + INPUT: + + - ``S`` -- the set to be checked, containing integers modulo `n`. + + - ``n`` -- the order of `G`. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import _is_skew_set + sage: Z5 = Zmod(5) + sage: _is_skew_set([Z5(1), Z5(2)], 5) + True + sage: _is_skew_set([Z5(1), Z5(2), Z5(3)], 5) + False + sage: _is_skew_set([Z5(1)], 5) + False + """ + G = Zmod(n) + for el in S: + if -el in S: + return False + for el in G: + if el == 0: + continue + if el not in S and -el not in S: + return False + return True + def difference_family(v, k, l=1, existence=False, explain_construction=False, check=True): r""" Return a (``k``, ``l``)-difference family on an Abelian group of cardinality ``v``. From fbc27fc5633ea6084038177ff34c595829abf38a Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Thu, 8 Dec 2022 18:15:58 +0100 Subject: [PATCH 15/19] Add construction of skew Hadamard matrices from skew SDS --- src/sage/combinat/matrices/hadamard_matrix.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 59374abee63..8d95323ff03 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -55,6 +55,7 @@ #***************************************************************************** from urllib.request import urlopen +from sage.combinat.designs.difference_family import skew_supplementary_difference_set from sage.rings.integer_ring import ZZ from sage.matrix.constructor import matrix, block_matrix, block_diagonal_matrix, diagonal_matrix @@ -1927,6 +1928,10 @@ def GS_skew_hadamard_smallcases(n, existence=False, check=True): :func:`sage.combinat.matrices.hadamard_matrix.williamson_goethals_seidel_skew_hadamard_matrix` Matrices for `n=36` and `52` are given in [GS70s]_. Matrices for `n=92` are given in [Wall71]_. + + Additional data is obtained from skew supplementary difference sets contained in + :func:`sage.combinat.designs.difference_family.skew_supplementary_difference_set`, using the + construction described in [Djo1992]_. INPUT: @@ -1953,7 +1958,7 @@ def pmtoZ(s): return [1 if x == '+' else -1 for x in s] if existence: - return n in [36, 52, 92] + return n in [36, 52, 92] or skew_supplementary_difference_set(n//4, existence=True) if n == 36: a = [ 1, 1, 1, -1, 1, -1, 1, -1, -1] @@ -1974,6 +1979,16 @@ def pmtoZ(s): c = [1, 1,-1,-1,-1, 1,-1, 1,-1, 1,-1, 1, 1,-1, 1,-1, 1,-1, 1,-1,-1,-1, 1] d = [1,-1,-1,-1,-1, 1,-1,-1, 1,-1,-1, 1, 1,-1,-1, 1,-1,-1, 1,-1,-1,-1,-1] return WGS(a, b, c, d, check=check) + + if skew_supplementary_difference_set(n//4, existence=True): + t = n//4 + S1, S2, S3, S4 = skew_supplementary_difference_set(t, check=False) + a = [-1 if i in S1 else 1 for i in range(t)] + b = [-1 if i in S2 else 1 for i in range(t)] + c = [-1 if i in S3 else 1 for i in range(t)] + d = [-1 if i in S4 else 1 for i in range(t)] + return WGS(a, b, c, d, check=check) + return None _skew_had_cache={} From 7a9e0025ee3dbf89a8af0b90a2b32f061f39e3de Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Thu, 8 Dec 2022 19:36:04 +0100 Subject: [PATCH 16/19] fix documentation in difference_family --- src/sage/combinat/designs/difference_family.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index 0c9af9cc871..251e6afd259 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -1310,8 +1310,8 @@ def _create_m_sequence(q, n, check=True): from a primitive function over the finite field `GF(q)`. Given a primitive function `f=c_0+c_1x+...+c_nx^n` over `K=GF(q)` of degree `n`, - the recurrence is given by: `a_i = -c_0^{-1}(c_1a_{i-1}+...+c_na{i-n}). - The first n elements will be `0,0,...,0,1` and these will give a maximal length recurrence sequence + the recurrence is given by: `a_i = -c_0^{-1}(c_1a_{i-1}+...+c_na{i-n})`. + The first `n` elements will be `0,0,...,0,1` and these will give a maximal length recurrence sequence as shown in [Mit2008]_. INPUT: @@ -1374,7 +1374,7 @@ def _get_submodule_of_order(G, order): INPUT: - - ``G`` --an additive abelian group. + - ``G`` -- an additive abelian group. - ``order`` -- integer, the order of the desired syubmodule. @@ -1602,11 +1602,11 @@ def is_relative_difference_set(R, G, H, params, verbose =False): return True def is_supplementary_difference_set(Ks, v, lmbda): - r"""Check that the sets in ``Ks`` are `n-\{v; k_1,...,k_n; \lambda \} supplementary difference sets. + r"""Check that the sets in ``Ks`` are `n-\{v; k_1,...,k_n; \lambda \}` supplementary difference sets. From the definition in [Spe1975]_: let `S_1, S_2, ..., S_n` be n subsets of an additive abelian group `G` of order `v` such that `|S_i|\eq k_i`. If, for each `g\in G`, `g \neq 0`, the total number of solutions of `a_i-a'_i \eq g`, with - `a_i,a'_i \in S_i` is `\lambda`, then `S_1, S_2, ..., S_n` are `n-\{v; k_1,...,k_n;\lambda\} supplementary difference sets. + `a_i,a'_i \in S_i` is `\lambda`, then `S_1, S_2, ..., S_n` are `n-\{v; k_1,...,k_n;\lambda\}` supplementary difference sets. INPUT: @@ -1629,7 +1629,7 @@ def is_supplementary_difference_set(Ks, v, lmbda): .. SEEALSO:: - :func: `supplementary_difference_set` + :func:`supplementary_difference_set` """ from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup @@ -1721,7 +1721,7 @@ def supplementary_difference_set(q, existence=False, check=True): .. SEEALSO:: - :func: `is_supplementary_difference_set` + :func:`is_supplementary_difference_set` """ s = 0 From eb3474de2516c56dd4eac90f18fde3c9d05fde96 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Thu, 8 Dec 2022 20:33:40 +0100 Subject: [PATCH 17/19] fix seealso directive in T sequences docs --- src/sage/combinat/t_sequences.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/t_sequences.py b/src/sage/combinat/t_sequences.py index e40ef3142b0..414aa2253fd 100644 --- a/src/sage/combinat/t_sequences.py +++ b/src/sage/combinat/t_sequences.py @@ -594,7 +594,7 @@ def base_sequences_construction(turyn_type_seqs, check=True): ... AssertionError - .. SEALSO:: + .. SEEALSO:: :func:`is_base_sequences_tuple` """ @@ -661,7 +661,7 @@ def is_base_sequences_tuple(base_sequences, verbose=False): sage: is_base_sequences_tuple(seqs) False - .. SEALSO:: + .. SEEALSO:: :func:`base_sequences_construction` """ From 3d6f3fb5f0f77a7e0de494f6f30dc49896c4063d Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Fri, 9 Dec 2022 09:58:54 +0100 Subject: [PATCH 18/19] Fix failing test in base_sequences_smallcases --- src/sage/combinat/t_sequences.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/t_sequences.py b/src/sage/combinat/t_sequences.py index 414aa2253fd..78a7ea37a09 100644 --- a/src/sage/combinat/t_sequences.py +++ b/src/sage/combinat/t_sequences.py @@ -829,7 +829,7 @@ def base_sequences_smallcases(n, p, existence=False, check=True): sage: base_sequences_smallcases(7, 5) Traceback (most recent call last): ... - ValueError: Data is only present for base sequences of the form 2n-1, 2n-1, n, n + ValueError: Base sequences of order 12, 12, 7, 7 not yet implemented. sage: seqs = base_sequences_smallcases(16, 15) sage: len(seqs[0]) == len(seqs[1]) == 16+15 True From 1f624a44bcd789d9788c693a262699db4313edd8 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Fri, 9 Dec 2022 10:24:50 +0100 Subject: [PATCH 19/19] Fix typos in documentations --- .../combinat/designs/difference_family.py | 33 +++++++++---------- src/sage/combinat/matrices/hadamard_matrix.py | 2 +- src/sage/combinat/t_sequences.py | 14 +++++--- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index 251e6afd259..8846dcd06bc 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -1395,7 +1395,7 @@ def _get_submodule_of_order(G, order): return None def relative_difference_set_from_m_sequence(q, N, check=True): - r"""Construct `R((q^N-1)/(q-1), q-1, q^{N-1}, q^{N-2})` where q is a prime power and `N\ge 2`. + r"""Construct `R((q^N-1)/(q-1), q-1, q^{N-1}, q^{N-2})` where `q` is a prime power and `N\ge 2`. The relative difference set is constructed over the set of additive integers modulo `q^N-1`, as described in Theorem 5.1 of [EB1966]_. Given an m-sequence `(a_i)` of period `q^N-1`, the @@ -1405,7 +1405,7 @@ def relative_difference_set_from_m_sequence(q, N, check=True): - ``q`` -- a prime power. - - ``N`` -- a nonegative number. + - ``N`` -- a nonnegative number. - ``check`` -- boolean (default True). If true, check that the result is a relative difference set before returning it. @@ -1455,7 +1455,7 @@ def relative_difference_set_from_m_sequence(q, N, check=True): return set1 def relative_difference_set_from_homomorphism(q, N, d, check=True): - r"""Construct `R((q^N-1)/(q-1), n, q^{N-1}, q^{N-2}*d)` where `nd = q-1`. + r"""Construct `R((q^N-1)/(q-1), n, q^{N-1}, q^{N-2}d)` where `nd = q-1`. Given a prime power `q`, a number `N \ge 2` and integers `d` such that `d | q-1` we create the relative difference set using the construction from Corollary 5.1.1 of [EB1966]_. @@ -1525,7 +1525,7 @@ def is_relative_difference_set(R, G, H, params, verbose =False): r"""Check if `R` is a difference set of `G` relative to `H`, with the given parameters. This function checks that `G`, `H` and `R` have the orders specified in the parameters, and - that R satisfies the definition of relative difference set (from [EB1966]_): the collection of + that `R` satisfies the definition of relative difference set (from [EB1966]_): the collection of differences `r-s`, `r,s \in R`, `r \neq s` contains only elements of `G` which are not in `H`, and contains every such element exactly `d` times. @@ -1604,8 +1604,8 @@ def is_relative_difference_set(R, G, H, params, verbose =False): def is_supplementary_difference_set(Ks, v, lmbda): r"""Check that the sets in ``Ks`` are `n-\{v; k_1,...,k_n; \lambda \}` supplementary difference sets. - From the definition in [Spe1975]_: let `S_1, S_2, ..., S_n` be n subsets of an additive abelian group `G` of order `v` - such that `|S_i|\eq k_i`. If, for each `g\in G`, `g \neq 0`, the total number of solutions of `a_i-a'_i \eq g`, with + From the definition in [Spe1975]_: let `S_1, S_2, ..., S_n` be `n` subsets of an additive abelian group `G` of order `v` + such that `|S_i|= k_i`. If, for each `g\in G`, `g \neq 0`, the total number of solutions of `a_i-a'_i = g`, with `a_i,a'_i \in S_i` is `\lambda`, then `S_1, S_2, ..., S_n` are `n-\{v; k_1,...,k_n;\lambda\}` supplementary difference sets. INPUT: @@ -1656,7 +1656,7 @@ def supplementary_difference_set(q, existence=False, check=True): r"""Construct `4-\{2v; v, v+1, v, v; 2v\}` supplementary difference sets where `q=2v+1`. The sets are created from relative difference sets as detailed in Theorem 3.3 of [Spe1975]_. this construction - requires that q is an odd prime power and that there exists `s \ge 0` such that `(q-(2^{s+1}+1))/2^{s+1}` is + requires that `q` is an odd prime power and that there exists `s \ge 0` such that `(q-(2^{s+1}+1))/2^{s+1}` is an odd prime power. Note that the construction from [Spe1975]_ states that the resulting sets are `4-\{2v; v+1, v, v, v; 2v\}` @@ -1822,7 +1822,7 @@ def _get_fixed_relative_difference_set(rel_diff_set, as_elements=False): Given a relative difference set `R(q+1, q-1, q, 1)`, it is possible to find a translation of this set fixed by `q` (see Section 3 of [Spe1975]_). We say that a set is fixed by `t` if - `\{td | d\in R\}\eq R`. + `\{td | d\in R\}= R`. In addition, the set returned by this function will contain the element `0`. This is needed in the construction of supplementary difference sets (see :func:`supplementary_difference_set`). @@ -1895,7 +1895,7 @@ def _get_fixed_relative_difference_set(rel_diff_set, as_elements=False): def _is_fixed_relative_difference_set(R, q): r"""Check if the relative difference set `R` is fixed by `q`. - A relative difference set `R` is fixed by `q` if `\{qd | d\in R\}\eq R` (see Section 3 of [Spe1975]_). + A relative difference set `R` is fixed by `q` if `\{qd | d\in R\}= R` (see Section 3 of [Spe1975]_). INPUT: @@ -1931,12 +1931,12 @@ def _is_fixed_relative_difference_set(R, q): def skew_supplementary_difference_set(n, existence=False, check=True): - r"""Construct `4-\{n; n_1, n_2, n_3, n_4; \lambda\}` supplementary difference sets where `S_1` is skew and `n_1+n_2+n_3+n_4\eq n+\lambda`. + r"""Construct `4-\{n; n_1, n_2, n_3, n_4; \lambda\}` supplementary difference sets where `S_1` is skew and `n_1+n_2+n_3+n_4= n+\lambda`. These sets are constructed from available data, as described in [Djo1994]_. The set `S_1 \subset G` is - always skew, i.e. `S_1 \cap (-S_1) \eq \emptyset` and `S_1 \cup (-S_1) \eq G\\\{0\}`. + always skew, i.e. `S_1 \cap (-S_1) = \emptyset` and `S_1 \cup (-S_1) = G\setminus\{0\}`. - The data for `n\eq 103, 151` is taken from [Djo1994]_ and the data for `n\eq 67, 113, 127, 157, 163, 181, 241` + The data for `n = 103, 151` is taken from [Djo1994]_ and the data for `n = 67, 113, 127, 157, 163, 181, 241` is taken from [Djo1992]_. INPUT: @@ -1946,15 +1946,14 @@ def skew_supplementary_difference_set(n, existence=False, check=True): - ``existence`` -- boolean (dafault False). If true, only check whether the supplementary difference sets can be constructed. - - ``check`` -- boolean (default True). If true, check that the sets are supplementary difference sets with - `S_1` skew before returning them. Setting this parameter to False may speed up the computation considerably. - + - ``check`` -- boolean (default True). If true, check that the sets are supplementary difference sets + with `S_1` skew before returning them. Setting this parameter to False may speed up the computation considerably. OUTPUT: If ``existence`` is false, the function returns the 4 sets (containing integers modulo `n`), or raises an error if data for the given ``n`` is not available. - If ``existence`` is true, the function returns a boolean representing whether supplementary difference + If ``existence`` is true, the function returns a boolean representing whether skew supplementary difference sets can be constructed. EXAMPLES:: @@ -2098,7 +2097,7 @@ def _is_skew_set(S, n): r"""Check if `S` is a skew set over the set of integers modulo `n`. From [Djo1994]_, a set `S \subset G` (where `G` is a finite abelian group of order `n`) is of skew - type if `S_1 \cap (-S_1) \eq \emptyset` and `S_1 \cup (-S_1) \eq G\\ \{0\}`. + type if `S_1 \cap (-S_1) = \emptyset` and `S_1 \cup (-S_1) = G\setminus \{0\}`. INPUT: diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 8d95323ff03..af7e3bd047f 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -804,7 +804,7 @@ def hadamard_matrix_cooper_wallis_smallcases(n, check=True, existence=False): This function calls the function :func:`hadamard_matrix_cooper_wallis_construction` with the appropriate arguments. It constructs the matrices `X_1`, `X_2`, `X_3`, `X_4` using either - T-matrices or the T-sequences from :func:`sage.combinat.t_sequences.t_sequences_smallcases`. + T-matrices or the T-sequences from :func:`sage.combinat.t_sequences.T_sequences_smallcases`. The matrices `A`, `B`, `C`, `D` are taken from :func:`williamson_type_quadruples_smallcases`. Data for T-matrices of order 67 is taken from [Saw1985]_. diff --git a/src/sage/combinat/t_sequences.py b/src/sage/combinat/t_sequences.py index 78a7ea37a09..fb20500ab28 100644 --- a/src/sage/combinat/t_sequences.py +++ b/src/sage/combinat/t_sequences.py @@ -463,7 +463,7 @@ def T_sequences_smallcases(t, existence=False, check=True): - ``t`` -- integer, the length of the T-sequences to construct. - - ``existence`` -- boolean (default false). If true, thsi method only returns whether a T-sequences of + - ``existence`` -- boolean (default false). If true, this method only returns whether a T-sequences of the given size can be constructed. - ``check`` -- boolean, if true (default) check that the sequences are T-sequences before returning them. @@ -621,7 +621,7 @@ def is_base_sequences_tuple(base_sequences, verbose=False): .. MATH:: - N_A(j)+N_B(j)+N_C(j)+N_D(j) \eq 0 + N_A(j)+N_B(j)+N_C(j)+N_D(j) = 0 where `N_X(j)` is the nonperiodic autocorrelation (See definition in [KTR2005]_). @@ -698,7 +698,7 @@ def turyn_type_sequences_smallcases(n, existence=False): r""" Construction of Turyn type sequences for small values of `n`. - The data is taken from [KTR2005]_ for `n\eq 36`, and from [BDKR2013]_ for `n\le 32`. + The data is taken from [KTR2005]_ for `n= 36`, and from [BDKR2013]_ for `n\le 32`. INPUT: @@ -787,8 +787,12 @@ def convertLists(hexstring): def base_sequences_smallcases(n, p, existence=False, check=True): r"""Construct base sequences of length `n+p, n+p, n, n` from available data. - The function uses the function :func:`base_sequences_construction`, together with - base sequences from :func:`turyn_type_sequences_smallcases` to construct base sequences with `p \eq n-1`. + The function uses the construction :func:`base_sequences_construction`, together with + Turyn type sequences from :func:`turyn_type_sequences_smallcases` to construct base sequences + with `p = n-1`. + + Furthermore, this function uses also Turyn sequences (i.e. base sequences with `p=1`) from + :func:`turyn_sequences_smallcases`. INPUT: