Path: blob/master/src/sage/combinat/designs/covering_design.py
8818 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]`.1112In addition to the parameters and incidence structure for a covering13design from this database, we include extra information:1415* Best known lower bound on the size of a `(v,k,t)`-covering design16* Name of the person(s) who produced the design17* Method of construction used18* Date when the design was added to the database1920REFERENCES:2122.. [1] La Jolla Covering Repository,23http://www.ccrwest.org/cover.html2425.. [2] Coverings,26Daniel Gordon and Douglas Stinson,27http://www.ccrwest.org/gordon/hcd.pdf28from the Handbook of Combinatorial Designs2930AUTHORS:3132-- Daniel M. Gordon (2008-12-22): initial version3334Classes and methods35-------------------36"""3738#*****************************************************************************39# Copyright (C) 2008 Daniel M. Gordon <[email protected]>40#41# Distributed under the terms of the GNU General Public License (GPL)42# http://www.gnu.org/licenses/43#*****************************************************************************4445from sage.misc.sage_eval import sage_eval46from sage.structure.sage_object import SageObject47from sage.rings.rational import Rational48from sage.rings.arith import binomial49from sage.combinat.combination import Combinations50from sage.combinat.designs.incidence_structures import IncidenceStructure5152###################### covering design functions ##############################535455def schonheim(v,k,t):56r"""57Schonheim lower bound for size of covering design5859INPUT:6061- ``v`` -- integer, size of point set62- ``k`` -- integer, cardinality of each block63- ``t`` -- integer, cardinality of sets being covered6465OUTPUT:6667The Schonheim lower bound for such a covering design's size:68`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`6970EXAMPLES::7172sage: 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:9091- ``v`` -- integer, size of point set92- ``k`` -- integer, cardinality of each block93- ``t`` -- integer, cardinality of sets being covered9495OUTPUT:9697`(v,k,t)` covering design9899EXAMPLE::100101sage: C = trivial_covering_design(8,3,1)102sage: print C103C(8,3,1) = 3104Method: Trivial1050 1 21060 6 71073 4 5108sage: C = trivial_covering_design(5,3,2)109sage: print C1104 <= C(5,3,2) <= 10111Method: Trivial1120 1 21130 1 31140 1 41150 2 31160 2 41170 3 41181 2 31191 2 41201 3 41212 3 4122123NOTES:124125Cases are:126127* `t=0`: This could be empty, but it's a useful convention to have128one block (which is empty if $k=0$).129130* `t=1` : This contains `\lceil v/k \rceil` blocks:131`[0,...,k-1],[k,...,2k-1],...`. The last block wraps around if132`k` does not divide `v`.133134* anything else: Just use every `k`-subset of `[0,1,...,v-1]`.135136"""137if t==0: #single block [0,...,k-1]138blk=[]139for i in range(k):140blk.append(i)141return CoveringDesign(v,k,t,1,range(v),[blk],1,"Trivial")142143if t==1: #blocks [0,...,k-1],[k,...,2k-1],...144size = Rational((v,k)).ceil()145blocks=[]146for i in range(size-1):147blk=[]148for j in range(i*k,(i+1)*k):149blk.append(j)150blocks.append(blk)151152blk=[] # last block: if k does not divide v, wrap around153for j in range((size-1)*k,v):154blk.append(j)155for j in range(k-len(blk)):156blk.append(j)157blk.sort()158blocks.append(blk)159return CoveringDesign(v,k,t,size,range(v),blocks,size,"Trivial")160161# default case, all k-subsets162return CoveringDesign(v,k,t,binomial(v,k),range(v),Combinations(range(v),k),schonheim(v,k,t),"Trivial")163164165class CoveringDesign(SageObject):166"""167Covering design.168169INPUT:170171- ``v,k,t`` -- integer parameters of the covering design.172173- ``size`` (integer)174175- ``points`` -- list of points (default points are `[0,...v-1]`).176177- ``blocks``178179- ``low_bd`` (integer) -- lower bound for such a design180181- ``method, creator, timestamp`` -- database information.182"""183184def __init__(self, v=0, k=0, t=0, size=0, points=[], blocks=[], low_bd=0, method='', creator ='',timestamp=''):185"""186EXAMPLES::187188sage: 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')189sage: print C190C(5,4,3) = 4191Method: Lexicographic Covering1920 1 2 31930 1 2 41940 1 3 41950 2 3 4196"""197self.__v = v198self.__k = k199self.__t = t200self.__size = size201if low_bd > 0:202self.__low_bd = low_bd203else:204self.__low_bd = schonheim(v,k,t)205self.__method = method206self.__creator = creator207self.__timestamp = timestamp208self.__incidence_structure = IncidenceStructure(points, blocks)209210211def __repr__(self):212"""213A print method, giving the parameters and any other214information about the covering (but not the blocks).215216EXAMPLES::217218sage: 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')219sage: C220(7,3,2)-covering design of size 7221Lower bound: 7222Method: Projective Plane223"""224repr = '(%d,%d,%d)-covering design of size %d\n'%(self.__v,self.__k,self.__t,self.__size)225repr += 'Lower bound: %d\n'%(self.__low_bd)226if self.__creator != '':227repr += 'Created by: %s\n'%(self.__creator)228if self.__method != '':229repr += 'Method: %s\n'%(self.__method)230if self.__timestamp != '':231repr += 'Submitted on: %s\n'%(self.__timestamp)232233return repr234235def __str__(self):236"""237A print method, displaying a covering design's parameters and blocks.238239OUTPUT:240241covering design parameters and blocks, in a readable form242243EXAMPLES::244245sage: 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')246sage: print C247C(7,3,2) = 7248Method: Projective Plane2490 1 22500 3 42510 5 62521 3 52531 4 62542 3 62552 4 5256"""257if self.__size == self.__low_bd: # check if the covering is known to be optimal258repr = 'C(%d,%d,%d) = %d\n'%(self.__v,self.__k,self.__t,self.__size)259else:260repr = '%d <= C(%d,%d,%d) <= %d\n'%(self.__low_bd,self.__v,self.__k,self.__t,self.__size);261if self.__creator != '':262repr += 'Created by: %s\n'%(self.__creator)263if self.__method != '':264repr += 'Method: %s\n'%(self.__method)265if self.__timestamp != '':266repr += 'Submitted on: %s\n'%(self.__timestamp)267for i in range(self.__size):268for j in range(self.__k):269repr = repr + str(self.__incidence_structure.blocks()[i][j]) + ' '270repr += '\n'271272return repr273274def is_covering(self):275"""276Checks that all `t`-sets are in fact covered by the blocks of277``self``278279.. NOTE::280281This is very slow and wasteful of memory.282283EXAMPLES::284285sage: 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')286sage: C.is_covering()287True288sage: 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 altered289sage: C.is_covering()290False291"""292v = self.__v293k = self.__k294t = self.__t295Svt = Combinations(range(v),t)296Skt = Combinations(range(k),t)297tset = {} # tables of t-sets: False = uncovered, True = covered298for i in Svt:299tset[tuple(i)] = False300301# mark all t-sets covered by each block302for a in self.__incidence_structure.blocks():303for z in Skt:304y = [a[x] for x in z]305tset[tuple(y)] = True306307for i in Svt:308if tset[tuple(i)] == False: # uncovered309return False310311return True # everything was covered312313314def v(self):315"""316Return `v`, the number of points in the covering design.317318EXAMPLES::319320sage: from sage.combinat.designs.covering_design import CoveringDesign321sage: 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')322sage: C.v()3237324"""325return self.__v326327328def k(self):329"""330Return `k`, the size of blocks of the covering design331332EXAMPLES::333334sage: from sage.combinat.designs.covering_design import CoveringDesign335sage: 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')336sage: C.k()3373338"""339return self.__k340341342def t(self):343"""344Return `t`, the size of sets which must be covered by the345blocks of the covering design346347EXAMPLES::348349sage: from sage.combinat.designs.covering_design import CoveringDesign350sage: 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')351sage: C.t()3522353"""354return self.__t355356def size(self):357"""358Return the number of blocks in the covering design359360EXAMPLES::361362sage: from sage.combinat.designs.covering_design import CoveringDesign363sage: 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')364sage: C.size()3657366"""367return self.__size368369370def low_bd(self):371"""372Return a lower bound for the number of blocks a covering373design with these parameters could have.374375Typically this is the Schonheim bound, but for some parameters376better bounds have been shown.377378EXAMPLES::379380sage: from sage.combinat.designs.covering_design import CoveringDesign381sage: 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')382sage: C.low_bd()3837384"""385return self.__low_bd386387def method(self):388"""389Return the method used to create the covering design390This field is optional, and is used in a database to give information about how coverings were constructed391392EXAMPLES::393394sage: from sage.combinat.designs.covering_design import CoveringDesign395sage: 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')396sage: C.method()397'Projective Plane'398"""399return self.__method400401def creator(self):402"""403Return the creator of the covering design404405This field is optional, and is used in a database to give406attribution for the covering design It can refer to the person407who submitted it, or who originally gave a construction408409EXAMPLES::410411sage: from sage.combinat.designs.covering_design import CoveringDesign412sage: 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')413sage: C.creator()414'Gino Fano'415"""416return self.__creator417418def timestamp(self):419"""420Return the time that the covering was submitted to the database421422EXAMPLES::423424sage: from sage.combinat.designs.covering_design import CoveringDesign425sage: 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')426sage: C.timestamp() #Fano had an article in 1892, I don't know the date it appeared427'1892-01-01 00:00:00'428"""429return self.__timestamp430431432def incidence_structure(self):433"""434Return the incidence structure of a covering design, without all the extra parameters.435436EXAMPLES::437438sage: from sage.combinat.designs.covering_design import CoveringDesign439sage: 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')440sage: D = C.incidence_structure()441sage: D.points()442[0, 1, 2, 3, 4, 5, 6]443sage: D.blocks()444[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]]445446"""447return self.__incidence_structure448449def best_known_covering_design_www(v, k, t, verbose=False):450r"""451Gives the best known `(v,k,t)` covering design, using the database452available at `<http://www.ccrwest.org/>`_453454INPUeT:455456- ``v`` -- integer, the size of the point set for the design457- ``k`` -- integer, the number of points per block458- ``t`` -- integer, the size of sets covered by the blocks459- ``verbose`` -- bool (default=``False``), print verbose message460461OUTPUT:462463A :class:`CoveringDesign` object representing the ``(v,k,t)``-covering464design with smallest number of blocks available in the database.465466EXAMPLES::467468sage: from sage.combinat.designs.covering_design import best_known_covering_design_www469sage: C = best_known_covering_design_www(7, 3, 2) # optional - internet470sage: print C # optional - internet471C(7,3,2) = 7472Method: lex covering473Submitted on: 1996-12-01 00:00:004740 1 24750 3 44760 5 64771 3 54781 4 64792 3 64802 4 5481482This function raises a ValueError if the ``(v,k,t)`` parameters are not483found in the database.484"""485import urllib486from sage.misc.sage_eval import sage_eval487488v = int(v)489k = int(k)490t = int(t)491492param = ("?v=%s&k=%s&t=%s"%(v,k,t))493494url = "http://www.ccrwest.org/cover/get_cover.php"+param495if verbose:496print "Looking up the bounds at %s"%url497f = urllib.urlopen(url)498s = f.read()499f.close()500501if 'covering not in database' in s: #not found502str = "no (%d,%d,%d) covering design in database\n"%(v,k,t)503raise ValueError, str504505return sage_eval(s)506507508