Path: blob/master/src/sage/combinat/designs/block_design.py
8818 views
"""1Block designs.23A module to help with constructions and computations of block4designs and other incidence structures.56A block design is an incidence structure consisting of a set of points `P` and a7set of blocks `B`, where each block is considered as a subset of `P`. More8precisely, a *block design* `B` is a class of `k`-element subsets of `P` such9that the number `r` of blocks that contain any point `x` in `P` is independent10of `x`, and the number `\lambda` of blocks that contain any given `t`-element11subset `T` is independent of the choice of `T` (see [1]_ for more). Such a block12design is also called a `t-(v,k,\lambda)`-design, and `v` (the number of13points), `b` (the number of blocks), `k`, `r`, and `\lambda` are the parameters14of the design. (In Python, ``lambda`` is reserved, so we sometimes use ``lmbda``15or ``L`` instead.)1617In Sage, sets are replaced by (ordered) lists and the standard representation of18a block design uses `P = [0,1,..., v-1]`, so a block design is specified by19`(v,B)`.2021REFERENCES:2223.. [1] Block design from wikipedia,24:wikipedia:`Block_design`2526.. [2] What is a block design?,27http://designtheory.org/library/extrep/extrep-1.1-html/node4.html (in 'The28External Representation of Block Designs' by Peter J. Cameron, Peter29Dobcsanyi, John P. Morgan, Leonard H. Soicher)3031AUTHORS:3233- Peter Dobcsanyi and David Joyner (2007-2008)3435This is a significantly modified form of the module block_design.py (version360.6) written by Peter Dobcsanyi [email protected]. Thanks go to Robert37Miller for lots of good design suggestions.383940Functions and methods41---------------------42"""43#***************************************************************************44# Copyright (C) 2007 #45# #46# Peter Dobcsanyi and David Joyner #47# <[email protected]> <[email protected]> #48# #49# #50# Distributed under the terms of the GNU General Public License (GPL) #51# as published by the Free Software Foundation; either version 2 of #52# the License, or (at your option) any later version. #53# http://www.gnu.org/licenses/ #54#***************************************************************************5556from sage.modules.free_module import VectorSpace57from sage.rings.integer_ring import ZZ58from sage.rings.arith import binomial, integer_floor59from sage.combinat.designs.incidence_structures import IncidenceStructure, IncidenceStructureFromMatrix60from sage.misc.decorators import rename_keyword61from sage.rings.finite_rings.constructor import FiniteField6263### utility functions -------------------------------------------------------6465def tdesign_params(t, v, k, L):66"""67Return the design's parameters: `(t, v, b, r , k, L)`. Note that `t` must be68given.6970EXAMPLES::7172sage: BD = BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]])73sage: from sage.combinat.designs.block_design import tdesign_params74sage: tdesign_params(2,7,3,1)75(2, 7, 7, 3, 3, 1)76"""77x = binomial(v, t)78y = binomial(k, t)79b = divmod(L * x, y)[0]80x = binomial(v-1, t-1)81y = binomial(k-1, t-1)82r = integer_floor(L * x/y)83return (t, v, b, r, k, L)8485def ProjectiveGeometryDesign(n, d, F, algorithm=None):86"""87Returns a projective geometry design.8889A projective geometry design of parameters `n,d,F` has for points the lines90of `F^{n+1}`, and for blocks the `d+1`-dimensional subspaces of `F^{n+1}`,91each of which contains `\\frac {|F|^{d+1}-1} {|F|-1}` lines.9293INPUT:9495- ``n`` is the projective dimension9697- ``d`` is the dimension of the subspaces of `P = PPn(F)` which98make up the blocks.99100- ``F`` is a finite field.101102- ``algorithm`` -- set to ``None`` by default, which results in using Sage's103own implementation. In order to use GAP's implementation instead (i.e. its104``PGPointFlatBlockDesign`` function) set ``algorithm="gap"``. Note that105GAP's "design" package must be available in this case.106107EXAMPLES:108109The points of the following design are the `\\frac {2^{2+1}-1} {2-1}=7`110lines of `\mathbb{Z}_2^{2+1}`. It has `7` blocks, corresponding to each1112-dimensional subspace of `\mathbb{Z}_2^{2+1}`::112113sage: designs.ProjectiveGeometryDesign(2, 1, GF(2))114Incidence structure with 7 points and 7 blocks115sage: BD = designs.ProjectiveGeometryDesign(2, 1, GF(2), algorithm="gap") # optional - gap_packages (design package)116sage: BD.is_block_design() # optional - gap_packages (design package)117(True, [2, 7, 3, 1])118"""119q = F.order()120from sage.interfaces.gap import gap, GapElement121from sage.sets.set import Set122if algorithm is None:123V = VectorSpace(F, n+1)124points = list(V.subspaces(1))125flats = list(V.subspaces(d+1))126blcks = []127for p in points:128b = []129for i in range(len(flats)):130if p.is_subspace(flats[i]):131b.append(i)132blcks.append(b)133v = (q**(n+1)-1)/(q-1)134return BlockDesign(v, blcks, name="ProjectiveGeometryDesign")135if algorithm == "gap": # Requires GAP's Design136gap.load_package("design")137gap.eval("D := PGPointFlatBlockDesign( %s, %s, %d )"%(n,q,d))138v = eval(gap.eval("D.v"))139gblcks = eval(gap.eval("D.blocks"))140gB = []141for b in gblcks:142gB.append([x-1 for x in b])143return BlockDesign(v, gB, name="ProjectiveGeometryDesign")144145def ProjectivePlaneDesign(n, type="Desarguesian"):146r"""147Returns a projective plane of order `n`.148149A finite projective plane is a 2-design with `n^2+n+1` lines (or blocks) and150`n^2+n+1` points. For more information on finite projective planes, see the151:wikipedia:`Projective_plane#Finite_projective_planes`.152153INPUT:154155- ``n`` -- the finite projective plane's order156157- ``type`` -- When set to ``"Desarguesian"``, the method returns158Desarguesian projective planes, i.e. a finite projective plane obtained by159considering the 1- and 2- dimensional spaces of `F_n^3`.160161For the moment, no other value is available for this parameter.162163.. SEEALSO::164165:meth:`ProjectiveGeometryDesign`166167EXAMPLES::168169sage: designs.ProjectivePlaneDesign(2)170Incidence structure with 7 points and 7 blocks171172Non-existent ones::173174sage: designs.ProjectivePlaneDesign(10)175Traceback (most recent call last):176...177ValueError: No projective plane design of order 10 exists.178sage: designs.ProjectivePlaneDesign(14)179Traceback (most recent call last):180...181ValueError: By the Bruck-Ryser-Chowla theorem, no projective plane of order 14 exists.182183An unknown one::184185sage: designs.ProjectivePlaneDesign(12)186Traceback (most recent call last):187...188ValueError: If such a projective plane exists, we do not know how to build it.189190TESTS::191192sage: designs.ProjectivePlaneDesign(10, type="AnyThingElse")193Traceback (most recent call last):194...195ValueError: The value of 'type' must be 'Desarguesian'.196"""197from sage.rings.arith import two_squares198199if type != "Desarguesian":200raise ValueError("The value of 'type' must be 'Desarguesian'.")201202try:203F = FiniteField(n, 'x')204except ValueError:205if n == 10:206raise ValueError("No projective plane design of order 10 exists.")207try:208if (n%4) in [1,2]:209two_squares(n)210except ValueError:211raise ValueError("By the Bruck-Ryser-Chowla theorem, no projective"212" plane of order "+str(n)+" exists.")213214raise ValueError("If such a projective plane exists, "215"we do not know how to build it.")216217return ProjectiveGeometryDesign(2,1,F)218219def AffineGeometryDesign(n, d, F):220r"""221Returns an Affine Geometry Design.222223INPUT:224225- `n` (integer) -- the Euclidean dimension. The number of points is226`v=|F^n|`.227228- `d` (integer) -- the dimension of the (affine) subspaces of `P = GF(q)^n`229which make up the blocks.230231- `F` -- a Finite Field (i.e. ``FiniteField(17)``), or a prime power232(i.e. an integer)233234`AG_{n,d} (F)`, as it is sometimes denoted, is a `2` - `(v, k, \lambda)`235design of points and `d`- flats (cosets of dimension `n`) in the affine236geometry `AG_n (F)`, where237238.. math::239240v = q^n,\ k = q^d ,241\lambda =\frac{(q^{n-1}-1) \cdots (q^{n+1-d}-1)}{(q^{n-1}-1) \cdots (q-1)}.242243Wraps some functions used in GAP Design's ``PGPointFlatBlockDesign``. Does244*not* require GAP's Design package.245246EXAMPLES::247248sage: BD = designs.AffineGeometryDesign(3, 1, GF(2))249sage: BD.parameters(t=2)250(2, 8, 2, 1)251sage: BD.is_block_design()252(True, [2, 8, 2, 1])253sage: BD = designs.AffineGeometryDesign(3, 2, GF(2))254sage: BD.parameters(t=3)255(3, 8, 4, 1)256sage: BD.is_block_design()257(True, [3, 8, 4, 1])258259A 3-design::260261sage: D = IncidenceStructure(range(32),designs.steiner_quadruple_system(32))262sage: D.is_block_design()263(True, [3, 32, 4, 1])264265With an integer instead of a Finite Field::266267sage: BD = designs.AffineGeometryDesign(3, 2, 4)268sage: BD.parameters(t=2)269(2, 64, 16, 5)270"""271try:272q = int(F)273except TypeError:274q = F.order()275276from sage.interfaces.gap import gap, GapElement277from sage.sets.set import Set278gap.eval("V:=GaloisField(%s)^%s"%(q,n))279gap.eval("points:=AsSet(V)")280gap.eval("Subs:=AsSet(Subspaces(V,%s));"%d)281gap.eval("CP:=Cartesian(points,Subs)")282flats = gap.eval("flats:=List(CP,x->Sum(x))") # affine spaces283gblcks = eval(gap.eval("Set(List(flats,f->Filtered([1..Length(points)],i->points[i] in f)));"))284v = q**n285gB = []286for b in gblcks:287gB.append([x-1 for x in b])288return BlockDesign(v, gB, name="AffineGeometryDesign")289290def WittDesign(n):291"""292INPUT:293294- ``n`` is in `9,10,11,12,21,22,23,24`.295296Wraps GAP Design's WittDesign. If ``n=24`` then this function returns the297large Witt design `W_{24}`, the unique (up to isomorphism) `5-(24,8,1)`298design. If ``n=12`` then this function returns the small Witt design299`W_{12}`, the unique (up to isomorphism) `5-(12,6,1)` design. The other300values of `n` return a block design derived from these.301302.. NOTE:303304Requires GAP's Design package (included in the gap_packages Sage spkg).305306EXAMPLES::307308sage: BD = designs.WittDesign(9) # optional - gap_packages (design package)309sage: BD.is_block_design() # optional - gap_packages (design package)310(2, 9, 3, 1)311sage: BD # optional - gap_packages (design package)312Incidence structure with 9 points and 12 blocks313sage: print BD # optional - gap_packages (design package)314WittDesign<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]]>315sage: BD = designs.WittDesign(12) # optional - gap_packages (design package)316sage: BD.is_block_design() # optional - gap_packages (design package)317(5, 12, 6, 1)318"""319from sage.interfaces.gap import gap, GapElement320gap.load_package("design")321gap.eval("B:=WittDesign(%s)"%n)322v = eval(gap.eval("B.v"))323gblcks = eval(gap.eval("B.blocks"))324gB = []325for b in gblcks:326gB.append([x-1 for x in b])327return BlockDesign(v, gB, name="WittDesign", test=True)328329def HadamardDesign(n):330"""331As described in Section 1, p. 10, in [CvL]. The input n must have the332property that there is a Hadamard matrix of order `n+1` (and that a333construction of that Hadamard matrix has been implemented...).334335EXAMPLES::336337sage: designs.HadamardDesign(7)338Incidence structure with 7 points and 7 blocks339sage: print designs.HadamardDesign(7)340HadamardDesign<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]]>341342REFERENCES:343344- [CvL] P. Cameron, J. H. van Lint, Designs, graphs, codes and345their links, London Math. Soc., 1991.346"""347from sage.combinat.matrices.hadamard_matrix import hadamard_matrix348from sage.matrix.constructor import matrix349H = hadamard_matrix(n+1)350H1 = H.matrix_from_columns(range(1,n+1))351H2 = H1.matrix_from_rows(range(1,n+1))352J = matrix(ZZ,n,n,[1]*n*n)353MS = J.parent()354A = MS((H2+J)/2) # convert -1's to 0's; coerce entries to ZZ355# A is the incidence matrix of the block design356return IncidenceStructureFromMatrix(A,name="HadamardDesign")357358def steiner_triple_system(n):359r"""360Returns a Steiner Triple System361362A Steiner Triple System (STS) of a set `\{0,...,n-1\}`363is a family `S` of 3-sets such that for any `i \not = j`364there exists exactly one set of `S` in which they are365both contained.366367It can alternatively be thought of as a factorization of368the complete graph `K_n` with triangles.369370A Steiner Triple System of a `n`-set exists if and only if371`n \equiv 1 \pmod 6` or `n \equiv 3 \pmod 6`, in which case372one can be found through Bose's and Skolem's constructions,373respectively [AndHon97]_.374375INPUT:376377- ``n`` returns a Steiner Triple System of `\{0,...,n-1\}`378379EXAMPLE:380381A Steiner Triple System on `9` elements ::382383sage: sts = designs.steiner_triple_system(9)384sage: sts385Incidence structure with 9 points and 12 blocks386sage: list(sts)387[[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]]388389As any pair of vertices is covered once, its parameters are ::390391sage: sts.parameters(t=2)392(2, 9, 3, 1)393394An exception is raised for invalid values of ``n`` ::395396sage: designs.steiner_triple_system(10)397Traceback (most recent call last):398...399ValueError: Steiner triple systems only exist for n = 1 mod 6 or n = 3 mod 6400401REFERENCE:402403.. [AndHon97] A short course in Combinatorial Designs,404Ian Anderson, Iiro Honkala,405Internet Editions, Spring 1997,406http://www.utu.fi/~honkala/designs.ps407"""408409name = "Steiner Triple System on "+str(n)+" elements"410411if n%6 == 3:412t = (n-3)/6413Z = range(2*t+1)414415T = lambda (x,y) : x + (2*t+1)*y416417sts = [[(i,0),(i,1),(i,2)] for i in Z] + \418[[(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]419420elif n%6 == 1:421422t = (n-1)/6423N = range(2*t)424T = lambda (x,y) : x+y*t*2 if (x,y) != (-1,-1) else n-1425426L1 = lambda i,j : (i+j) % (int((n-1)/3))427L = lambda i,j : L1(i,j)/2 if L1(i,j)%2 == 0 else t+(L1(i,j)-1)/2428429sts = [[(i,0),(i,1),(i,2)] for i in range(t)] + \430[[(-1,-1),(i,k),(i-t,(k+1) % 3)] for i in range(t,2*t) for k in [0,1,2]] + \431[[(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]432433else:434raise ValueError("Steiner triple systems only exist for n = 1 mod 6 or n = 3 mod 6")435436from sage.sets.set import Set437sts = Set(map(lambda x: Set(map(T,x)),sts))438439return BlockDesign(n, sts, name=name)440441def BlockDesign(max_pt, blks, name=None, test=True):442"""443Returns an instance of the :class:`IncidenceStructure` class.444445Requires each B in blks to be contained in range(max_pt). Does not test if446the result is a block design.447448EXAMPLES::449450sage: 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")451Incidence structure with 7 points and 7 blocks452sage: 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")453Fano 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]]>454"""455nm = name456tst = test457if nm == None and test:458nm = "BlockDesign"459BD = BlockDesign_generic( range(max_pt), blks, name=nm, test=tst )460if not(test):461return BD462else:463pars = BD.parameters(t=2)464if BD.block_design_checker(pars[0],pars[1],pars[2],pars[3]):465return BD466else:467raise TypeError("parameters are not those of a block design.")468469# Possibly in the future there will be methods which apply to block designs and470# not incidence structures. None have been implemented yet though. The class471# name BlockDesign_generic is reserved in the name space in case more472# specialized methods are implemented later. In that case, BlockDesign_generic473# should inherit from IncidenceStructure.474BlockDesign_generic = IncidenceStructure475476477