Example #1
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 #2
0
def group_divisible_design(v, K, G, existence=False, check=False):
    r"""
    Return a `(v,K,G)`-Group Divisible Design.

    A `(v,K,G)`-GDD is a pair `(\mathcal G, \mathcal B)` where:

    - `\mathcal G` is a partition of `X=\bigcup \mathcal G` where `|X|=v`

    - `\forall S\in \mathcal G, |S| \in G`

    - `\forall S\in \mathcal B, |S| \in K`

    - `\mathcal G\cup \mathcal B` is a `(v,K\cup G)`-PBD

    For more information, see the documentation of
    :class:`~sage.combinat.designs.incidence_structures.GroupDivisibleDesign` or
    :class:`~sage.combinat.designs.bibd.PairwiseBalancedDesign`.

    INPUT:

    - ``v`` (integer)

    - ``K,G`` (sets of integers)

    - ``existence`` (boolean) -- instead of building the design, return:

        - ``True`` -- meaning that Sage knows how to build the design

        - ``Unknown`` -- meaning that Sage does not know how to build the
          design, but that the design may exist (see :mod:`sage.misc.unknown`).

        - ``False`` -- meaning that the design does not exist.

    - ``check`` -- (boolean) Whether to check that output is correct before
      returning it. As this is expected to be useless (but we are cautious
      guys), you may want to disable it whenever you want speed. Set to ``True``
      by default.

    .. NOTE::

        The GDD returned by this function are defined on ``range(v)``, and its
        groups are sets of consecutive integers.

    EXAMPLES::

        sage: designs.group_divisible_design(14,{4},{2})
        Group Divisible Design on 14 points of type 2^7
    """
    G = list(set(G))
    K = list(set(K))

    blocks = None

    # from a (v+1,k,1)-BIBD
    if (len(G) == 1 and len(K) == 1 and G[0] + 1 in K):
        from bibd import balanced_incomplete_block_design
        k = K[0]
        if existence:
            return balanced_incomplete_block_design(v + 1, k, existence=True)
        BIBD = balanced_incomplete_block_design(v + 1, k)
        groups = [[x for x in S if x != v] for S in BIBD if v in S]
        d = {p: i for i, p in enumerate(sum(groups, []))}
        d[v] = v
        BIBD.relabel(d)
        groups = [
            range((k - 1) * i, (k - 1) * (i + 1)) for i in range(v // (k - 1))
        ]
        blocks = [S for S in BIBD if v not in S]

    # (v,{4},{2})-GDD
    elif (v % 2 == 0 and K == [4] and G == [2]
          and GDD_4_2(v // 2, existence=True)):
        if existence:
            return True
        return GDD_4_2(v // 2, check=check)

    # From a TD(k,g)
    elif (len(G) == 1 and len(K) == 1 and K[0] * G[0] == v):
        from orthogonal_arrays import transversal_design
        return transversal_design(k=K[0], n=G[0], existence=existence)

    if blocks:
        return GroupDivisibleDesign(v,
                                    groups=groups,
                                    blocks=blocks,
                                    G=G,
                                    K=K,
                                    check=check,
                                    copy=True)

    if existence:
        return Unknown
    raise NotImplementedError
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)
def group_divisible_design(v,K,G,existence=False,check=False):
    r"""
    Return a `(v,K,G)`-Group Divisible Design.

    A `(v,K,G)`-GDD is a pair `(\mathcal G, \mathcal B)` where:

    - `\mathcal G` is a partition of `X=\bigcup \mathcal G` where `|X|=v`

    - `\forall S\in \mathcal G, |S| \in G`

    - `\forall S\in \mathcal B, |S| \in K`

    - `\mathcal G\cup \mathcal B` is a `(v,K\cup G)`-PBD

    For more information, see the documentation of
    :class:`~sage.combinat.designs.incidence_structures.GroupDivisibleDesign` or
    :class:`~sage.combinat.designs.bibd.PairwiseBalancedDesign`.

    INPUT:

    - ``v`` (integer)

    - ``K,G`` (sets of integers)

    - ``existence`` (boolean) -- instead of building the design, return:

        - ``True`` -- meaning that Sage knows how to build the design

        - ``Unknown`` -- meaning that Sage does not know how to build the
          design, but that the design may exist (see :mod:`sage.misc.unknown`).

        - ``False`` -- meaning that the design does not exist.

    - ``check`` -- (boolean) Whether to check that output is correct before
      returning it. As this is expected to be useless (but we are cautious
      guys), you may want to disable it whenever you want speed. Set to ``True``
      by default.

    .. NOTE::

        The GDD returned by this function are defined on ``range(v)``, and its
        groups are sets of consecutive integers.

    EXAMPLES::

        sage: designs.group_divisible_design(14,{4},{2})
        Group Divisible Design on 14 points of type 2^7
    """
    G = list(set(G))
    K = list(set(K))

    blocks = None

    # from a (v+1,k,1)-BIBD
    if (len(G) == 1 and
        len(K) == 1 and
        G[0]+1 in K):
        from bibd import balanced_incomplete_block_design
        k = K[0]
        if existence:
            return balanced_incomplete_block_design(v+1,k,existence=True)
        BIBD = balanced_incomplete_block_design(v+1,k)
        groups = [[x for x in S if x!=v] for S in BIBD if v in S]
        d = {p:i for i,p in enumerate(sum(groups,[]))}
        d[v]=v
        BIBD.relabel(d)
        groups = [range((k-1)*i,(k-1)*(i+1)) for i in range(v//(k-1))]
        blocks = [S for S in BIBD if v not in S]

    # (v,{4},{2})-GDD
    elif (v%2==0   and
          K == [4] and
          G == [2] and
          GDD_4_2(v//2,existence=True)):
        if existence:
            return True
        return GDD_4_2(v//2,check=check)

    # From a TD(k,g)
    elif (len(G)    == 1 and
          len(K)    == 1 and
          K[0]*G[0] == v):
        from orthogonal_arrays import transversal_design
        return transversal_design(k=K[0],n=G[0],existence=existence)

    if blocks:
        return GroupDivisibleDesign(v,
                                    groups = groups,
                                    blocks = blocks,
                                    G = G,
                                    K = K,
                                    check = check,
                                    copy  = True)

    if existence:
        return Unknown
    raise NotImplementedError