Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/combinat/designs/block_design.py
8818 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 points `P` and a
8
set of blocks `B`, where each block is considered as a subset of `P`. More
9
precisely, a *block design* `B` is a class of `k`-element subsets of `P` such
10
that the number `r` of blocks that contain any point `x` in `P` is independent
11
of `x`, and the number `\lambda` of blocks that contain any given `t`-element
12
subset `T` is independent of the choice of `T` (see [1]_ for more). Such a block
13
design is also called a `t-(v,k,\lambda)`-design, and `v` (the number of
14
points), `b` (the number of blocks), `k`, `r`, and `\lambda` are the parameters
15
of the design. (In Python, ``lambda`` is reserved, so we sometimes use ``lmbda``
16
or ``L`` instead.)
17
18
In Sage, sets are replaced by (ordered) lists and the standard representation of
19
a block design uses `P = [0,1,..., v-1]`, so a block design is specified by
20
`(v,B)`.
21
22
REFERENCES:
23
24
.. [1] Block design from wikipedia,
25
:wikipedia:`Block_design`
26
27
.. [2] What is a block design?,
28
http://designtheory.org/library/extrep/extrep-1.1-html/node4.html (in 'The
29
External Representation of Block Designs' by Peter J. Cameron, Peter
30
Dobcsanyi, John P. Morgan, Leonard H. Soicher)
31
32
AUTHORS:
33
34
- Peter Dobcsanyi and David Joyner (2007-2008)
35
36
This is a significantly modified form of the module block_design.py (version
37
0.6) written by Peter Dobcsanyi [email protected]. Thanks go to Robert
38
Miller for lots of good design suggestions.
39
40
41
Functions and methods
42
---------------------
43
"""
44
#***************************************************************************
45
# Copyright (C) 2007 #
46
# #
47
# Peter Dobcsanyi and David Joyner #
48
# <[email protected]> <[email protected]> #
49
# #
50
# #
51
# Distributed under the terms of the GNU General Public License (GPL) #
52
# as published by the Free Software Foundation; either version 2 of #
53
# the License, or (at your option) any later version. #
54
# http://www.gnu.org/licenses/ #
55
#***************************************************************************
56
57
from sage.modules.free_module import VectorSpace
58
from sage.rings.integer_ring import ZZ
59
from sage.rings.arith import binomial, integer_floor
60
from sage.combinat.designs.incidence_structures import IncidenceStructure, IncidenceStructureFromMatrix
61
from sage.misc.decorators import rename_keyword
62
from sage.rings.finite_rings.constructor import FiniteField
63
64
### utility functions -------------------------------------------------------
65
66
def tdesign_params(t, v, k, L):
67
"""
68
Return the design's parameters: `(t, v, b, r , k, L)`. Note that `t` must be
69
given.
70
71
EXAMPLES::
72
73
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]])
74
sage: from sage.combinat.designs.block_design import tdesign_params
75
sage: tdesign_params(2,7,3,1)
76
(2, 7, 7, 3, 3, 1)
77
"""
78
x = binomial(v, t)
79
y = binomial(k, t)
80
b = divmod(L * x, y)[0]
81
x = binomial(v-1, t-1)
82
y = binomial(k-1, t-1)
83
r = integer_floor(L * x/y)
84
return (t, v, b, r, k, L)
85
86
def ProjectiveGeometryDesign(n, d, F, algorithm=None):
87
"""
88
Returns a projective geometry design.
89
90
A projective geometry design of parameters `n,d,F` has for points the lines
91
of `F^{n+1}`, and for blocks the `d+1`-dimensional subspaces of `F^{n+1}`,
92
each of which contains `\\frac {|F|^{d+1}-1} {|F|-1}` lines.
93
94
INPUT:
95
96
- ``n`` is the projective dimension
97
98
- ``d`` is the dimension of the subspaces of `P = PPn(F)` which
99
make up the blocks.
100
101
- ``F`` is a finite field.
102
103
- ``algorithm`` -- set to ``None`` by default, which results in using Sage's
104
own implementation. In order to use GAP's implementation instead (i.e. its
105
``PGPointFlatBlockDesign`` function) set ``algorithm="gap"``. Note that
106
GAP's "design" package must be available in this case.
107
108
EXAMPLES:
109
110
The points of the following design are the `\\frac {2^{2+1}-1} {2-1}=7`
111
lines of `\mathbb{Z}_2^{2+1}`. It has `7` blocks, corresponding to each
112
2-dimensional subspace of `\mathbb{Z}_2^{2+1}`::
113
114
sage: designs.ProjectiveGeometryDesign(2, 1, GF(2))
115
Incidence structure with 7 points and 7 blocks
116
sage: BD = designs.ProjectiveGeometryDesign(2, 1, GF(2), algorithm="gap") # optional - gap_packages (design package)
117
sage: BD.is_block_design() # optional - gap_packages (design package)
118
(True, [2, 7, 3, 1])
119
"""
120
q = F.order()
121
from sage.interfaces.gap import gap, GapElement
122
from sage.sets.set import Set
123
if algorithm is None:
124
V = VectorSpace(F, n+1)
125
points = list(V.subspaces(1))
126
flats = list(V.subspaces(d+1))
127
blcks = []
128
for p in points:
129
b = []
130
for i in range(len(flats)):
131
if p.is_subspace(flats[i]):
132
b.append(i)
133
blcks.append(b)
134
v = (q**(n+1)-1)/(q-1)
135
return BlockDesign(v, blcks, name="ProjectiveGeometryDesign")
136
if algorithm == "gap": # Requires GAP's Design
137
gap.load_package("design")
138
gap.eval("D := PGPointFlatBlockDesign( %s, %s, %d )"%(n,q,d))
139
v = eval(gap.eval("D.v"))
140
gblcks = eval(gap.eval("D.blocks"))
141
gB = []
142
for b in gblcks:
143
gB.append([x-1 for x in b])
144
return BlockDesign(v, gB, name="ProjectiveGeometryDesign")
145
146
def ProjectivePlaneDesign(n, type="Desarguesian"):
147
r"""
148
Returns a projective plane of order `n`.
149
150
A finite projective plane is a 2-design with `n^2+n+1` lines (or blocks) and
151
`n^2+n+1` points. For more information on finite projective planes, see the
152
:wikipedia:`Projective_plane#Finite_projective_planes`.
153
154
INPUT:
155
156
- ``n`` -- the finite projective plane's order
157
158
- ``type`` -- When set to ``"Desarguesian"``, the method returns
159
Desarguesian projective planes, i.e. a finite projective plane obtained by
160
considering the 1- and 2- dimensional spaces of `F_n^3`.
161
162
For the moment, no other value is available for this parameter.
163
164
.. SEEALSO::
165
166
:meth:`ProjectiveGeometryDesign`
167
168
EXAMPLES::
169
170
sage: designs.ProjectivePlaneDesign(2)
171
Incidence structure with 7 points and 7 blocks
172
173
Non-existent ones::
174
175
sage: designs.ProjectivePlaneDesign(10)
176
Traceback (most recent call last):
177
...
178
ValueError: No projective plane design of order 10 exists.
179
sage: designs.ProjectivePlaneDesign(14)
180
Traceback (most recent call last):
181
...
182
ValueError: By the Bruck-Ryser-Chowla theorem, no projective plane of order 14 exists.
183
184
An unknown one::
185
186
sage: designs.ProjectivePlaneDesign(12)
187
Traceback (most recent call last):
188
...
189
ValueError: If such a projective plane exists, we do not know how to build it.
190
191
TESTS::
192
193
sage: designs.ProjectivePlaneDesign(10, type="AnyThingElse")
194
Traceback (most recent call last):
195
...
196
ValueError: The value of 'type' must be 'Desarguesian'.
197
"""
198
from sage.rings.arith import two_squares
199
200
if type != "Desarguesian":
201
raise ValueError("The value of 'type' must be 'Desarguesian'.")
202
203
try:
204
F = FiniteField(n, 'x')
205
except ValueError:
206
if n == 10:
207
raise ValueError("No projective plane design of order 10 exists.")
208
try:
209
if (n%4) in [1,2]:
210
two_squares(n)
211
except ValueError:
212
raise ValueError("By the Bruck-Ryser-Chowla theorem, no projective"
213
" plane of order "+str(n)+" exists.")
214
215
raise ValueError("If such a projective plane exists, "
216
"we do not know how to build it.")
217
218
return ProjectiveGeometryDesign(2,1,F)
219
220
def AffineGeometryDesign(n, d, F):
221
r"""
222
Returns an Affine Geometry Design.
223
224
INPUT:
225
226
- `n` (integer) -- the Euclidean dimension. The number of points is
227
`v=|F^n|`.
228
229
- `d` (integer) -- the dimension of the (affine) subspaces of `P = GF(q)^n`
230
which make up the blocks.
231
232
- `F` -- a Finite Field (i.e. ``FiniteField(17)``), or a prime power
233
(i.e. an integer)
234
235
`AG_{n,d} (F)`, as it is sometimes denoted, is a `2` - `(v, k, \lambda)`
236
design of points and `d`- flats (cosets of dimension `n`) in the affine
237
geometry `AG_n (F)`, where
238
239
.. math::
240
241
v = q^n,\ k = q^d ,
242
\lambda =\frac{(q^{n-1}-1) \cdots (q^{n+1-d}-1)}{(q^{n-1}-1) \cdots (q-1)}.
243
244
Wraps some functions used in GAP Design's ``PGPointFlatBlockDesign``. Does
245
*not* require GAP's Design package.
246
247
EXAMPLES::
248
249
sage: BD = designs.AffineGeometryDesign(3, 1, GF(2))
250
sage: BD.parameters(t=2)
251
(2, 8, 2, 1)
252
sage: BD.is_block_design()
253
(True, [2, 8, 2, 1])
254
sage: BD = designs.AffineGeometryDesign(3, 2, GF(2))
255
sage: BD.parameters(t=3)
256
(3, 8, 4, 1)
257
sage: BD.is_block_design()
258
(True, [3, 8, 4, 1])
259
260
A 3-design::
261
262
sage: D = IncidenceStructure(range(32),designs.steiner_quadruple_system(32))
263
sage: D.is_block_design()
264
(True, [3, 32, 4, 1])
265
266
With an integer instead of a Finite Field::
267
268
sage: BD = designs.AffineGeometryDesign(3, 2, 4)
269
sage: BD.parameters(t=2)
270
(2, 64, 16, 5)
271
"""
272
try:
273
q = int(F)
274
except TypeError:
275
q = F.order()
276
277
from sage.interfaces.gap import gap, GapElement
278
from sage.sets.set import Set
279
gap.eval("V:=GaloisField(%s)^%s"%(q,n))
280
gap.eval("points:=AsSet(V)")
281
gap.eval("Subs:=AsSet(Subspaces(V,%s));"%d)
282
gap.eval("CP:=Cartesian(points,Subs)")
283
flats = gap.eval("flats:=List(CP,x->Sum(x))") # affine spaces
284
gblcks = eval(gap.eval("Set(List(flats,f->Filtered([1..Length(points)],i->points[i] in f)));"))
285
v = q**n
286
gB = []
287
for b in gblcks:
288
gB.append([x-1 for x in b])
289
return BlockDesign(v, gB, name="AffineGeometryDesign")
290
291
def WittDesign(n):
292
"""
293
INPUT:
294
295
- ``n`` is in `9,10,11,12,21,22,23,24`.
296
297
Wraps GAP Design's WittDesign. If ``n=24`` then this function returns the
298
large Witt design `W_{24}`, the unique (up to isomorphism) `5-(24,8,1)`
299
design. If ``n=12`` then this function returns the small Witt design
300
`W_{12}`, the unique (up to isomorphism) `5-(12,6,1)` design. The other
301
values of `n` return a block design derived from these.
302
303
.. NOTE:
304
305
Requires GAP's Design package (included in the gap_packages Sage spkg).
306
307
EXAMPLES::
308
309
sage: BD = designs.WittDesign(9) # optional - gap_packages (design package)
310
sage: BD.is_block_design() # optional - gap_packages (design package)
311
(2, 9, 3, 1)
312
sage: BD # optional - gap_packages (design package)
313
Incidence structure with 9 points and 12 blocks
314
sage: print BD # optional - gap_packages (design package)
315
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]]>
316
sage: BD = designs.WittDesign(12) # optional - gap_packages (design package)
317
sage: BD.is_block_design() # optional - gap_packages (design package)
318
(5, 12, 6, 1)
319
"""
320
from sage.interfaces.gap import gap, GapElement
321
gap.load_package("design")
322
gap.eval("B:=WittDesign(%s)"%n)
323
v = eval(gap.eval("B.v"))
324
gblcks = eval(gap.eval("B.blocks"))
325
gB = []
326
for b in gblcks:
327
gB.append([x-1 for x in b])
328
return BlockDesign(v, gB, name="WittDesign", test=True)
329
330
def HadamardDesign(n):
331
"""
332
As described in Section 1, p. 10, in [CvL]. The input n must have the
333
property that there is a Hadamard matrix of order `n+1` (and that a
334
construction of that Hadamard matrix has been implemented...).
335
336
EXAMPLES::
337
338
sage: designs.HadamardDesign(7)
339
Incidence structure with 7 points and 7 blocks
340
sage: print designs.HadamardDesign(7)
341
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]]>
342
343
REFERENCES:
344
345
- [CvL] P. Cameron, J. H. van Lint, Designs, graphs, codes and
346
their links, London Math. Soc., 1991.
347
"""
348
from sage.combinat.matrices.hadamard_matrix import hadamard_matrix
349
from sage.matrix.constructor import matrix
350
H = hadamard_matrix(n+1)
351
H1 = H.matrix_from_columns(range(1,n+1))
352
H2 = H1.matrix_from_rows(range(1,n+1))
353
J = matrix(ZZ,n,n,[1]*n*n)
354
MS = J.parent()
355
A = MS((H2+J)/2) # convert -1's to 0's; coerce entries to ZZ
356
# A is the incidence matrix of the block design
357
return IncidenceStructureFromMatrix(A,name="HadamardDesign")
358
359
def steiner_triple_system(n):
360
r"""
361
Returns a Steiner Triple System
362
363
A Steiner Triple System (STS) of a set `\{0,...,n-1\}`
364
is a family `S` of 3-sets such that for any `i \not = j`
365
there exists exactly one set of `S` in which they are
366
both contained.
367
368
It can alternatively be thought of as a factorization of
369
the complete graph `K_n` with triangles.
370
371
A Steiner Triple System of a `n`-set exists if and only if
372
`n \equiv 1 \pmod 6` or `n \equiv 3 \pmod 6`, in which case
373
one can be found through Bose's and Skolem's constructions,
374
respectively [AndHon97]_.
375
376
INPUT:
377
378
- ``n`` returns a Steiner Triple System of `\{0,...,n-1\}`
379
380
EXAMPLE:
381
382
A Steiner Triple System on `9` elements ::
383
384
sage: sts = designs.steiner_triple_system(9)
385
sage: sts
386
Incidence structure with 9 points and 12 blocks
387
sage: list(sts)
388
[[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]]
389
390
As any pair of vertices is covered once, its parameters are ::
391
392
sage: sts.parameters(t=2)
393
(2, 9, 3, 1)
394
395
An exception is raised for invalid values of ``n`` ::
396
397
sage: designs.steiner_triple_system(10)
398
Traceback (most recent call last):
399
...
400
ValueError: Steiner triple systems only exist for n = 1 mod 6 or n = 3 mod 6
401
402
REFERENCE:
403
404
.. [AndHon97] A short course in Combinatorial Designs,
405
Ian Anderson, Iiro Honkala,
406
Internet Editions, Spring 1997,
407
http://www.utu.fi/~honkala/designs.ps
408
"""
409
410
name = "Steiner Triple System on "+str(n)+" elements"
411
412
if n%6 == 3:
413
t = (n-3)/6
414
Z = range(2*t+1)
415
416
T = lambda (x,y) : x + (2*t+1)*y
417
418
sts = [[(i,0),(i,1),(i,2)] for i in Z] + \
419
[[(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]
420
421
elif n%6 == 1:
422
423
t = (n-1)/6
424
N = range(2*t)
425
T = lambda (x,y) : x+y*t*2 if (x,y) != (-1,-1) else n-1
426
427
L1 = lambda i,j : (i+j) % (int((n-1)/3))
428
L = lambda i,j : L1(i,j)/2 if L1(i,j)%2 == 0 else t+(L1(i,j)-1)/2
429
430
sts = [[(i,0),(i,1),(i,2)] for i in range(t)] + \
431
[[(-1,-1),(i,k),(i-t,(k+1) % 3)] for i in range(t,2*t) for k in [0,1,2]] + \
432
[[(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]
433
434
else:
435
raise ValueError("Steiner triple systems only exist for n = 1 mod 6 or n = 3 mod 6")
436
437
from sage.sets.set import Set
438
sts = Set(map(lambda x: Set(map(T,x)),sts))
439
440
return BlockDesign(n, sts, name=name)
441
442
def BlockDesign(max_pt, blks, name=None, test=True):
443
"""
444
Returns an instance of the :class:`IncidenceStructure` class.
445
446
Requires each B in blks to be contained in range(max_pt). Does not test if
447
the result is a block design.
448
449
EXAMPLES::
450
451
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")
452
Incidence structure with 7 points and 7 blocks
453
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")
454
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]]>
455
"""
456
nm = name
457
tst = test
458
if nm == None and test:
459
nm = "BlockDesign"
460
BD = BlockDesign_generic( range(max_pt), blks, name=nm, test=tst )
461
if not(test):
462
return BD
463
else:
464
pars = BD.parameters(t=2)
465
if BD.block_design_checker(pars[0],pars[1],pars[2],pars[3]):
466
return BD
467
else:
468
raise TypeError("parameters are not those of a block design.")
469
470
# Possibly in the future there will be methods which apply to block designs and
471
# not incidence structures. None have been implemented yet though. The class
472
# name BlockDesign_generic is reserved in the name space in case more
473
# specialized methods are implemented later. In that case, BlockDesign_generic
474
# should inherit from IncidenceStructure.
475
BlockDesign_generic = IncidenceStructure
476
477