# Difference families¶

This module gathers everything related to difference families. One can build a difference family (or check that it can be built) with difference_family():

sage: G,F = designs.difference_family(13,4,1)


It defines the following functions:

 is_difference_family() Return a (k, l)-difference family on an Abelian group of size v. singer_difference_set() Return a difference set associated to hyperplanes in a projective space. difference_family() Return a (k, l)-difference family on an Abelian group of size v.

REFERENCES:

 [Wi72] R. M. Wilson “Cyclotomy and difference families in elementary Abelian groups”, J. of Num. Th., 4 (1972), pp. 17-47.

## Functions¶

sage.combinat.designs.difference_family.difference_family(v, k, l=1, existence=False, check=True)

Return a (k, l)-difference family on an Abelian group of size v.

Let $$G$$ be a finite Abelian group. For a given subset $$D$$ of $$G$$, we define $$\Delta D$$ to be the multi-set of differences $$\Delta D = \{x - y; x \in D, y \in D, x \not= y\}$$. A $$(G,k,\lambda)$$-difference family is a collection of $$k$$-subsets of $$G$$, $$D = \{D_1, D_2, \ldots, D_b\}$$ such that the union of the difference sets $$\Delta D_i$$ for $$i=1,...b$$, seen as a multi-set, contains each element of $$G \backslash \{0\}$$ exactly $$\lambda$$-times.

When there is only one block, i.e. $$\lambda(v - 1) = k(k-1)$$, then a $$(G,k,\lambda)$$-difference family is also called a difference set.

If there is no such difference family, an EmptySetError is raised and if there is no construction at the moment NotImplementedError is raised.

EXAMPLES:

sage: K,D = designs.difference_family(73,4)
sage: D
[[0, 1, 8, 64],
[0, 25, 54, 67],
[0, 41, 36, 69],
[0, 3, 24, 46],
[0, 2, 16, 55],
[0, 50, 35, 61]]

sage: K,D = designs.difference_family(337,7)
sage: D
[[1, 175, 295, 64, 79, 8, 52],
[326, 97, 125, 307, 142, 249, 102],
[121, 281, 310, 330, 123, 294, 226],
[17, 279, 297, 77, 332, 136, 210],
[150, 301, 103, 164, 55, 189, 49],
[35, 59, 215, 218, 69, 280, 135],
[289, 25, 331, 298, 252, 290, 200],
[191, 62, 66, 92, 261, 180, 159]]


For $$k=6,7$$ we look at the set of small prime powers for which a construction is available:

sage: def prime_power_mod(r,m):
....:     k = m+r
....:     while True:
....:         if is_prime_power(k):
....:             yield k
....:         k += m

sage: from itertools import islice
sage: l6 = {True:[], False: [], Unknown: []}
sage: for q in islice(prime_power_mod(1,30), 60):
....:     l6[designs.difference_family(q,6,existence=True)].append(q)
sage: l6[True]
[31, 151, 181, 211, ...,  3061, 3121, 3181]
sage: l6[Unknown]
[61, 121]
sage: l6[False]
[]

sage: l7 = {True: [], False: [], Unknown: []}
sage: for q in islice(prime_power_mod(1,42), 60):
....:     l7[designs.difference_family(q,7,existence=True)].append(q)
sage: l7[True]
[337, 421, 463, 883, 1723, 3067, 3319, 3529, 3823, 3907, 4621, 4957, 5167]
sage: l7[Unknown]
[43, 127, 169, 211, ..., 4999, 5041, 5209]
sage: l7[False]
[]


Other constructions for $$\lambda > 1$$:

sage: for v in xrange(2,100):
....:     constructions = []
....:     for k in xrange(2,10):
....:         for l in xrange(2,10):
....:             if designs.difference_family(v,k,l,existence=True):
....:                 constructions.append((k,l))
....:                 _ = designs.difference_family(v,k,l)
....:     if constructions:
....:         print "%2d: %s"%(v, ', '.join('(%d,%d)'%(k,l) for k,l in constructions))
4: (3,2)
5: (4,3)
7: (3,2), (6,5)
8: (7,6)
9: (4,3), (8,7)
11: (5,2), (5,4)
13: (3,2), (4,3), (6,5)
15: (7,3)
16: (3,2), (5,4)
17: (4,3), (8,7)
19: (3,2), (6,5), (9,4), (9,8)
25: (3,2), (4,3), (6,5), (8,7)
29: (4,3), (7,6)
31: (3,2), (5,4), (6,5)
37: (3,2), (4,3), (6,5), (9,2), (9,8)
41: (4,3), (5,4), (8,7)
43: (3,2), (6,5), (7,6)
49: (3,2), (4,3), (6,5), (8,7)
53: (4,3)
61: (3,2), (4,3), (5,4), (6,5)
64: (3,2), (7,6), (9,8)
67: (3,2), (6,5)
71: (5,4), (7,6)
73: (3,2), (4,3), (6,5), (8,7), (9,8)
79: (3,2), (6,5)
81: (4,3), (5,4), (8,7)
89: (4,3), (8,7)
97: (3,2), (4,3), (6,5), (8,7)


TESTS:

Check more of the Wilson constructions from [Wi72]:

sage: Q5 = [241, 281,421,601,641, 661, 701, 821,881]
sage: Q9 = [73, 1153, 1873, 2017]
sage: Q15 = [76231]
sage: Q4 = [13, 73, 97, 109, 181, 229, 241, 277, 337, 409, 421, 457]
sage: Q8 = [1009, 3137, 3697]
sage: for Q,k in [(Q4,4),(Q5,5),(Q8,8),(Q9,9),(Q15,15)]:
....:     for q in Q:
....:         assert designs.difference_family(q,k,1,existence=True) is True
....:         _ = designs.difference_family(q,k,1)


Check Singer difference sets:

sage: sgp = lambda q,d: ((q**(d+1)-1)//(q-1), (q**d-1)//(q-1), (q**(d-1)-1)//(q-1))

sage: for q in range(2,10):
....:     if is_prime_power(q):
....:         for d in [2,3,4]:
....:           v,k,l = sgp(q,d)
....:           assert designs.difference_family(v,k,l,existence=True) is True
....:           _ = designs.difference_family(v,k,l)


Todo

There is a slightly more general version of difference families where the stabilizers of the blocks are taken into account. A block is short if the stabilizer is not trivial. The more general version is called a partial difference family. It is still possible to construct BIBD from this more general version (see the chapter 16 in the Handbook [DesignHandbook]).

Implement recursive constructions from Buratti “Recursive for difference matrices and relative difference families” (1998) and Jungnickel “Composition theorems for difference families and regular planes” (1978)

sage.combinat.designs.difference_family.group_law(G)

Return a triple (identity, operation, inverse) that define the operations on the group G.

EXAMPLES:

sage: from sage.combinat.designs.difference_family import group_law
sage: group_law(Zmod(3))
(0, <built-in function add>, <built-in function neg>)
sage: group_law(SymmetricGroup(5))
((), <built-in function mul>, <built-in function inv>)
sage: group_law(VectorSpace(QQ,3))
((0, 0, 0), <built-in function add>, <built-in function neg>)

sage.combinat.designs.difference_family.is_difference_family(G, D, v=None, k=None, l=None, verbose=False)

Check wether D forms a difference family in G.

INPUT:

• G - Abelian group of cardinality v
• D - a set of k-subsets of G
• v, k and l - optional parameters of the difference family
• verbose - whether to print additional information

EXAMPLES:

sage: from sage.combinat.designs.difference_family import is_difference_family
sage: G = Zmod(21)
sage: D = [[0,1,4,14,16]]
sage: is_difference_family(G, D, 21, 5)
True

sage: G = Zmod(41)
sage: D = [[0,1,4,11,29],[0,2,8,17,21]]
sage: is_difference_family(G, D, verbose=True)
the element 28 in G is obtained more than 1 times
False
sage: D = [[0,1,4,11,29],[0,2,8,17,22]]
sage: is_difference_family(G, D)
True

sage: G = Zmod(61)
sage: D = [[0,1,3,13,34],[0,4,9,23,45],[0,6,17,24,32]]
sage: is_difference_family(G, D)
True

sage: a,b,c,d = G.gens()
sage: D = [[d, -a+d, -c+d, a-b-d, b+c+d],
....:      [c, a+b-d, -b+c, a-b+d, a+b+c],
....:      [-a-b+c+d, a-b-c-d, -a+c-d, b-c+d, a+b],
....:      [-b-d, a+b+d, a-b+c-d, a-b+c, -b+c+d]]
sage: is_difference_family(G, D)
True


The function also supports multiplicative groups (non necessarily Abelian):

sage: G = DihedralGroup(8)
sage: x,y = G.gens()
sage: D1 = [[1,x,x^4], [1,x^2, y*x], [1,x^5,y], [1,x^6,y*x^2], [1,x^7,y*x^5]]
sage: is_difference_family(G, D1, 16, 3, 2)
True

sage.combinat.designs.difference_family.singer_difference_set(q, d)

Return a difference set associated to the set of hyperplanes in a projective space of dimension $$d$$ over $$GF(q)$$.

A Singer difference set has parameters:

$v = \frac{q^{d+1}-1}{q-1}, \quad k = \frac{q^d-1}{q-1}, \quad \lambda = \frac{q^{d-1}-1}{q-1}.$

The idea of the construction is as follows. One consider the finite field $$GF(q^{d+1})$$ as a vector space of dimension $$d+1$$ over $$GF(q)$$. The set of $$GF(q)$$-lines in $$GF(q^{d+1})$$ is a projective plane and its set of hyperplanes form a balanced incomplete block design.

Now, considering a multiplicative generator $$z$$ of $$GF(q^{d+1})$$, we get a transitive action of a cyclic group on our projective plane from which it is possible to build a difference set.

The construction is given in details in [Stinson2004], section 3.3.

EXAMPLES:

sage: from sage.combinat.designs.difference_family import singer_difference_set, is_difference_family
sage: G,D = singer_difference_set(3,2)
sage: is_difference_family(G,D,verbose=True)
It is a (13,4,1)-difference family
True

sage: G,D = singer_difference_set(4,2)
sage: is_difference_family(G,D,verbose=True)
It is a (21,5,1)-difference family
True

sage: G,D = singer_difference_set(3,3)
sage: is_difference_family(G,D,verbose=True)
It is a (40,13,4)-difference family
True

sage: G,D = singer_difference_set(9,3)
sage: is_difference_family(G,D,verbose=True)
It is a (820,91,10)-difference family
True


#### Previous topic

Orthogonal arrays (Recursive constructions)