Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/combinat/designs/covering_design.py
4097 views
1
r"""
2
Covering designs: coverings of $t$-element subsets of a $v$-set by $k$-sets
3
4
A $(v,k,t)$ covering design $C$ is an incidence structure consisting of a
5
set of points $P$ of order $v$, and a set of blocks $B$, where each
6
block contains $k$ points of $P$. Every $t$-element subset of $P$
7
must be contained in at least one block.
8
9
If every $t$-set is contained in exactly one block of $C$, then we
10
have a block design. Following the block design implementation, the
11
standard representation of a covering design uses $P = [0,1,..., v-1]$.
12
13
There is an online database of the best known covering designs, the La
14
Jolla Covering Repository (LJCR), at [1]. This is maintained with an SQL
15
database, also in Sage, but since it changes frequently, and is over
16
60MB, that code is not included here. A user may get individual
17
coverings from the LJCR using best_known_covering_design_www.
18
19
In addition to the parameters and incidence structure for a covering
20
design from this database, we include extra information:
21
22
\begin{itemize}
23
\item Best known lower bound on the size of a (v,k,t)-covering design
24
\item Name of the person(s) who produced the design
25
\item Method of construction used
26
\item Date when the design was added to the database
27
\end{itemize}
28
29
REFERENCES
30
[1] La Jolla Covering Repository, http://www.ccrwest.org/cover.html
31
[2] Coverings, by Daniel Gordon and Douglas Stinson,
32
http://www.ccrwest.org/gordon/hcd.pdf, in Handbook of Combinatorial Designs
33
34
35
AUTHORS:
36
-- Daniel M. Gordon (2008-12-22): initial version
37
38
"""
39
40
#*****************************************************************************
41
# Copyright (C) 2008 Daniel M. Gordon <[email protected]>
42
#
43
# Distributed under the terms of the GNU General Public License (GPL)
44
# http://www.gnu.org/licenses/
45
#*****************************************************************************
46
47
import urllib
48
from sage.misc.sage_eval import sage_eval
49
from sage.structure.sage_object import SageObject
50
from sage.rings.rational import Rational
51
from sage.rings.arith import binomial
52
from sage.combinat.combination import Combinations
53
from sage.combinat.designs.incidence_structures import IncidenceStructure
54
55
###################### covering design functions ##############################
56
57
58
def schonheim(v,k,t):
59
r"""
60
Schonheim lower bound for size of covering design
61
62
63
INPUT:
64
v -- integer, size of point set
65
k -- integer, cardinality of each block
66
t -- integer, cardinality of sets being covered
67
68
OUTPUT:
69
The Schonheim lower bound for such a covering design's size:
70
$C(v,k,t) \leq \lceil(\frac{v}{k} \lceil \frac{v-1}{k-1} \cdots \lceil \frac{v-t+1}{k-t+1} \rceil \cdots \rceil \rceil$
71
72
EXAMPLES:
73
sage: from sage.combinat.designs.covering_design import schonheim
74
sage: schonheim(10,3,2)
75
17
76
sage: schonheim(32,16,8)
77
930
78
"""
79
bound = 1
80
for i in range(t-1,-1,-1):
81
bound = Rational((bound*(v-i),k-i)).ceil()
82
83
return bound
84
85
86
def trivial_covering_design(v,k,t):
87
r"""
88
Construct a trivial covering design.
89
90
INPUT:
91
v -- integer, size of point set
92
k -- integer, cardinality of each block
93
t -- integer, cardinality of sets being covered
94
95
OUTPUT:
96
(v,k,t) covering design
97
98
EXAMPLE:
99
sage: C = trivial_covering_design(8,3,1)
100
sage: print C
101
C(8,3,1) = 3
102
Method: Trivial
103
0 1 2
104
0 6 7
105
3 4 5
106
sage: C = trivial_covering_design(5,3,2)
107
sage: print C
108
4 <= C(5,3,2) <= 10
109
Method: Trivial
110
0 1 2
111
0 1 3
112
0 1 4
113
0 2 3
114
0 2 4
115
0 3 4
116
1 2 3
117
1 2 4
118
1 3 4
119
2 3 4
120
121
NOTES:
122
Cases are:
123
\begin{enumerate}
124
\item $t=0$: This could be empty, but it's a useful convention to
125
have one block (which is empty if $k=0$).
126
\item $t=1$: This contains $\lceil v/k \rceil$ blocks:
127
$[0,...,k-1]$,[k,...,2k-1],...$. The last block
128
wraps around if $k$ does not divide $v$.
129
\item anything else: Just use every $k$-subset of $[0,1,...,v-1]$.
130
\end{enumerate}
131
132
"""
133
if t==0: #single block [0,...,k-1]
134
blk=[]
135
for i in range(k):
136
blk.append(i)
137
return CoveringDesign(v,k,t,1,range(v),[blk],1,"Trivial")
138
139
if t==1: #blocks [0,...,k-1],[k,...,2k-1],...
140
size = Rational((v,k)).ceil()
141
blocks=[]
142
for i in range(size-1):
143
blk=[]
144
for j in range(i*k,(i+1)*k):
145
blk.append(j)
146
blocks.append(blk)
147
148
blk=[] # last block: if k does not divide v, wrap around
149
for j in range((size-1)*k,v):
150
blk.append(j)
151
for j in range(k-len(blk)):
152
blk.append(j)
153
blk.sort()
154
blocks.append(blk)
155
return CoveringDesign(v,k,t,size,range(v),blocks,size,"Trivial")
156
157
# default case, all k-subsets
158
return CoveringDesign(v,k,t,binomial(v,k),range(v),Combinations(range(v),k),schonheim(v,k,t),"Trivial")
159
160
161
class CoveringDesign(SageObject):
162
"""
163
Covering design.
164
165
INPUT:
166
data -- parameters (v,k,t)
167
size
168
incidence structure (points and blocks) -- default points are $[0,...v-1]$
169
lower bound for such a design
170
database information: creator, method, timestamp
171
"""
172
173
def __init__(self, v=0, k=0, t=0, size=0, points=[], blocks=[], low_bd=0, method='', creator ='',timestamp=''):
174
"""
175
EXAMPLES:
176
sage: C=CoveringDesign(5,4,3,4,range(5),[[0,1,2,3],[0,1,2,4],[0,1,3,4],[0,2,3,4]],4, 'Lexicographic Covering')
177
sage: print C
178
C(5,4,3) = 4
179
Method: Lexicographic Covering
180
0 1 2 3
181
0 1 2 4
182
0 1 3 4
183
0 2 3 4
184
"""
185
self.__v = v
186
self.__k = k
187
self.__t = t
188
self.__size = size
189
if low_bd > 0:
190
self.__low_bd = low_bd
191
else:
192
self.__low_bd = schonheim(v,k,t)
193
self.__method = method
194
self.__creator = creator
195
self.__timestamp = timestamp
196
self.__incidence_structure = IncidenceStructure(points, blocks)
197
198
199
def __repr__(self):
200
"""
201
A print method, giving the parameters and any other information about the covering (but not the blocks).
202
203
EXAMPLES:
204
sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane')
205
sage: C
206
(7,3,2)-covering design of size 7
207
Lower bound: 7
208
Method: Projective Plane
209
"""
210
repr = '(%d,%d,%d)-covering design of size %d\n'%(self.__v,self.__k,self.__t,self.__size)
211
repr += 'Lower bound: %d\n'%(self.__low_bd)
212
if self.__creator != '':
213
repr += 'Created by: %s\n'%(self.__creator)
214
if self.__method != '':
215
repr += 'Method: %s\n'%(self.__method)
216
if self.__timestamp != '':
217
repr += 'Submitted on: %s\n'%(self.__timestamp)
218
219
return repr
220
221
222
def __str__(self):
223
"""
224
A print method, displaying a covering design's parameters and blocks.
225
226
OUTPUT:
227
covering design parameters and blocks, in a readable form
228
229
EXAMPLES:
230
sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane')
231
sage: print C
232
C(7,3,2) = 7
233
Method: Projective Plane
234
0 1 2
235
0 3 4
236
0 5 6
237
1 3 5
238
1 4 6
239
2 3 6
240
2 4 5
241
"""
242
if self.__size == self.__low_bd: # check if the covering is known to be optimal
243
repr = 'C(%d,%d,%d) = %d\n'%(self.__v,self.__k,self.__t,self.__size)
244
else:
245
repr = '%d <= C(%d,%d,%d) <= %d\n'%(self.__low_bd,self.__v,self.__k,self.__t,self.__size);
246
if self.__creator != '':
247
repr += 'Created by: %s\n'%(self.__creator)
248
if self.__method != '':
249
repr += 'Method: %s\n'%(self.__method)
250
if self.__timestamp != '':
251
repr += 'Submitted on: %s\n'%(self.__timestamp)
252
for i in range(self.__size):
253
for j in range(self.__k):
254
repr = repr + str(self.__incidence_structure.blocks()[i][j]) + ' '
255
repr += '\n'
256
257
return repr
258
259
260
def is_covering(self):
261
"""
262
Checks that all t-sets are in fact covered.
263
264
INPUT:
265
putative covering design
266
267
OUTPUT:
268
True if all t-sets are in at least one block
269
270
271
NOTES:
272
This is very slow and wasteful of memory. A faster Cython
273
version will be added soon.
274
275
EXAMPLES:
276
sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane')
277
sage: C.is_covering()
278
True
279
sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 6]],0, 'not a covering') # last block altered
280
sage: C.is_covering()
281
False
282
"""
283
v = self.__v
284
k = self.__k
285
t = self.__t
286
Svt = Combinations(range(v),t)
287
Skt = Combinations(range(k),t)
288
tset = {} # tables of t-sets: False = uncovered, True = covered
289
for i in Svt:
290
tset[tuple(i)] = False
291
292
# mark all t-sets covered by each block
293
for a in self.__incidence_structure.blocks():
294
for z in Skt:
295
y = [a[x] for x in z]
296
tset[tuple(y)] = True
297
298
for i in Svt:
299
if tset[tuple(i)] == False: # uncovered
300
return False
301
302
return True # everything was covered
303
304
305
def v(self):
306
"""
307
Return v, the number of points in the covering design
308
309
EXAMPLES:
310
sage: from sage.combinat.designs.covering_design import CoveringDesign
311
sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane')
312
sage: C.v()
313
7
314
"""
315
return self.__v
316
317
318
def k(self):
319
"""
320
Return k, the size of blocks of the covering design
321
322
EXAMPLES:
323
sage: from sage.combinat.designs.covering_design import CoveringDesign
324
sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane')
325
sage: C.k()
326
3
327
"""
328
return self.__k
329
330
331
def t(self):
332
"""
333
Return t, the size of sets which must be covered by the blocks of the covering design
334
335
EXAMPLES:
336
sage: from sage.combinat.designs.covering_design import CoveringDesign
337
sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane')
338
sage: C.t()
339
2
340
"""
341
return self.__t
342
343
def size(self):
344
"""
345
Return the number of blocks in the covering design
346
347
EXAMPLES:
348
sage: from sage.combinat.designs.covering_design import CoveringDesign
349
sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane')
350
sage: C.size()
351
7
352
"""
353
return self.__size
354
355
356
def low_bd(self):
357
"""
358
Return a lower bound for the number of blocks a covering design with these parameters could have.
359
Typically this is the Schonheim bound, but for some parameters better bounds have been shown.
360
361
EXAMPLES:
362
sage: from sage.combinat.designs.covering_design import CoveringDesign
363
sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane')
364
sage: C.low_bd()
365
7
366
"""
367
return self.__low_bd
368
369
370
def method(self):
371
"""
372
Return the method used to create the covering design
373
This field is optional, and is used in a database to give information about how coverings were constructed
374
375
EXAMPLES:
376
sage: from sage.combinat.designs.covering_design import CoveringDesign
377
sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane')
378
sage: C.method()
379
'Projective Plane'
380
"""
381
return self.__method
382
383
384
def creator(self):
385
"""
386
Return the creator of the covering design
387
This field is optional, and is used in a database to give attribution for the covering design
388
It can refer to the person who submitted it, or who originally gave a construction
389
390
EXAMPLES:
391
sage: from sage.combinat.designs.covering_design import CoveringDesign
392
sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane','Gino Fano')
393
sage: C.creator()
394
'Gino Fano'
395
"""
396
return self.__creator
397
398
399
def timestamp(self):
400
"""
401
Return the time that the covering was submitted to the database
402
403
EXAMPLES:
404
sage: from sage.combinat.designs.covering_design import CoveringDesign
405
sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane','Gino Fano','1892-01-01 00:00:00')
406
sage: C.timestamp() #Fano had an article in 1892, I don't know the date it appeared
407
'1892-01-01 00:00:00'
408
"""
409
return self.__timestamp
410
411
412
def incidence_structure(self):
413
"""
414
Return the incidence structure of a covering design, without all the extra parameters.
415
416
EXAMPLES:
417
sage: from sage.combinat.designs.covering_design import CoveringDesign
418
sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane')
419
sage: D = C.incidence_structure()
420
sage: D.points()
421
[0, 1, 2, 3, 4, 5, 6]
422
sage: D.blocks()
423
[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]]
424
425
"""
426
return self.__incidence_structure
427
428
429
430
431
def best_known_covering_design_www(v, k, t, verbose=False):
432
r"""
433
Gives the best known (v,k,t) covering design, using database at
434
\url{http://www.ccrwest.org/}.
435
436
INPUT:
437
v -- integer, the size of the point set for the design
438
k -- integer, the number of points per block
439
t -- integer, the size of sets covered by the blocks
440
verbose -- bool (default=False), print verbose message
441
442
OUTPUT:
443
CoveringDesign -- (v,k,t) covering design with smallest number of blocks
444
445
EXAMPLES:
446
sage: C = best_known_covering_design_www(7, 3, 2) # optional -- requires internet
447
sage: print C # optional -- requires internet
448
C(7,3,2) = 7
449
Method: lex covering
450
Submitted on: 1996-12-01 00:00:00
451
0 1 2
452
0 3 4
453
0 5 6
454
1 3 5
455
1 4 6
456
2 3 6
457
2 4 5
458
459
This function raises a ValueError if the (v,k,t) parameters are
460
not found in the database.
461
"""
462
v = int(v)
463
k = int(k)
464
t = int(t)
465
466
param = ("?v=%s&k=%s&t=%s"%(v,k,t))
467
468
url = "http://www.ccrwest.org/cover/get_cover.php"+param
469
if verbose:
470
print "Looking up the bounds at %s"%url
471
f = urllib.urlopen(url)
472
s = f.read()
473
f.close()
474
475
if 'covering not in database' in s: #not found
476
str = "no (%d,%d,%d) covering design in database\n"%(v,k,t)
477
raise ValueError, str
478
479
return sage_eval(s)
480
481
482