Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/combinat/designs/block_design.py
4079 views
1
"""
2
Block designs.
3
4
A module to help with constructions and computations of block
5
designs and other incidence structures.
6
7
A block design is an incidence structure consisting of a set of
8
points P and a set of blocks B, where each block is considered as a
9
subset of P. More precisely, a *block design* B is a class of
10
k-element subsets of P such that the number r of blocks that
11
contain any point x in P is independent of x, and the number lambda
12
of blocks that contain any given t-element subset T is independent
13
of the choice of T (see [1] for more). Such a block design is also
14
called a t-(v,k,lambda)-design, and v (the number of points), b
15
(the number of blocks), k, r, and lambda are the parameters of the
16
design. (In Python, lambda is reserved, so we sometimes use lmbda
17
or L instead.)
18
19
In Sage, sets are replaced by (ordered) lists and the standard
20
representation of a block design uses P = [0,1,..., v-1], so a
21
block design is specified by (v,B).
22
23
This software is released under the terms of the GNU General Public
24
License, version 2 or above (your choice). For details on
25
licensing, see the accompanying documentation.
26
27
REFERENCES:
28
29
- [1] Block design from wikipedia,
30
http://en.wikipedia.org/wiki/Block_design
31
32
- [2] What is a block design?,
33
http://designtheory.org/library/extrep/html/node4.html (in 'The
34
External Representation of Block Designs' by Peter J. Cameron, Peter
35
Dobcsanyi, John P. Morgan, Leonard H. Soicher)
36
37
This is a significantly modified form of the module
38
block_design.py (version 0.6) written by Peter Dobcsanyi
39
[email protected]. Thanks go to Robert Miller for lots of good
40
design suggestions.
41
42
Copyright 2007-2008 by Peter Dobcsanyi [email protected], and
43
David Joyner [email protected].
44
45
TODO: Implement DerivedDesign, ComplementaryDesign,
46
Hadamard3Design
47
"""
48
49
from sage.modules.free_module import VectorSpace
50
from sage.rings.integer_ring import ZZ
51
from sage.rings.arith import binomial, integer_floor
52
from sage.combinat.designs.incidence_structures import IncidenceStructure, IncidenceStructureFromMatrix
53
from sage.misc.decorators import rename_keyword
54
55
### utility functions -------------------------------------------------------
56
57
def tdesign_params(t, v, k, L):
58
"""
59
Return the design's parameters: (t, v, b, r , k, L). Note t must be
60
given.
61
62
EXAMPLES::
63
64
sage: BD = BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]])
65
sage: from sage.combinat.designs.block_design import tdesign_params
66
sage: tdesign_params(2,7,3,1)
67
(2, 7, 7, 3, 3, 1)
68
"""
69
x = binomial(v, t)
70
y = binomial(k, t)
71
b = divmod(L * x, y)[0]
72
x = binomial(v-1, t-1)
73
y = binomial(k-1, t-1)
74
r = integer_floor(L * x/y)
75
return (t, v, b, r, k, L)
76
77
@rename_keyword(deprecated='Sage version 4.6', method="algorithm")
78
def ProjectiveGeometryDesign(n, d, F, algorithm=None):
79
"""
80
Input: n is the projective dimension, so the number of points is v
81
= PPn(GF(q)) d is the dimension of the subspaces of P = PPn(GF(q))
82
which make up the blocks, so b is the number of d-dimensional
83
subspaces of P
84
85
Wraps GAP Design's PGPointFlatBlockDesign. Does *not* require
86
GAP's Design.
87
88
EXAMPLES::
89
90
sage: ProjectiveGeometryDesign(2, 1, GF(2))
91
Incidence structure with 7 points and 7 blocks
92
sage: BD = ProjectiveGeometryDesign(2, 1, GF(2), algorithm="gap") # requires optional gap package 'design'
93
sage: BD.is_block_design() # requires optional gap package 'design'
94
(True, [2, 7, 3, 1])
95
"""
96
q = F.order()
97
from sage.interfaces.gap import gap, GapElement
98
from sage.sets.set import Set
99
if algorithm == None:
100
V = VectorSpace(F, n+1)
101
points = list(V.subspaces(1))
102
flats = list(V.subspaces(d+1))
103
blcks = []
104
for p in points:
105
b = []
106
for i in range(len(flats)):
107
if p.is_subspace(flats[i]):
108
b.append(i)
109
blcks.append(b)
110
v = (q**(n+1)-1)/(q-1)
111
return BlockDesign(v, blcks, name="ProjectiveGeometryDesign")
112
if algorithm == "gap": # Requires GAP's Design
113
gap.eval('LoadPackage("design")')
114
gap.eval("D := PGPointFlatBlockDesign( %s, %s, %d )"%(n,q,d))
115
v = eval(gap.eval("D.v"))
116
gblcks = eval(gap.eval("D.blocks"))
117
gB = []
118
for b in gblcks:
119
gB.append([x-1 for x in b])
120
return BlockDesign(v, gB, name="ProjectiveGeometryDesign")
121
122
def AffineGeometryDesign(n, d, F):
123
r"""
124
Input: n is the Euclidean dimension, so the number of points is
125
`v = |F^n|` (F = GF(q), some q) d is the dimension of the
126
(affine) subspaces of `P = GF(q)^n` which make up the
127
blocks.
128
129
`AG_{n,d} (F)`, as it is sometimes denoted, is a
130
`2` - `(v, k, \lambda)` design of points and
131
`d`- flats (cosets of dimension n) in the affine geometry
132
`AG_n (F)`, where
133
134
.. math::
135
136
v = q^n,\ k = q^d ,
137
\lambda =\frac{(q^{n-1}-1) \cdots (q^{n+1-d}-1)}{(q^{n-1}-1) \cdots (q-1)}.
138
139
140
141
Wraps some functions used in GAP Design's PGPointFlatBlockDesign.
142
Does *not* require GAP's Design.
143
144
EXAMPLES::
145
146
sage: BD = AffineGeometryDesign(3, 1, GF(2))
147
sage: BD.parameters()
148
(2, 8, 2, 2)
149
sage: BD.is_block_design()
150
(True, [2, 8, 2, 2])
151
sage: BD = AffineGeometryDesign(3, 2, GF(2))
152
sage: BD.parameters()
153
(2, 8, 4, 12)
154
sage: BD.is_block_design()
155
(True, [3, 8, 4, 4])
156
"""
157
q = F.order()
158
from sage.interfaces.gap import gap, GapElement
159
from sage.sets.set import Set
160
gap.eval("V:=GaloisField(%s)^%s"%(q,n))
161
gap.eval("points:=AsSet(V)")
162
gap.eval("Subs:=AsSet(Subspaces(V,%s));"%d)
163
gap.eval("CP:=Cartesian(points,Subs)")
164
flats = gap.eval("flats:=List(CP,x->Sum(x))") # affine spaces
165
gblcks = eval(gap.eval("AsSortedList(List(flats,f->Filtered([1..Length(points)],i->points[i] in f)));"))
166
v = q**n
167
gB = []
168
for b in gblcks:
169
gB.append([x-1 for x in b])
170
return BlockDesign(v, gB, name="AffineGeometryDesign")
171
172
def WittDesign(n):
173
"""
174
Input: n is in 9,10,11,12,21,22,23,24.
175
176
Wraps GAP Design's WittDesign. If n=24 then this function returns
177
the large Witt design W24, the unique (up to isomorphism)
178
5-(24,8,1) design. If n=12 then this function returns the small
179
Witt design W12, the unique (up to isomorphism) 5-(12,6,1) design.
180
The other values of n return a block design derived from these.
181
182
REQUIRES: GAP's Design package.
183
184
EXAMPLES::
185
186
sage: BD = WittDesign(9) # requires optional gap package 'design'
187
sage: BD.parameters() # requires optional gap package 'design'
188
(2, 9, 3, 1)
189
sage: BD # requires optional gap package 'design'
190
Incidence structure with 9 points and 12 blocks
191
sage: print BD # requires optional gap package 'design'
192
WittDesign<points=[0, 1, 2, 3, 4, 5, 6, 7, 8], blocks=[[0, 1, 7], [0, 2, 5], [0, 3, 4], [0, 6, 8], [1, 2, 6], [1, 3, 5], [1, 4, 8], [2, 3, 8], [2, 4, 7], [3, 6, 7], [4, 5, 6], [5, 7, 8]]>
193
sage: BD = WittDesign(12) # requires optional gap package 'design'
194
sage: BD.parameters(t=5) # requires optional gap package 'design'
195
(5, 12, 6, 1)
196
"""
197
from sage.interfaces.gap import gap, GapElement
198
gap.eval('LoadPackage("design")')
199
gap.eval("B:=WittDesign(%s)"%n)
200
v = eval(gap.eval("B.v"))
201
gblcks = eval(gap.eval("B.blocks"))
202
gB = []
203
for b in gblcks:
204
gB.append([x-1 for x in b])
205
return BlockDesign(v, gB, name="WittDesign", test=True)
206
207
def HadamardDesign(n):
208
"""
209
As described in Section 1, p. 10, in [CvL]. The input n must have the
210
property that there is a Hadamard matrix of order n+1 (and that a
211
construction of that Hadamard matrix has been implemented...).
212
213
EXAMPLES::
214
215
sage: HadamardDesign(7)
216
Incidence structure with 7 points and 7 blocks
217
sage: print HadamardDesign(7)
218
HadamardDesign<points=[0, 1, 2, 3, 4, 5, 6], blocks=[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]]>
219
220
REFERENCES:
221
222
- [CvL] P. Cameron, J. H. van Lint, Designs, graphs, codes and
223
their links, London Math. Soc., 1991.
224
"""
225
from sage.combinat.matrices.hadamard_matrix import hadamard_matrix
226
from sage.matrix.constructor import matrix
227
H = hadamard_matrix(n+1)
228
H1 = H.matrix_from_columns(range(1,n+1))
229
H2 = H1.matrix_from_rows(range(1,n+1))
230
J = matrix(ZZ,n,n,[1]*n*n)
231
MS = J.parent()
232
A = MS((H2+J)/2) # convert -1's to 0's; coerce entries to ZZ
233
# A is the incidence matrix of the block design
234
return IncidenceStructureFromMatrix(A,name="HadamardDesign")
235
236
def steiner_triple_system(n):
237
r"""
238
Returns a Steiner Triple System
239
240
A Steiner Triple System (STS) of a set `\{0,...,n-1\}`
241
is a family `S` of 3-sets such that for any `i \not = j`
242
there exists exactly one set of `S` in which they are
243
both contained.
244
245
It can alternatively be thought of as a factorization of
246
the complete graph `K_n` with triangles.
247
248
A Steiner Triple System of a `n`-set exists if and only if
249
`n \equiv 1 mod 6` or `n \equiv 3 mod 6`, in which case
250
one can be found through Bose's and Skolem's constructions,
251
respectively [AndHon97]_.
252
253
INPUT:
254
255
- ``n`` returns a Steiner Triple System of `\{0,...,n-1\}`
256
257
EXAMPLE:
258
259
A Steiner Triple System on `9` elements ::
260
261
sage: from sage.combinat.designs.block_design import steiner_triple_system
262
sage: sts = steiner_triple_system(9)
263
sage: sts
264
Incidence structure with 9 points and 12 blocks
265
sage: list(sts)
266
[[0, 1, 5], [0, 2, 4], [0, 3, 6], [0, 7, 8], [1, 2, 3], [1, 4, 7], [1, 6, 8], [2, 5, 8], [2, 6, 7], [3, 4, 8], [3, 5, 7], [4, 5, 6]]
267
268
As any pair of vertices is covered once, its parameters are ::
269
270
sage: sts.parameters()
271
(2, 9, 3, 1)
272
273
An exception is raised for invalid values of ``n`` ::
274
275
sage: steiner_triple_system(10)
276
Traceback (most recent call last):
277
...
278
ValueError: Steiner triple systems only exist for n = 1 mod 6 or n = 3 mod 6
279
280
REFERENCE:
281
282
.. [AndHon97] A short course in Combinatorial Designs,
283
Ian Anderson, Iiro Honkala,
284
Internet Editions, Spring 1997,
285
http://www.utu.fi/~honkala/designs.ps
286
"""
287
288
name = "Steiner Triple System on "+str(n)+" elements"
289
290
if n%6 == 3:
291
t = (n-3)/6
292
Z = range(2*t+1)
293
294
T = lambda (x,y) : x + (2*t+1)*y
295
296
sts = [[(i,0),(i,1),(i,2)] for i in Z] + \
297
[[(i,k),(j,k),(((t+1)*(i+j)) % (2*t+1),(k+1)%3)] for k in range(3) for i in Z for j in Z if i != j]
298
299
elif n%6 == 1:
300
301
t = (n-1)/6
302
N = range(2*t)
303
T = lambda (x,y) : x+y*t*2 if (x,y) != (-1,-1) else n-1
304
305
L1 = lambda i,j : (i+j) % (int((n-1)/3))
306
L = lambda i,j : L1(i,j)/2 if L1(i,j)%2 == 0 else t+(L1(i,j)-1)/2
307
308
sts = [[(i,0),(i,1),(i,2)] for i in range(t)] + \
309
[[(-1,-1),(i,k),(i-t,(k+1) % 3)] for i in range(t,2*t) for k in [0,1,2]] + \
310
[[(i,k),(j,k),(L(i,j),(k+1) % 3)] for k in [0,1,2] for i in N for j in N if i < j]
311
312
else:
313
raise ValueError("Steiner triple systems only exist for n = 1 mod 6 or n = 3 mod 6")
314
315
from sage.sets.set import Set
316
sts = Set(map(lambda x: Set(map(T,x)),sts))
317
318
return BlockDesign(n, sts, name=name)
319
320
def BlockDesign(max_pt, blks, name=None, test=True):
321
"""
322
Returns an instance of the IncidenceStructure class. Requires each
323
B in blks to be contained in range(max_pt). Does not test if the
324
result is a block design.
325
326
EXAMPLES::
327
328
sage: BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]], name="Fano plane")
329
Incidence structure with 7 points and 7 blocks
330
sage: print BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]], name="Fano plane")
331
Fano plane<points=[0, 1, 2, 3, 4, 5, 6], blocks=[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]]>
332
"""
333
nm = name
334
tst = test
335
if nm == None and test:
336
nm = "BlockDesign"
337
BD = BlockDesign_generic( range(max_pt), blks, name=nm, test=tst )
338
if not(test):
339
return BD
340
else:
341
pars = BD.parameters()
342
if BD.block_design_checker(pars[0],pars[1],pars[2],pars[3]):
343
return BD
344
else:
345
raise TypeError("parameters are not those of a block design.")
346
347
BlockDesign_generic = IncidenceStructure
348
"""
349
Possibly in the future there will be methods which apply to
350
block designs and not incidence structures. None have been
351
implemented yet though. The class name BlockDesign_generic
352
is reserved in the name space in case more specialized
353
methods are implemented later. In that case, BlockDesign_generic
354
should inherit from IncidenceStructure.
355
"""
356
357