Example #1
0
    def __init__(self, points, blocks, K=None, lambd=1, check=True, copy=True,**kwds):
        r"""
        Constructor

        EXAMPLE::

            sage: designs.balanced_incomplete_block_design(13,3) # indirect doctest
            (13,3,1)-Balanced Incomplete Block Design

        """
        try:
            i = int(points)
        except TypeError:
            pass
        else:
            points = range(i)

        GroupDivisibleDesign.__init__(self,
                                      points,
                                      [[x] for x in points],
                                      blocks,
                                      K=K,
                                      lambd=lambd,
                                      check=check,
                                      copy=copy,
                                      **kwds)
Example #2
0
    def __init__(self,
                 points,
                 blocks,
                 K=None,
                 lambd=1,
                 check=True,
                 copy=True,
                 **kwds):
        r"""
        Constructor

        EXAMPLE::

            sage: designs.balanced_incomplete_block_design(13,3) # indirect doctest
            (13,3,1)-Balanced Incomplete Block Design

        """
        try:
            i = int(points)
        except TypeError:
            pass
        else:
            points = range(i)

        GroupDivisibleDesign.__init__(self,
                                      points, [[x] for x in points],
                                      blocks,
                                      K=K,
                                      lambd=lambd,
                                      check=check,
                                      copy=copy,
                                      **kwds)
Example #3
0
def PBD_4_7(v,check=True, existence=False):
    r"""
    Return a `(v,\{4,7\})`-PBD

    For all `v` such that `n\equiv 1\pmod{3}` and `n\neq 10,19, 31` there exists
    a `(v,\{4,7\})`-PBD. This is proved in Proposition IX.4.5 from [BJL99]_,
    which this method implements.

    This construction of PBD is used by the construction of Kirkman Triple
    Systems.

    EXAMPLE::

        sage: from sage.combinat.designs.resolvable_bibd import PBD_4_7
        sage: PBD_4_7(22)
        Pairwise Balanced Design on 22 points with sets of sizes in set([4, 7])

    TESTS:

    All values `\leq 300`::

        sage: for i in range(1,300,3):
        ....:     if i not in [10,19,31]:
        ....:         assert PBD_4_7(i,existence=True)
        ....:         _ = PBD_4_7(i,check=True)
    """
    if v%3 != 1 or v in [10,19,31]:
        if existence:
            return Unknown
        raise NotImplementedError
    if existence:
        return True

    from group_divisible_designs import GroupDivisibleDesign
    from group_divisible_designs import GDD_4_2
    from bibd import PairwiseBalancedDesign
    from bibd import balanced_incomplete_block_design

    if v == 22:
        # Beth/Jungnickel/Lenz: take KTS(15) and extend each of the 7 classes
        # with a new point. Make those new points a 7-set.
        KTS15 = kirkman_triple_system(15)
        blocks = [S+[i+15] for i,classs in enumerate(KTS15._classes) for S in classs]+[range(15,22)]

    elif v == 34:
        # [BJL99] (p527,vol1), but originally Brouwer
        A = [(0,0),(1,1),(2,0),(4,1)]
        B = [(0,0),(1,0),(4,2)]
        C = [(0,0),(2,2),(5,0)]
        D = [(0,0),(0,1),(0,2)]

        A = [[(x+i,  y+j)     for x,y in A]        for i in range(9) for j in range(3)]
        B = [[(x+i,  y+i+j)   for x,y in B]+[27+j] for i in range(9) for j in range(3)]
        C = [[(x+i+j,y+2*i+j) for x,y in C]+[30+j] for i in range(9) for j in range(3)]
        D = [[(x+i,  y+i)     for x,y in D]+[33]   for i in range(9)]

        blocks = [[int(x) if not isinstance(x,tuple) else (x[1]%3)*9+(x[0]%9) for x in S]
                  for S in A+B+C+D+[range(27,34)]]
    elif v == 46:
        # [BJL99] (p527,vol1), but originally Brouwer
        A = [(1,0),(3,0),(9,0),(0,1)]
        B = [(2,0),(6,0),(5,0),(0,1)]
        C = [(0,0),(1,1),(4,2)]
        D = [(0,0),(2,1),(7,2)]
        E = [(0,0),(0,1),(0,2)]

        A = [[(x+i,  y+j) for x,y in A]        for i in range(13) for j in range(3)]
        B = [[(x+i,  y+j) for x,y in B]        for i in range(13) for j in range(3)]
        C = [[(x+i,  y+j) for x,y in C]+[39+j] for i in range(13) for j in range(3)]
        D = [[(x+i,  y+j) for x,y in D]+[42+j] for i in range(13) for j in range(3)]
        E = [[(x+i,  y+i) for x,y in E]+[45]   for i in range(13)]

        blocks = [[int(x) if not isinstance(x,tuple) else (x[1]%3)*13+(x[0]%13) for x in S]
                  for S in A+B+C+D+E+[range(39,46)]]

    elif v == 58:
        # [BJL99] (p527,vol1), but originally Brouwer
        A = [(0,0),(1,0),(4,0),( 5,1)]
        B = [(0,0),(2,0),(8,0),(11,1)]
        C = [(0,0),(5,0),(2,1),(12,1)]
        D = [(0,0),(8,1),(7,2)]
        E = [(0,0),(6,1),(4,2)]
        F = [(0,0),(0,1),(0,2)]

        A = [[(x+i,  y+j) for x,y in A]        for i in range(17) for j in range(3)]
        B = [[(x+i,  y+j) for x,y in B]        for i in range(17) for j in range(3)]
        C = [[(x+i,  y+j) for x,y in C]        for i in range(17) for j in range(3)]
        D = [[(x+i,  y+j) for x,y in D]+[51+j] for i in range(17) for j in range(3)]
        E = [[(x+i,  y+j) for x,y in E]+[54+j] for i in range(17) for j in range(3)]
        F = [[(x+i,  y+i) for x,y in F]+[57]   for i in range(17)]

        blocks = [[int(x) if not isinstance(x,tuple) else (x[1]%3)*17+(x[0]%17) for x in S]
                  for S in A+B+C+D+E+F+[range(51,58)]]

    elif v == 70:
        # [BJL99] (p527,vol1), but originally Brouwer
        A = [(0,0),(1,0),(5,1),(13,1)]
        B = [(0,0),(4,0),(20,1),(10,1)]
        C = [(0,0),(16,0),(17,1),(19,1)]
        D = [(0,0),(2,1),(8,1),(11,1)]
        E = [(0,0),(3,2),(9,1)]
        F = [(0,0),(7,0),(14,1)]
        H = [(0,0),(0,1),(0,2)]

        A = [[(x+i,  y+j)       for x,y in A]        for i in range(21) for j in range(3)]
        B = [[(x+i,  y+j)       for x,y in B]        for i in range(21) for j in range(3)]
        C = [[(x+i,  y+j)       for x,y in C]        for i in range(21) for j in range(3)]
        D = [[(x+i,  y+j)       for x,y in D]        for i in range(21) for j in range(3)]
        E = [[(x+i,  y+j)       for x,y in E]+[63+j] for i in range(21) for j in range(3)]
        F = [[(x+3*i+j, y+ii+j) for x,y in F]+[66+j] for i in range( 7) for j in range(3) for ii in range(3)]
        H = [[(x+i,  y+i)       for x,y in H]+[69]   for i in range(21)]

        blocks = [[int(x) if not isinstance(x,tuple) else (x[1]%3)*21+(x[0]%21)
                   for x in S]
                  for S in A+B+C+D+E+F+H+[range(63,70)]]

    elif v == 82:
        # This construction is Theorem IX.3.16 from [BJL99] (p.627).
        #
        # A (15,{4},{3})-GDD from a (16,4)-BIBD
        from group_divisible_designs import group_divisible_design
        from orthogonal_arrays       import transversal_design
        GDD = group_divisible_design(3*5,K=[4],G=[3],check=False)
        TD  = transversal_design(5,5)

        # A (75,{4},{15})-GDD
        GDD2 = [[3*B[x//3]+x%3 for x in BB] for B in TD for BB in GDD]

        # We now complete the (75,{4},{15})-GDD into a (82,{4,7})-PBD. For this,
        # we add 7 new points that are added to all groups of size 15.
        #
        # On these groups a (15+7,{4,7})-PBD is pasted, in such a way that the 7
        # new points are a set of the final PBD
        PBD22 = PBD_4_7(15+7)
        S = next(SS for SS in PBD22 if len(SS) == 7) # a set of size 7
        PBD22.relabel({v:i for i,v in enumerate([i for i in range(15+7) if i not in S] + S)})

        for B in PBD22:
            if B == S:
                continue
            for i in range(5):
                GDD2.append([x+i*15 if x<15 else x+60 for x in B])

        GDD2.append(range(75,82))
        blocks = GDD2

    elif v == 94:
        # IX.4.5.l from [BJL99].
        #
        # take 4 parallel lines from an affine plane of order 7, and a 5th
        # one. This is a (31,{4,5,7})-BIBD. And 94=3*31+1.
        from sage.combinat.designs.block_design import AffineGeometryDesign
        AF = AffineGeometryDesign(2,1,7)
        parall = []
        plus_one = None
        for S in AF:
            if all(all(x not in SS for x in S) for SS in parall):
                parall.append(S)
            elif plus_one is None:
                plus_one = S
            if len(parall) == 4 and plus_one is not None:
                break
        X = set(sum(parall,plus_one))

        S_4_5_7 = [X.intersection(S) for S in AF]
        S_4_5_7 = [S for S in S_4_5_7 if len(S)>1]
        S_4_5_7 = PairwiseBalancedDesign(X,
                                         blocks = S_4_5_7,
                                         K = [4,5,7],
                                         check=False)
        S_4_5_7.relabel()
        return PBD_4_7_from_Y(S_4_5_7,check=check)

    elif v == 127 or v == 142:
        # IX.4.5.o from [BJL99].
        #
        # Attach two or seven infinite points to a (40,4)-RBIBD to get a
        # (42,{4,5},{1,2,7})-GDD or a (47,{4,5},{1,2,7})-GDD
        points_to_add = 2 if v == 127 else 7
        rBIBD4 = v_4_1_rbibd(40)
        GDD = [S+[40+i] if i<points_to_add else S
               for i,classs in enumerate(rBIBD4._classes)
               for S in classs]
        if points_to_add == 7:
            GDD.append(range(40,40+points_to_add))
            groups = [[x] for x in range(40+points_to_add)]
        else:
            groups = [[x] for x in range(40)]
            groups.append(range(40,40+points_to_add))
        GDD = GroupDivisibleDesign(40+points_to_add,
                                   groups = groups,
                                   blocks = GDD,
                                   K = [2,4,5,7],
                                   check = False,
                                   copy = False)

        return PBD_4_7_from_Y(GDD,check=check)

    elif v%6 == 1 and GDD_4_2((v-1)/6,existence=True):
        # VII.5.17 from [BJL99]
        gdd = GDD_4_2((v-1)/6)
        return PBD_4_7_from_Y(gdd,check=check)

    elif v == 202:
        # IV.4.5.p from [BJL99]
        PBD = PBD_4_7(22,check=False)
        PBD = PBD_4_7_from_Y(PBD,check=False)
        return PBD_4_7_from_Y(PBD,check=check)

    elif balanced_incomplete_block_design(v,4,existence=True):
        return balanced_incomplete_block_design(v,4)
    elif balanced_incomplete_block_design(v,7,existence=True):
        return balanced_incomplete_block_design(v,7)
    else:
        from sage.combinat.designs.orthogonal_arrays import orthogonal_array
        # IX.4.5.m from [BJL99].
        #
        # This construction takes a TD(5,g) and truncates its last column to
        # size u: it yields a (4g+u,{4,5},{g,u})-GDD. If there exists a
        # (3g+1,{4,7})-PBD and a (3u+1,{4,7})-PBD, then we can apply the x->3x+1
        # construction on the truncated transversal design (which is a GDD).
        #
        # We write vv = 4g+u while satisfying the hypotheses.
        vv = (v-1)/3
        for g in range((vv+5-1)//5,vv//4+1):
            u = vv-4*g
            if (orthogonal_array(5,g,existence=True) and
                PBD_4_7(3*g+1,existence=True)        and
                PBD_4_7(3*u+1,existence=True)):
                from orthogonal_arrays import transversal_design
                domain = set(range(vv))
                GDD = transversal_design(5,g)
                GDD = GroupDivisibleDesign(vv,
                                           groups = [[x for x in gr if x in domain] for gr in GDD.groups()],
                                           blocks = [[x for x in B if x in domain] for B in GDD],
                                           G = set([g,u]),
                                           K = [4,5],
                                           check=False)
                return PBD_4_7_from_Y(GDD,check=check)

    return PairwiseBalancedDesign(v,
                                  blocks = blocks,
                                  K = [4,7],
                                  check = check,
                                  copy = False)
Example #4
0
def PBD_4_7(v, check=True, existence=False):
    r"""
    Return a `(v,\{4,7\})`-PBD

    For all `v` such that `n\equiv 1\pmod{3}` and `n\neq 10,19, 31` there exists
    a `(v,\{4,7\})`-PBD. This is proved in Proposition IX.4.5 from [BJL99]_,
    which this method implements.

    This construction of PBD is used by the construction of Kirkman Triple
    Systems.

    EXAMPLE::

        sage: from sage.combinat.designs.resolvable_bibd import PBD_4_7
        sage: PBD_4_7(22)
        Pairwise Balanced Design on 22 points with sets of sizes in set([4, 7])

    TESTS:

    All values `\leq 300`::

        sage: for i in range(1,300,3):
        ....:     if i not in [10,19,31]:
        ....:         assert PBD_4_7(i,existence=True)
        ....:         _ = PBD_4_7(i,check=True)
    """
    if v % 3 != 1 or v in [10, 19, 31]:
        if existence:
            return Unknown
        raise NotImplementedError
    if existence:
        return True

    from group_divisible_designs import GroupDivisibleDesign
    from group_divisible_designs import GDD_4_2
    from bibd import PairwiseBalancedDesign
    from bibd import balanced_incomplete_block_design

    if v == 22:
        # Beth/Jungnickel/Lenz: take KTS(15) and extend each of the 7 classes
        # with a new point. Make those new points a 7-set.
        KTS15 = kirkman_triple_system(15)
        blocks = [
            S + [i + 15] for i, classs in enumerate(KTS15._classes)
            for S in classs
        ] + [range(15, 22)]

    elif v == 34:
        # [BJL99] (p527,vol1), but originally Brouwer
        A = [(0, 0), (1, 1), (2, 0), (4, 1)]
        B = [(0, 0), (1, 0), (4, 2)]
        C = [(0, 0), (2, 2), (5, 0)]
        D = [(0, 0), (0, 1), (0, 2)]

        A = [[(x + i, y + j) for x, y in A] for i in range(9)
             for j in range(3)]
        B = [[(x + i, y + i + j) for x, y in B] + [27 + j] for i in range(9)
             for j in range(3)]
        C = [[(x + i + j, y + 2 * i + j) for x, y in C] + [30 + j]
             for i in range(9) for j in range(3)]
        D = [[(x + i, y + i) for x, y in D] + [33] for i in range(9)]

        blocks = [[
            int(x) if not isinstance(x, tuple) else (x[1] % 3) * 9 + (x[0] % 9)
            for x in S
        ] for S in A + B + C + D + [range(27, 34)]]
    elif v == 46:
        # [BJL99] (p527,vol1), but originally Brouwer
        A = [(1, 0), (3, 0), (9, 0), (0, 1)]
        B = [(2, 0), (6, 0), (5, 0), (0, 1)]
        C = [(0, 0), (1, 1), (4, 2)]
        D = [(0, 0), (2, 1), (7, 2)]
        E = [(0, 0), (0, 1), (0, 2)]

        A = [[(x + i, y + j) for x, y in A] for i in range(13)
             for j in range(3)]
        B = [[(x + i, y + j) for x, y in B] for i in range(13)
             for j in range(3)]
        C = [[(x + i, y + j) for x, y in C] + [39 + j] for i in range(13)
             for j in range(3)]
        D = [[(x + i, y + j) for x, y in D] + [42 + j] for i in range(13)
             for j in range(3)]
        E = [[(x + i, y + i) for x, y in E] + [45] for i in range(13)]

        blocks = [[
            int(x) if not isinstance(x, tuple) else
            (x[1] % 3) * 13 + (x[0] % 13) for x in S
        ] for S in A + B + C + D + E + [range(39, 46)]]

    elif v == 58:
        # [BJL99] (p527,vol1), but originally Brouwer
        A = [(0, 0), (1, 0), (4, 0), (5, 1)]
        B = [(0, 0), (2, 0), (8, 0), (11, 1)]
        C = [(0, 0), (5, 0), (2, 1), (12, 1)]
        D = [(0, 0), (8, 1), (7, 2)]
        E = [(0, 0), (6, 1), (4, 2)]
        F = [(0, 0), (0, 1), (0, 2)]

        A = [[(x + i, y + j) for x, y in A] for i in range(17)
             for j in range(3)]
        B = [[(x + i, y + j) for x, y in B] for i in range(17)
             for j in range(3)]
        C = [[(x + i, y + j) for x, y in C] for i in range(17)
             for j in range(3)]
        D = [[(x + i, y + j) for x, y in D] + [51 + j] for i in range(17)
             for j in range(3)]
        E = [[(x + i, y + j) for x, y in E] + [54 + j] for i in range(17)
             for j in range(3)]
        F = [[(x + i, y + i) for x, y in F] + [57] for i in range(17)]

        blocks = [[
            int(x) if not isinstance(x, tuple) else
            (x[1] % 3) * 17 + (x[0] % 17) for x in S
        ] for S in A + B + C + D + E + F + [range(51, 58)]]

    elif v == 70:
        # [BJL99] (p527,vol1), but originally Brouwer
        A = [(0, 0), (1, 0), (5, 1), (13, 1)]
        B = [(0, 0), (4, 0), (20, 1), (10, 1)]
        C = [(0, 0), (16, 0), (17, 1), (19, 1)]
        D = [(0, 0), (2, 1), (8, 1), (11, 1)]
        E = [(0, 0), (3, 2), (9, 1)]
        F = [(0, 0), (7, 0), (14, 1)]
        H = [(0, 0), (0, 1), (0, 2)]

        A = [[(x + i, y + j) for x, y in A] for i in range(21)
             for j in range(3)]
        B = [[(x + i, y + j) for x, y in B] for i in range(21)
             for j in range(3)]
        C = [[(x + i, y + j) for x, y in C] for i in range(21)
             for j in range(3)]
        D = [[(x + i, y + j) for x, y in D] for i in range(21)
             for j in range(3)]
        E = [[(x + i, y + j) for x, y in E] + [63 + j] for i in range(21)
             for j in range(3)]
        F = [[(x + 3 * i + j, y + ii + j) for x, y in F] + [66 + j]
             for i in range(7) for j in range(3) for ii in range(3)]
        H = [[(x + i, y + i) for x, y in H] + [69] for i in range(21)]

        blocks = [[
            int(x) if not isinstance(x, tuple) else
            (x[1] % 3) * 21 + (x[0] % 21) for x in S
        ] for S in A + B + C + D + E + F + H + [range(63, 70)]]

    elif v == 82:
        # This construction is Theorem IX.3.16 from [BJL99] (p.627).
        #
        # A (15,{4},{3})-GDD from a (16,4)-BIBD
        from group_divisible_designs import group_divisible_design
        from orthogonal_arrays import transversal_design
        GDD = group_divisible_design(3 * 5, K=[4], G=[3], check=False)
        TD = transversal_design(5, 5)

        # A (75,{4},{15})-GDD
        GDD2 = [[3 * B[x // 3] + x % 3 for x in BB] for B in TD for BB in GDD]

        # We now complete the (75,{4},{15})-GDD into a (82,{4,7})-PBD. For this,
        # we add 7 new points that are added to all groups of size 15.
        #
        # On these groups a (15+7,{4,7})-PBD is pasted, in such a way that the 7
        # new points are a set of the final PBD
        PBD22 = PBD_4_7(15 + 7)
        S = next(SS for SS in PBD22 if len(SS) == 7)  # a set of size 7
        PBD22.relabel({
            v: i
            for i, v in enumerate([i for i in range(15 + 7) if i not in S] + S)
        })

        for B in PBD22:
            if B == S:
                continue
            for i in range(5):
                GDD2.append([x + i * 15 if x < 15 else x + 60 for x in B])

        GDD2.append(range(75, 82))
        blocks = GDD2

    elif v == 94:
        # IX.4.5.l from [BJL99].
        #
        # take 4 parallel lines from an affine plane of order 7, and a 5th
        # one. This is a (31,{4,5,7})-BIBD. And 94=3*31+1.
        from sage.combinat.designs.block_design import AffineGeometryDesign
        AF = AffineGeometryDesign(2, 1, 7)
        parall = []
        plus_one = None
        for S in AF:
            if all(all(x not in SS for x in S) for SS in parall):
                parall.append(S)
            elif plus_one is None:
                plus_one = S
            if len(parall) == 4 and plus_one is not None:
                break
        X = set(sum(parall, plus_one))

        S_4_5_7 = [X.intersection(S) for S in AF]
        S_4_5_7 = [S for S in S_4_5_7 if len(S) > 1]
        S_4_5_7 = PairwiseBalancedDesign(X,
                                         blocks=S_4_5_7,
                                         K=[4, 5, 7],
                                         check=False)
        S_4_5_7.relabel()
        return PBD_4_7_from_Y(S_4_5_7, check=check)

    elif v == 127 or v == 142:
        # IX.4.5.o from [BJL99].
        #
        # Attach two or seven infinite points to a (40,4)-RBIBD to get a
        # (42,{4,5},{1,2,7})-GDD or a (47,{4,5},{1,2,7})-GDD
        points_to_add = 2 if v == 127 else 7
        rBIBD4 = v_4_1_rbibd(40)
        GDD = [
            S + [40 + i] if i < points_to_add else S
            for i, classs in enumerate(rBIBD4._classes) for S in classs
        ]
        if points_to_add == 7:
            GDD.append(range(40, 40 + points_to_add))
            groups = [[x] for x in range(40 + points_to_add)]
        else:
            groups = [[x] for x in range(40)]
            groups.append(range(40, 40 + points_to_add))
        GDD = GroupDivisibleDesign(40 + points_to_add,
                                   groups=groups,
                                   blocks=GDD,
                                   K=[2, 4, 5, 7],
                                   check=False,
                                   copy=False)

        return PBD_4_7_from_Y(GDD, check=check)

    elif v % 6 == 1 and GDD_4_2((v - 1) / 6, existence=True):
        # VII.5.17 from [BJL99]
        gdd = GDD_4_2((v - 1) / 6)
        return PBD_4_7_from_Y(gdd, check=check)

    elif v == 202:
        # IV.4.5.p from [BJL99]
        PBD = PBD_4_7(22, check=False)
        PBD = PBD_4_7_from_Y(PBD, check=False)
        return PBD_4_7_from_Y(PBD, check=check)

    elif balanced_incomplete_block_design(v, 4, existence=True):
        return balanced_incomplete_block_design(v, 4)
    elif balanced_incomplete_block_design(v, 7, existence=True):
        return balanced_incomplete_block_design(v, 7)
    else:
        from sage.combinat.designs.orthogonal_arrays import orthogonal_array
        # IX.4.5.m from [BJL99].
        #
        # This construction takes a TD(5,g) and truncates its last column to
        # size u: it yields a (4g+u,{4,5},{g,u})-GDD. If there exists a
        # (3g+1,{4,7})-PBD and a (3u+1,{4,7})-PBD, then we can apply the x->3x+1
        # construction on the truncated transversal design (which is a GDD).
        #
        # We write vv = 4g+u while satisfying the hypotheses.
        vv = (v - 1) / 3
        for g in range((vv + 5 - 1) // 5, vv // 4 + 1):
            u = vv - 4 * g
            if (orthogonal_array(5, g, existence=True)
                    and PBD_4_7(3 * g + 1, existence=True)
                    and PBD_4_7(3 * u + 1, existence=True)):
                from orthogonal_arrays import transversal_design
                domain = set(range(vv))
                GDD = transversal_design(5, g)
                GDD = GroupDivisibleDesign(
                    vv,
                    groups=[[x for x in gr if x in domain]
                            for gr in GDD.groups()],
                    blocks=[[x for x in B if x in domain] for B in GDD],
                    G=set([g, u]),
                    K=[4, 5],
                    check=False)
                return PBD_4_7_from_Y(GDD, check=check)

    return PairwiseBalancedDesign(v,
                                  blocks=blocks,
                                  K=[4, 7],
                                  check=check,
                                  copy=False)