Path: blob/master/sage/combinat/designs/covering_design.py
4097 views
r"""1Covering designs: coverings of $t$-element subsets of a $v$-set by $k$-sets23A $(v,k,t)$ covering design $C$ is an incidence structure consisting of a4set of points $P$ of order $v$, and a set of blocks $B$, where each5block contains $k$ points of $P$. Every $t$-element subset of $P$6must be contained in at least one block.78If every $t$-set is contained in exactly one block of $C$, then we9have a block design. Following the block design implementation, the10standard representation of a covering design uses $P = [0,1,..., v-1]$.1112There is an online database of the best known covering designs, the La13Jolla Covering Repository (LJCR), at [1]. This is maintained with an SQL14database, also in Sage, but since it changes frequently, and is over1560MB, that code is not included here. A user may get individual16coverings from the LJCR using best_known_covering_design_www.1718In addition to the parameters and incidence structure for a covering19design from this database, we include extra information:2021\begin{itemize}22\item Best known lower bound on the size of a (v,k,t)-covering design23\item Name of the person(s) who produced the design24\item Method of construction used25\item Date when the design was added to the database26\end{itemize}2728REFERENCES29[1] La Jolla Covering Repository, http://www.ccrwest.org/cover.html30[2] Coverings, by Daniel Gordon and Douglas Stinson,31http://www.ccrwest.org/gordon/hcd.pdf, in Handbook of Combinatorial Designs323334AUTHORS:35-- Daniel M. Gordon (2008-12-22): initial version3637"""3839#*****************************************************************************40# Copyright (C) 2008 Daniel M. Gordon <[email protected]>41#42# Distributed under the terms of the GNU General Public License (GPL)43# http://www.gnu.org/licenses/44#*****************************************************************************4546import urllib47from sage.misc.sage_eval import sage_eval48from sage.structure.sage_object import SageObject49from sage.rings.rational import Rational50from sage.rings.arith import binomial51from sage.combinat.combination import Combinations52from sage.combinat.designs.incidence_structures import IncidenceStructure5354###################### covering design functions ##############################555657def schonheim(v,k,t):58r"""59Schonheim lower bound for size of covering design606162INPUT:63v -- integer, size of point set64k -- integer, cardinality of each block65t -- integer, cardinality of sets being covered6667OUTPUT:68The Schonheim lower bound for such a covering design's size:69$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$7071EXAMPLES:72sage: from sage.combinat.designs.covering_design import schonheim73sage: schonheim(10,3,2)741775sage: schonheim(32,16,8)7693077"""78bound = 179for i in range(t-1,-1,-1):80bound = Rational((bound*(v-i),k-i)).ceil()8182return bound838485def trivial_covering_design(v,k,t):86r"""87Construct a trivial covering design.8889INPUT:90v -- integer, size of point set91k -- integer, cardinality of each block92t -- integer, cardinality of sets being covered9394OUTPUT:95(v,k,t) covering design9697EXAMPLE:98sage: C = trivial_covering_design(8,3,1)99sage: print C100C(8,3,1) = 3101Method: Trivial1020 1 21030 6 71043 4 5105sage: C = trivial_covering_design(5,3,2)106sage: print C1074 <= C(5,3,2) <= 10108Method: Trivial1090 1 21100 1 31110 1 41120 2 31130 2 41140 3 41151 2 31161 2 41171 3 41182 3 4119120NOTES:121Cases are:122\begin{enumerate}123\item $t=0$: This could be empty, but it's a useful convention to124have one block (which is empty if $k=0$).125\item $t=1$: This contains $\lceil v/k \rceil$ blocks:126$[0,...,k-1]$,[k,...,2k-1],...$. The last block127wraps around if $k$ does not divide $v$.128\item anything else: Just use every $k$-subset of $[0,1,...,v-1]$.129\end{enumerate}130131"""132if t==0: #single block [0,...,k-1]133blk=[]134for i in range(k):135blk.append(i)136return CoveringDesign(v,k,t,1,range(v),[blk],1,"Trivial")137138if t==1: #blocks [0,...,k-1],[k,...,2k-1],...139size = Rational((v,k)).ceil()140blocks=[]141for i in range(size-1):142blk=[]143for j in range(i*k,(i+1)*k):144blk.append(j)145blocks.append(blk)146147blk=[] # last block: if k does not divide v, wrap around148for j in range((size-1)*k,v):149blk.append(j)150for j in range(k-len(blk)):151blk.append(j)152blk.sort()153blocks.append(blk)154return CoveringDesign(v,k,t,size,range(v),blocks,size,"Trivial")155156# default case, all k-subsets157return CoveringDesign(v,k,t,binomial(v,k),range(v),Combinations(range(v),k),schonheim(v,k,t),"Trivial")158159160class CoveringDesign(SageObject):161"""162Covering design.163164INPUT:165data -- parameters (v,k,t)166size167incidence structure (points and blocks) -- default points are $[0,...v-1]$168lower bound for such a design169database information: creator, method, timestamp170"""171172def __init__(self, v=0, k=0, t=0, size=0, points=[], blocks=[], low_bd=0, method='', creator ='',timestamp=''):173"""174EXAMPLES:175sage: 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')176sage: print C177C(5,4,3) = 4178Method: Lexicographic Covering1790 1 2 31800 1 2 41810 1 3 41820 2 3 4183"""184self.__v = v185self.__k = k186self.__t = t187self.__size = size188if low_bd > 0:189self.__low_bd = low_bd190else:191self.__low_bd = schonheim(v,k,t)192self.__method = method193self.__creator = creator194self.__timestamp = timestamp195self.__incidence_structure = IncidenceStructure(points, blocks)196197198def __repr__(self):199"""200A print method, giving the parameters and any other information about the covering (but not the blocks).201202EXAMPLES:203sage: 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')204sage: C205(7,3,2)-covering design of size 7206Lower bound: 7207Method: Projective Plane208"""209repr = '(%d,%d,%d)-covering design of size %d\n'%(self.__v,self.__k,self.__t,self.__size)210repr += 'Lower bound: %d\n'%(self.__low_bd)211if self.__creator != '':212repr += 'Created by: %s\n'%(self.__creator)213if self.__method != '':214repr += 'Method: %s\n'%(self.__method)215if self.__timestamp != '':216repr += 'Submitted on: %s\n'%(self.__timestamp)217218return repr219220221def __str__(self):222"""223A print method, displaying a covering design's parameters and blocks.224225OUTPUT:226covering design parameters and blocks, in a readable form227228EXAMPLES:229sage: 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')230sage: print C231C(7,3,2) = 7232Method: Projective Plane2330 1 22340 3 42350 5 62361 3 52371 4 62382 3 62392 4 5240"""241if self.__size == self.__low_bd: # check if the covering is known to be optimal242repr = 'C(%d,%d,%d) = %d\n'%(self.__v,self.__k,self.__t,self.__size)243else:244repr = '%d <= C(%d,%d,%d) <= %d\n'%(self.__low_bd,self.__v,self.__k,self.__t,self.__size);245if self.__creator != '':246repr += 'Created by: %s\n'%(self.__creator)247if self.__method != '':248repr += 'Method: %s\n'%(self.__method)249if self.__timestamp != '':250repr += 'Submitted on: %s\n'%(self.__timestamp)251for i in range(self.__size):252for j in range(self.__k):253repr = repr + str(self.__incidence_structure.blocks()[i][j]) + ' '254repr += '\n'255256return repr257258259def is_covering(self):260"""261Checks that all t-sets are in fact covered.262263INPUT:264putative covering design265266OUTPUT:267True if all t-sets are in at least one block268269270NOTES:271This is very slow and wasteful of memory. A faster Cython272version will be added soon.273274EXAMPLES:275sage: 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')276sage: C.is_covering()277True278sage: 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 altered279sage: C.is_covering()280False281"""282v = self.__v283k = self.__k284t = self.__t285Svt = Combinations(range(v),t)286Skt = Combinations(range(k),t)287tset = {} # tables of t-sets: False = uncovered, True = covered288for i in Svt:289tset[tuple(i)] = False290291# mark all t-sets covered by each block292for a in self.__incidence_structure.blocks():293for z in Skt:294y = [a[x] for x in z]295tset[tuple(y)] = True296297for i in Svt:298if tset[tuple(i)] == False: # uncovered299return False300301return True # everything was covered302303304def v(self):305"""306Return v, the number of points in the covering design307308EXAMPLES:309sage: from sage.combinat.designs.covering_design import CoveringDesign310sage: 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')311sage: C.v()3127313"""314return self.__v315316317def k(self):318"""319Return k, the size of blocks of the covering design320321EXAMPLES:322sage: from sage.combinat.designs.covering_design import CoveringDesign323sage: 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')324sage: C.k()3253326"""327return self.__k328329330def t(self):331"""332Return t, the size of sets which must be covered by the blocks of the covering design333334EXAMPLES:335sage: from sage.combinat.designs.covering_design import CoveringDesign336sage: 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')337sage: C.t()3382339"""340return self.__t341342def size(self):343"""344Return the number of blocks in the covering design345346EXAMPLES:347sage: from sage.combinat.designs.covering_design import CoveringDesign348sage: 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')349sage: C.size()3507351"""352return self.__size353354355def low_bd(self):356"""357Return a lower bound for the number of blocks a covering design with these parameters could have.358Typically this is the Schonheim bound, but for some parameters better bounds have been shown.359360EXAMPLES:361sage: from sage.combinat.designs.covering_design import CoveringDesign362sage: 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')363sage: C.low_bd()3647365"""366return self.__low_bd367368369def method(self):370"""371Return the method used to create the covering design372This field is optional, and is used in a database to give information about how coverings were constructed373374EXAMPLES:375sage: from sage.combinat.designs.covering_design import CoveringDesign376sage: 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')377sage: C.method()378'Projective Plane'379"""380return self.__method381382383def creator(self):384"""385Return the creator of the covering design386This field is optional, and is used in a database to give attribution for the covering design387It can refer to the person who submitted it, or who originally gave a construction388389EXAMPLES:390sage: from sage.combinat.designs.covering_design import CoveringDesign391sage: 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')392sage: C.creator()393'Gino Fano'394"""395return self.__creator396397398def timestamp(self):399"""400Return the time that the covering was submitted to the database401402EXAMPLES:403sage: from sage.combinat.designs.covering_design import CoveringDesign404sage: 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')405sage: C.timestamp() #Fano had an article in 1892, I don't know the date it appeared406'1892-01-01 00:00:00'407"""408return self.__timestamp409410411def incidence_structure(self):412"""413Return the incidence structure of a covering design, without all the extra parameters.414415EXAMPLES:416sage: from sage.combinat.designs.covering_design import CoveringDesign417sage: 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')418sage: D = C.incidence_structure()419sage: D.points()420[0, 1, 2, 3, 4, 5, 6]421sage: D.blocks()422[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]]423424"""425return self.__incidence_structure426427428429430def best_known_covering_design_www(v, k, t, verbose=False):431r"""432Gives the best known (v,k,t) covering design, using database at433\url{http://www.ccrwest.org/}.434435INPUT:436v -- integer, the size of the point set for the design437k -- integer, the number of points per block438t -- integer, the size of sets covered by the blocks439verbose -- bool (default=False), print verbose message440441OUTPUT:442CoveringDesign -- (v,k,t) covering design with smallest number of blocks443444EXAMPLES:445sage: C = best_known_covering_design_www(7, 3, 2) # optional -- requires internet446sage: print C # optional -- requires internet447C(7,3,2) = 7448Method: lex covering449Submitted on: 1996-12-01 00:00:004500 1 24510 3 44520 5 64531 3 54541 4 64552 3 64562 4 5457458This function raises a ValueError if the (v,k,t) parameters are459not found in the database.460"""461v = int(v)462k = int(k)463t = int(t)464465param = ("?v=%s&k=%s&t=%s"%(v,k,t))466467url = "http://www.ccrwest.org/cover/get_cover.php"+param468if verbose:469print "Looking up the bounds at %s"%url470f = urllib.urlopen(url)471s = f.read()472f.close()473474if 'covering not in database' in s: #not found475str = "no (%d,%d,%d) covering design in database\n"%(v,k,t)476raise ValueError, str477478return sage_eval(s)479480481482