Exemple #1
0
def balanced_incomplete_block_design(v, k, existence=False, use_LJCR=False):
    r"""
    Return a BIBD of parameters `v,k`.

    A Balanced Incomplete Block Design of parameters `v,k` is a collection
    `\mathcal C` of `k`-subsets of `V=\{0,\dots,v-1\}` such that for any two
    distinct elements `x,y\in V` there is a unique element `S\in \mathcal C`
    such that `x,y\in S`.

    More general definitions sometimes involve a `\lambda` parameter, and we
    assume here that `\lambda=1`.

    For more information on BIBD, see the
    :wikipedia:`corresponding Wikipedia entry <Block_design#Definition_of_a_BIBD_.28or_2-design.29>`.

    INPUT:

    - ``v,k`` (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.

    - ``use_LJCR`` (boolean) -- whether to query the La Jolla Covering
      Repository for the design when Sage does not know how to build it (see
      :func:`~sage.combinat.designs.covering_design.best_known_covering_design_www`). This
      requires internet.

    .. SEEALSO::

        * :func:`steiner_triple_system`
        * :func:`v_4_1_BIBD`
        * :func:`v_5_1_BIBD`

    TODO:

        * Implement other constructions from the Handbook of Combinatorial
          Designs.

    EXAMPLES::

        sage: designs.balanced_incomplete_block_design(7, 3).blocks()
        [[0, 1, 3], [0, 2, 4], [0, 5, 6], [1, 2, 6], [1, 4, 5], [2, 3, 5], [3, 4, 6]]
        sage: B = designs.balanced_incomplete_block_design(66, 6, use_LJCR=True) # optional - internet
        sage: B                                                              # optional - internet
        Incidence structure with 66 points and 143 blocks
        sage: B.blocks()                                                     # optional - internet
        [[0, 1, 2, 3, 4, 65], [0, 5, 24, 25, 39, 57], [0, 6, 27, 38, 44, 55], ...
        sage: designs.balanced_incomplete_block_design(66, 6, use_LJCR=True)  # optional - internet
        Incidence structure with 66 points and 143 blocks
        sage: designs.balanced_incomplete_block_design(216, 6)
        Traceback (most recent call last):
        ...
        NotImplementedError: I don't know how to build a (216,6,1)-BIBD!

    TESTS::

        sage: designs.balanced_incomplete_block_design(85,5,existence=True)
        True
        sage: _ = designs.balanced_incomplete_block_design(85,5)

    A BIBD from a Finite Projective Plane::

        sage: _ = designs.balanced_incomplete_block_design(21,5)

    Some trivial BIBD::

        sage: designs.balanced_incomplete_block_design(10,10)
        (10,10,1)-Balanced Incomplete Block Design
        sage: designs.balanced_incomplete_block_design(1,10)
        (1,0,1)-Balanced Incomplete Block Design

    Existence of BIBD with `k=3,4,5`::

        sage: [v for v in xrange(50) if designs.balanced_incomplete_block_design(v,3,existence=True)]
        [1, 3, 7, 9, 13, 15, 19, 21, 25, 27, 31, 33, 37, 39, 43, 45, 49]
        sage: [v for v in xrange(100) if designs.balanced_incomplete_block_design(v,4,existence=True)]
        [1, 4, 13, 16, 25, 28, 37, 40, 49, 52, 61, 64, 73, 76, 85, 88, 97]
        sage: [v for v in xrange(150) if designs.balanced_incomplete_block_design(v,5,existence=True)]
        [1, 5, 21, 25, 41, 45, 61, 65, 81, 85, 101, 105, 121, 125, 141, 145]

    For `k > 5` there are currently very few constructions::

        sage: [v for v in xrange(300) if designs.balanced_incomplete_block_design(v,6,existence=True) is True]
        [1, 6, 31, 66, 76, 91, 96, 106, 111, 121, 126, 136, 141, 151, 156, 171, 181, 186, 196, 201, 211, 241, 271]
        sage: [v for v in xrange(300) if designs.balanced_incomplete_block_design(v,6,existence=True) is Unknown]
        [51, 61, 81, 166, 216, 226, 231, 246, 256, 261, 276, 286, 291]

    Here are some constructions with `k \geq 7` and `v` a prime power::

        sage: designs.balanced_incomplete_block_design(169,7)
        (169,7,1)-Balanced Incomplete Block Design
        sage: designs.balanced_incomplete_block_design(617,8)
        (617,8,1)-Balanced Incomplete Block Design
        sage: designs.balanced_incomplete_block_design(433,9)
        (433,9,1)-Balanced Incomplete Block Design
        sage: designs.balanced_incomplete_block_design(1171,10)
        (1171,10,1)-Balanced Incomplete Block Design

    And we know some inexistence results::

        sage: designs.balanced_incomplete_block_design(21,6,existence=True)
        False
    """
    lmbd = 1

    # Trivial BIBD
    if v == 1:
        if existence:
            return True
        return BalancedIncompleteBlockDesign(v, [], check=False)

    if k == v:
        if existence:
            return True
        return BalancedIncompleteBlockDesign(v, [range(v)], check=False, copy=False)

    # Non-existence of BIBD
    if (v < k or
        k < 2 or
        (v-1) % (k-1) != 0 or
        (v*(v-1)) % (k*(k-1)) != 0 or
        # From the Handbook of combinatorial designs:
        #
        # With lambda>1 other exceptions are
        # (15,5,2),(21,6,2),(22,7,2),(22,8,4).
        (k==6 and v in [36,46]) or
        (k==7 and v == 43) or
        # Fisher's inequality
        (v*(v-1))/(k*(k-1)) < v):
        if existence:
            return False
        raise EmptySetError("There exists no ({},{},{})-BIBD".format(v,k,lmbd))

    if k == 2:
        if existence:
            return True
        from itertools import combinations
        return BalancedIncompleteBlockDesign(v, combinations(range(v),2), check=False, copy=True)
    if k == 3:
        if existence:
            return v%6 == 1 or v%6 == 3
        return steiner_triple_system(v)
    if k == 4:
        if existence:
            return v%12 == 1 or v%12 == 4
        return BalancedIncompleteBlockDesign(v, v_4_1_BIBD(v), copy=False)
    if k == 5:
        if existence:
            return v%20 == 1 or v%20 == 5
        return BalancedIncompleteBlockDesign(v, v_5_1_BIBD(v), copy=False)

    from difference_family import difference_family
    from database import BIBD_constructions

    if (v,k,1) in BIBD_constructions:
        if existence:
            return True
        return BlockDesign(v,BIBD_constructions[(v,k,1)](), copy=False)
    if BIBD_from_arc_in_desarguesian_projective_plane(v,k,existence=True):
        if existence:
            return True
        B = BIBD_from_arc_in_desarguesian_projective_plane(v,k)
        return BalancedIncompleteBlockDesign(v, B, copy=False)
    if BIBD_from_TD(v,k,existence=True):
        if existence:
            return True
        return BalancedIncompleteBlockDesign(v, BIBD_from_TD(v,k), copy=False)
    if v == (k-1)**2+k and is_prime_power(k-1):
        if existence:
            return True
        from block_design import projective_plane
        return BalancedIncompleteBlockDesign(v, projective_plane(k-1),copy=False)
    if difference_family(v,k,existence=True):
        if existence:
            return True
        G,D = difference_family(v,k)
        return BalancedIncompleteBlockDesign(v, BIBD_from_difference_family(G,D,check=False), copy=False)
    if use_LJCR:
        from covering_design import best_known_covering_design_www
        B = best_known_covering_design_www(v,k,2)

        # Is it a BIBD or just a good covering ?
        expected_n_of_blocks = binomial(v,2)/binomial(k,2)
        if B.low_bd() > expected_n_of_blocks:
            if existence:
                return False
            raise EmptySetError("There exists no ({},{},{})-BIBD".format(v,k,lmbd))
        B = B.incidence_structure()
        if B.num_blocks() == expected_n_of_blocks:
            if existence:
                return True
            else:
                return B

    if existence:
        return Unknown
    else:
        raise NotImplementedError("I don't know how to build a ({},{},1)-BIBD!".format(v,k))
Exemple #2
0
def orthogonal_array(k,n,t=2,check=True,existence=False):
    r"""
    Return an orthogonal array of parameters `k,n,t`.

    An orthogonal array of parameters `k,n,t` is a matrix with `k` columns
    filled with integers from `[n]` in such a way that for any `t` columns, each
    of the `n^t` possible rows occurs exactly once. In
    particular, the matrix has `n^t` rows.

    More general definitions sometimes involve a `\lambda` parameter, and we
    assume here that `\lambda=1`.

    For more information on orthogonal arrays, see
    :wikipedia:`Orthogonal_array`.

    INPUT:

    - ``k`` -- (integer) number of columns. If ``k=None`` it is set to the
      largest value available.

    - ``n`` -- (integer) number of symbols

    - ``t`` -- (integer; default: 2) -- strength of the array

    - ``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.

    - ``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.

      .. NOTE::

          When ``k=None`` and ``existence=True`` the function returns an
          integer, i.e. the largest `k` such that we can build a `TD(k,n)`.

    OUTPUT:

    The kind of output depends on the input:

    - if ``existence=False`` (the default) then the output is a list of lists
      that represent an orthogonal array with parameters ``k`` and ``n``

    - if ``existence=True`` and ``k`` is an integer, then the function returns a
      troolean: either ``True``, ``Unknown`` or ``False``

    - if ``existence=True`` and ``k=None`` then the output is the largest value
      of ``k`` for which Sage knows how to compute a `TD(k,n)`.

    .. NOTE::

        This method implements theorems from [Stinson2004]_. See the code's
        documentation for details.

    .. SEEALSO::

        When `t=2` an orthogonal array is also a transversal design (see
        :func:`transversal_design`) and a family of mutually orthogonal latin
        squares (see
        :func:`~sage.combinat.designs.latin_squares.mutually_orthogonal_latin_squares`).

    EXAMPLES::

        sage: designs.orthogonal_array(3,2)
        [[0, 0, 0], [0, 1, 1], [1, 0, 1], [1, 1, 0]]

        sage: designs.orthogonal_array(5,5)
        [[0, 0, 0, 0, 0], [0, 1, 2, 3, 4], [0, 2, 4, 1, 3],
         [0, 3, 1, 4, 2], [0, 4, 3, 2, 1], [1, 0, 4, 3, 2],
         [1, 1, 1, 1, 1], [1, 2, 3, 4, 0], [1, 3, 0, 2, 4],
         [1, 4, 2, 0, 3], [2, 0, 3, 1, 4], [2, 1, 0, 4, 3],
         [2, 2, 2, 2, 2], [2, 3, 4, 0, 1], [2, 4, 1, 3, 0],
         [3, 0, 2, 4, 1], [3, 1, 4, 2, 0], [3, 2, 1, 0, 4],
         [3, 3, 3, 3, 3], [3, 4, 0, 1, 2], [4, 0, 1, 2, 3],
         [4, 1, 3, 0, 2], [4, 2, 0, 3, 1], [4, 3, 2, 1, 0],
         [4, 4, 4, 4, 4]]

    What is the largest value of `k` for which Sage knows how to compute a
    `OA(k,14,2)`?::

        sage: designs.orthogonal_array(None,14,existence=True)
        6

    If you ask for an orthogonal array that does not exist, then the function
    either raise an ``EmptySetError`` (if it knows that such an orthogonal array
    does not exist) or a ``NotImplementedError``::

        sage: designs.orthogonal_array(4,2)
        Traceback (most recent call last):
        ...
        EmptySetError: No Orthogonal Array exists when k>=n+t except when n<=1
        sage: designs.orthogonal_array(12,20)
        Traceback (most recent call last):
        ...
        NotImplementedError: I don't know how to build an OA(12,20)!

    Note that these errors correspond respectively to the answers ``False`` and
    ``Unknown`` when the parameter ``existence`` is set to ``True``::

        sage: designs.orthogonal_array(4,2,existence=True)
        False
        sage: designs.orthogonal_array(12,20,existence=True)
        Unknown

    TESTS:

    The special cases `n=0,1`::

        sage: designs.orthogonal_array(3,0)
        []
        sage: designs.orthogonal_array(3,1)
        [[0, 0, 0]]
        sage: designs.orthogonal_array(None,0,existence=True)
        +Infinity
        sage: designs.orthogonal_array(None,1,existence=True)
        +Infinity
        sage: designs.orthogonal_array(None,1)
        Traceback (most recent call last):
        ...
        ValueError: there is no upper bound on k when 0<=n<=1
        sage: designs.orthogonal_array(None,0)
        Traceback (most recent call last):
        ...
        ValueError: there is no upper bound on k when 0<=n<=1
        sage: designs.orthogonal_array(16,0)
        []
        sage: designs.orthogonal_array(16,1)
        [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

    when `t>2` and `k=None`::

        sage: t = 3
        sage: designs.orthogonal_array(None,5,t=t,existence=True) == t
        True
        sage: _ = designs.orthogonal_array(t,5,t)
    """

    from latin_squares import mutually_orthogonal_latin_squares
    from database import OA_constructions, MOLS_constructions
    from block_design import projective_plane, projective_plane_to_OA
    from orthogonal_arrays_recursive import find_recursive_construction

    assert n>=0

    # If k is set to None we find the largest value available
    if k is None:
        if n == 0 or n == 1:
            if existence:
                from sage.rings.infinity import Infinity
                return Infinity
            raise ValueError("there is no upper bound on k when 0<=n<=1")
        elif t == 2 and projective_plane(n,existence=True):
            k = n+1
        else:
            for k in range(t-1,n+2):
                if not orthogonal_array(k+1,n,t=t,existence=True):
                    break
        if existence:
            return k

    if k < t:
        raise ValueError("undefined for k<t")

    if existence and _OA_cache_get(k,n) is not None and t == 2:
        return _OA_cache_get(k,n)

    may_be_available = _OA_cache_construction_available(k,n) is not False

    if n <= 1:
        if existence:
            return True
        OA = [[0]*k]*n

    elif k >= n+t:
        # When t=2 then k<n+t as it is equivalent to the existence of n-1 MOLS.
        # When t>2 the submatrix defined by the rows whose first t-2 elements
        # are 0s yields a OA with t=2 and k-(t-2) columns. Thus k-(t-2) < n+2,
        # i.e. k<n+t.
        if existence:
            return False
        raise EmptySetError("No Orthogonal Array exists when k>=n+t except when n<=1")

    elif k <= t:
        if existence:
            return True

        from itertools import product
        return map(list, product(range(n), repeat=k))

    elif t != 2:
        if existence:
            return Unknown
        raise NotImplementedError("Only trivial orthogonal arrays are implemented for t>=2")

    elif k <= 3:
        if existence:
            return True
        return [[i,j,(i+j)%n] for i in xrange(n) for j in xrange(n)]

    # projective spaces are equivalent to OA(n+1,n,2)
    elif (projective_plane(n, existence=True) or
           (k == n+1 and projective_plane(n, existence=True) is False)):
        _OA_cache_set(n+1,n,projective_plane(n, existence=True))
        if k == n+1:
            if existence:
                return projective_plane(n, existence=True)
            p = projective_plane(n, check=False)
            OA = projective_plane_to_OA(p, check=False)
        else:
            if existence:
                return True
            p = projective_plane(n, check=False)
            OA = [l[:k] for l in projective_plane_to_OA(p, check=False)]

    # Constructions from the database
    elif may_be_available and n in OA_constructions and k <= OA_constructions[n][0]:
        _OA_cache_set(OA_constructions[n][0],n,True)
        if existence:
            return True
        _, construction = OA_constructions[n]

        OA = OA_from_wider_OA(construction(),k)

    # Constructions from the database II
    elif may_be_available and k <= 6 and n == 12:
        _OA_cache_set(6,12,True)

        if existence:
            return True
        else:
            from database import TD_6_12
            TD = TD_6_12()
            OA = [[x%n for x in R] for R in TD]

    # Constructions from the database III
    # Section 6.5.1 from [Stinson2004]
    elif may_be_available and n in MOLS_constructions and k-2 <= MOLS_constructions[n][0]:
        _OA_cache_set(MOLS_constructions[n][0]+2,n,True)

        if existence:
            return True
        else:
            construction = MOLS_constructions[n][1]
            mols = construction()
            OA = [[i,j]+[m[i,j] for m in mols]
                  for i in range(n) for j in range(n)]
            OA = OA_from_wider_OA(OA,k)

    elif may_be_available and find_recursive_construction(k,n):
        _OA_cache_set(k,n,True)
        if existence:
            return True
        f,args = find_recursive_construction(k,n)
        OA = f(*args)

    else:
        _OA_cache_set(k,n,Unknown)
        if existence:
            return Unknown
        raise NotImplementedError("I don't know how to build an OA({},{})!".format(k,n))

    if check:
        assert is_orthogonal_array(OA,k,n,t)

    return OA
Exemple #3
0
def orthogonal_array(k,
                     n,
                     t=2,
                     check=True,
                     existence=False,
                     who_asked=tuple()):
    r"""
    Return an orthogonal array of parameters `k,n,t`.

    An orthogonal array of parameters `k,n,t` is a matrix with `k` columns
    filled with integers from `[n]` in such a way that for any `t` columns, each
    of the `n^t` possible rows occurs exactly once. In
    particular, the matrix has `n^t` rows.

    More general definitions sometimes involve a `\lambda` parameter, and we
    assume here that `\lambda=1`.

    For more information on orthogonal arrays, see
    :wikipedia:`Orthogonal_array`.

    INPUT:

    - ``k`` -- (integer) number of columns. If ``k=None`` it is set to the
      largest value available.

    - ``n`` -- (integer) number of symbols

    - ``t`` -- (integer; default: 2) -- strength of the array

    - ``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.

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

        - ``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.

      .. NOTE::

          When ``k=None`` and ``existence=True`` the function returns an
          integer, i.e. the largest `k` such that we can build a `TD(k,n)`.

    - ``who_asked`` (internal use only) -- because of the equivalence between
      OA/TD/MOLS, each of the three constructors calls the others. We must keep
      track of who calls who in order to avoid infinite loops. ``who_asked`` is
      the tuple of the other functions that were called before this one on the
      same input `k,n`.

    OUTPUT:

    The kind of output depends on the input:

    - if ``existence=False`` (the default) then the output is a list of lists
      that represent an orthogonal array with parameters ``k`` and ``n``

    - if ``existence=True`` and ``k`` is an integer, then the function returns a
      troolean: either ``True``, ``Unknown`` or ``False``

    - if ``existence=True`` and ``k=None`` then the output is the largest value
      of ``k`` for which Sage knows how to compute a `TD(k,n)`.

    .. NOTE::

        This method implements theorems from [Stinson2004]_. See the code's
        documentation for details.

    .. SEEALSO::

        When `t=2` an orthogonal array is also a transversal design (see
        :func:`transversal_design`) and a family of mutually orthogonal latin
        squares (see
        :func:`~sage.combinat.designs.latin_squares.mutually_orthogonal_latin_squares`).

    EXAMPLES::

        sage: designs.orthogonal_array(3,2)
        [[0, 0, 0], [0, 1, 1], [1, 0, 1], [1, 1, 0]]

        sage: designs.orthogonal_array(5,5)
        [[0, 0, 0, 0, 0], [0, 1, 2, 3, 4], [0, 2, 4, 1, 3],
         [0, 3, 1, 4, 2], [0, 4, 3, 2, 1], [1, 0, 4, 3, 2],
         [1, 1, 1, 1, 1], [1, 2, 3, 4, 0], [1, 3, 0, 2, 4],
         [1, 4, 2, 0, 3], [2, 0, 3, 1, 4], [2, 1, 0, 4, 3],
         [2, 2, 2, 2, 2], [2, 3, 4, 0, 1], [2, 4, 1, 3, 0],
         [3, 0, 2, 4, 1], [3, 1, 4, 2, 0], [3, 2, 1, 0, 4],
         [3, 3, 3, 3, 3], [3, 4, 0, 1, 2], [4, 0, 1, 2, 3],
         [4, 1, 3, 0, 2], [4, 2, 0, 3, 1], [4, 3, 2, 1, 0],
         [4, 4, 4, 4, 4]]

    What is the largest value of `k` for which Sage knows how to compute a
    `OA(k,14,2)`?::

        sage: designs.orthogonal_array(None,14,existence=True)
        6

    If you ask for an orthogonal array that does not exist, then the function
    either raise an `EmptySetError` (if it knows that such an orthogonal array
    does not exist) or a `NotImplementedError`::

        sage: designs.orthogonal_array(4,2)
        Traceback (most recent call last):
        ...
        EmptySetError: No Orthogonal Array exists when k>=n+t except when n=1
        sage: designs.orthogonal_array(12,20)
        Traceback (most recent call last):
        ...
        NotImplementedError: I don't know how to build this orthogonal array!

    Note that these errors correspond respectively to the answers ``False`` and
    ``Unknown`` when the parameter ``existence`` is set to ``True``::

        sage: designs.orthogonal_array(4,2,existence=True)
        False
        sage: designs.orthogonal_array(12,20,existence=True)
        Unknown

    TESTS:

    The special case `n=1`::

        sage: designs.orthogonal_array(3,1)
        [[0, 0, 0]]
        sage: designs.orthogonal_array(None,1,existence=True)
        +Infinity
        sage: designs.orthogonal_array(None,1)
        Traceback (most recent call last):
        ...
        ValueError: there are no bound on k when n=1.
        sage: designs.orthogonal_array(None,14,existence=True)
        6
        sage: designs.orthogonal_array(16,1)
        [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
    """

    from sage.rings.finite_rings.constructor import FiniteField
    from latin_squares import mutually_orthogonal_latin_squares
    from database import OA_constructions
    from block_design import projective_plane, projective_plane_to_OA

    # If k is set to None we find the largest value available
    if k is None:
        if n == 1:
            if existence:
                from sage.rings.infinity import Infinity
                return Infinity
            raise ValueError("there are no bound on k when n=1.")

        for k in range(1, n + 2):
            if not orthogonal_array(k + 1, n, existence=True):
                break
        if existence:
            return k

    if k < 2:
        raise ValueError("undefined for k less than 2")

    if n == 1:
        OA = [[0] * k]

    elif k >= n + t:
        # When t=2 then k<n+t as it is equivalent to the existence of n-1 MOLS.
        # When t>2 the submatrix defined by the rows whose first t-2 elements
        # are 0s yields a OA with t=2 and k-(t-2) columns. Thus k-(t-2) < n+2,
        # i.e. k<n+t.
        if existence:
            return False
        raise EmptySetError(
            "No Orthogonal Array exists when k>=n+t except when n=1")

    elif k == t:
        if existence:
            return True

        from itertools import product
        OA = map(list, product(range(n), repeat=k))

    elif n in OA_constructions and k <= OA_constructions[n][0]:
        if existence:
            return True
        _, construction = OA_constructions[n]

        OA = OA_from_wider_OA(construction(), k)

    # projective spaces are equivalent to OA(n+1,n,2)
    elif (t == 2
          and (projective_plane(n, existence=True) or
               (k == n + 1 and projective_plane(n, existence=True) is False))):
        if k == n + 1:
            if existence:
                return projective_plane(n, existence=True)
            p = projective_plane(n, check=False)
            OA = projective_plane_to_OA(p)
        else:
            if existence:
                return True
            p = projective_plane(n, check=False)
            OA = [l[:k] for l in projective_plane_to_OA(p)]

    # Constructions from the database
    elif n in OA_constructions and k <= OA_constructions[n][0]:
        if existence:
            return True
        _, construction = OA_constructions[n]

        OA = OA_from_wider_OA(construction(), k)

    elif (t == 2 and transversal_design not in who_asked
          and transversal_design(
              k, n, existence=True, who_asked=who_asked +
              (orthogonal_array, )) is not Unknown):

        # forward existence
        if transversal_design(k,
                              n,
                              existence=True,
                              who_asked=who_asked + (orthogonal_array, )):
            if existence:
                return True
            else:
                TD = transversal_design(k,
                                        n,
                                        check=False,
                                        who_asked=who_asked +
                                        (orthogonal_array, ))
                OA = [[x % n for x in R] for R in TD]

        # forward non-existence
        else:
            if existence:
                return False
            raise EmptySetError("There exists no OA" + str((k, n)) + "!")

    # Section 6.5.1 from [Stinson2004]
    elif (t == 2 and mutually_orthogonal_latin_squares not in who_asked
          and mutually_orthogonal_latin_squares(
              n,
              k - 2,
              existence=True,
              who_asked=who_asked + (orthogonal_array, )) is not Unknown):

        # forward existence
        if mutually_orthogonal_latin_squares(n,
                                             k - 2,
                                             existence=True,
                                             who_asked=who_asked +
                                             (orthogonal_array, )):
            if existence:
                return True
            else:
                mols = mutually_orthogonal_latin_squares(n,
                                                         k - 2,
                                                         who_asked=who_asked +
                                                         (orthogonal_array, ))
                OA = [[i, j] + [m[i, j] for m in mols] for i in range(n)
                      for j in range(n)]
        # forward non-existence
        else:
            if existence:
                return False
            raise EmptySetError("There exists no OA" + str((k, n)) + "!")

    else:
        if existence:
            return Unknown
        raise NotImplementedError(
            "I don't know how to build this orthogonal array!")

    if check:
        assert is_orthogonal_array(OA, k, n, t)

    return OA
Exemple #4
0
def v_4_1_BIBD(v, check=True):
    r"""
    Return a `(v,4,1)`-BIBD.

    A `(v,4,1)`-BIBD is an edge-decomposition of the complete graph `K_v` into
    copies of `K_4`. For more information, see
    :func:`balanced_incomplete_block_design`. It exists if and only if `v\equiv 1,4
    \pmod {12}`.

    See page 167 of [Stinson2004]_ for the construction details.

    .. SEEALSO::

        * :func:`balanced_incomplete_block_design`

    INPUT:

    - ``v`` (integer) -- number of points.

    - ``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.

    EXAMPLES::

        sage: from sage.combinat.designs.bibd import v_4_1_BIBD  # long time
        sage: for n in range(13,100):                            # long time
        ....:    if n%12 in [1,4]:                               # long time
        ....:       _ = v_4_1_BIBD(n, check = True)              # long time

    TESTS:

    Check that the `(25,4)` and `(37,4)`-difference family are available::

        sage: assert designs.difference_family(25,4,existence=True)
        sage: _ = designs.difference_family(25,4)
        sage: assert designs.difference_family(37,4,existence=True)
        sage: _ = designs.difference_family(37,4)

    Check some larger `(v,4,1)`-BIBD (see :trac:`17557`)::

        sage: for v in range(400):                                      # long time
        ....:     if v%12 in [1,4]:                                     # long time
        ....:         _ = designs.balanced_incomplete_block_design(v,4) # long time
    """
    k = 4
    if v == 0:
        return []
    if v <= 12 or v%12 not in [1,4]:
        raise EmptySetError("A K_4-decomposition of K_v exists iif v=2,4 mod 12, v>12 or v==0")

    # Step 1. Base cases.
    if v == 13:
        # note: this construction can also be obtained from difference_family
        from block_design import projective_plane
        return projective_plane(3)._blocks
    if v == 16:
        from block_design import AffineGeometryDesign
        from sage.rings.finite_rings.constructor import FiniteField
        return AffineGeometryDesign(2,1,FiniteField(4,'x'))._blocks
    if v == 25 or v == 37:
        from difference_family import difference_family
        G,D = difference_family(v,4)
        return BIBD_from_difference_family(G,D,check=False)
    if v == 28:
        return [[0, 1, 23, 26], [0, 2, 10, 11], [0, 3, 16, 18], [0, 4, 15, 20],
                [0, 5, 8, 9], [0, 6, 22, 25], [0, 7, 14, 21], [0, 12, 17, 27],
                [0, 13, 19, 24], [1, 2, 24, 27], [1, 3, 11, 12], [1, 4, 17, 19],
                [1, 5, 14, 16], [1, 6, 9, 10], [1, 7, 20, 25], [1, 8, 15, 22],
                [1, 13, 18, 21], [2, 3, 21, 25], [2, 4, 12, 13], [2, 5, 18, 20],
                [2, 6, 15, 17], [2, 7, 19, 22], [2, 8, 14, 26], [2, 9, 16, 23],
                [3, 4, 22, 26], [3, 5, 7, 13], [3, 6, 14, 19], [3, 8, 20, 23],
                [3, 9, 15, 27], [3, 10, 17, 24], [4, 5, 23, 27], [4, 6, 7, 8],
                [4, 9, 14, 24], [4, 10, 16, 21], [4, 11, 18, 25], [5, 6, 21, 24],
                [5, 10, 15, 25], [5, 11, 17, 22], [5, 12, 19, 26], [6, 11, 16, 26],
                [6, 12, 18, 23], [6, 13, 20, 27], [7, 9, 17, 18], [7, 10, 26, 27],
                [7, 11, 23, 24], [7, 12, 15, 16], [8, 10, 18, 19], [8, 11, 21, 27],
                [8, 12, 24, 25], [8, 13, 16, 17], [9, 11, 19, 20], [9, 12, 21, 22],
                [9, 13, 25, 26], [10, 12, 14, 20], [10, 13, 22, 23], [11, 13, 14, 15],
                [14, 17, 23, 25], [14, 18, 22, 27], [15, 18, 24, 26], [15, 19, 21, 23],
                [16, 19, 25, 27], [16, 20, 22, 24], [17, 20, 21, 26]]

    # Step 2 : this is function PBD_4_5_8_9_12
    PBD = PBD_4_5_8_9_12((v-1)/(k-1),check=False)

    # Step 3 : Theorem 7.20
    bibd = BIBD_from_PBD(PBD,v,k,check=False)

    if check:
        assert is_pairwise_balanced_design(bibd,v,[k])

    return bibd
Exemple #5
0
def balanced_incomplete_block_design(v, k, existence=False, use_LJCR=False):
    r"""
    Return a BIBD of parameters `v,k`.

    A Balanced Incomplete Block Design of parameters `v,k` is a collection
    `\mathcal C` of `k`-subsets of `V=\{0,\dots,v-1\}` such that for any two
    distinct elements `x,y\in V` there is a unique element `S\in \mathcal C`
    such that `x,y\in S`.

    More general definitions sometimes involve a `\lambda` parameter, and we
    assume here that `\lambda=1`.

    For more information on BIBD, see the
    :wikipedia:`corresponding Wikipedia entry <Block_design#Definition_of_a_BIBD_.28or_2-design.29>`.

    INPUT:

    - ``v,k`` (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.

    - ``use_LJCR`` (boolean) -- whether to query the La Jolla Covering
      Repository for the design when Sage does not know how to build it (see
      :func:`~sage.combinat.designs.covering_design.best_known_covering_design_www`). This
      requires internet.

    .. SEEALSO::

        * :func:`steiner_triple_system`
        * :func:`v_4_1_BIBD`
        * :func:`v_5_1_BIBD`

    TODO:

        * Implement other constructions from the Handbook of Combinatorial
          Designs.

    EXAMPLES::

        sage: designs.balanced_incomplete_block_design(7, 3).blocks()
        [[0, 1, 3], [0, 2, 4], [0, 5, 6], [1, 2, 6], [1, 4, 5], [2, 3, 5], [3, 4, 6]]
        sage: B = designs.balanced_incomplete_block_design(66, 6, use_LJCR=True) # optional - internet
        sage: B                                                              # optional - internet
        Incidence structure with 66 points and 143 blocks
        sage: B.blocks()                                                     # optional - internet
        [[0, 1, 2, 3, 4, 65], [0, 5, 24, 25, 39, 57], [0, 6, 27, 38, 44, 55], ...
        sage: designs.balanced_incomplete_block_design(66, 6, use_LJCR=True)  # optional - internet
        Incidence structure with 66 points and 143 blocks
        sage: designs.balanced_incomplete_block_design(141, 6)
        Traceback (most recent call last):
        ...
        NotImplementedError: I don't know how to build a (141,6,1)-BIBD!

    TESTS::

        sage: designs.balanced_incomplete_block_design(85,5,existence=True)
        True
        sage: _ = designs.balanced_incomplete_block_design(85,5)

    A BIBD from a Finite Projective Plane::

        sage: _ = designs.balanced_incomplete_block_design(21,5)

    Some trivial BIBD::

        sage: designs.balanced_incomplete_block_design(10,10)
        (10,10,1)-Balanced Incomplete Block Design
        sage: designs.balanced_incomplete_block_design(1,10)
        (1,0,1)-Balanced Incomplete Block Design

    Existence of BIBD with `k=3,4,5`::

        sage: [v for v in xrange(50) if designs.balanced_incomplete_block_design(v,3,existence=True)]
        [1, 3, 7, 9, 13, 15, 19, 21, 25, 27, 31, 33, 37, 39, 43, 45, 49]
        sage: [v for v in xrange(100) if designs.balanced_incomplete_block_design(v,4,existence=True)]
        [1, 4, 13, 16, 25, 28, 37, 40, 49, 52, 61, 64, 73, 76, 85, 88, 97]
        sage: [v for v in xrange(150) if designs.balanced_incomplete_block_design(v,5,existence=True)]
        [1, 5, 21, 25, 41, 45, 61, 65, 81, 85, 101, 105, 121, 125, 141, 145]

    For `k > 5` there are currently very few constructions::

        sage: [v for v in xrange(150) if designs.balanced_incomplete_block_design(v,6,existence=True) is True]
        [1, 6, 31, 91, 121]
        sage: [v for v in xrange(150) if designs.balanced_incomplete_block_design(v,6,existence=True) is Unknown]
        [51, 61, 66, 76, 81, 96, 106, 111, 126, 136, 141]

    Here are some constructions with `k \geq 7` and `v` a prime power::

        sage: designs.balanced_incomplete_block_design(169,7)
        (169,7,1)-Balanced Incomplete Block Design
        sage: designs.balanced_incomplete_block_design(617,8)
        (617,8,1)-Balanced Incomplete Block Design
        sage: designs.balanced_incomplete_block_design(433,9)
        (433,9,1)-Balanced Incomplete Block Design
        sage: designs.balanced_incomplete_block_design(1171,10)
        (1171,10,1)-Balanced Incomplete Block Design

    And we know some inexistence results::

        sage: designs.balanced_incomplete_block_design(21,6,existence=True)
        False
    """
    lmbd = 1

    # Trivial BIBD
    if v == 1:
        if existence:
            return True
        return BalancedIncompleteBlockDesign(v, [], check=False)

    if k == v:
        if existence:
            return True
        return BalancedIncompleteBlockDesign(v, [range(v)],
                                             check=False,
                                             copy=False)

    # Non-existence of BIBD
    if (v < k or k < 2 or (v - 1) % (k - 1) != 0
            or (v * (v - 1)) % (k * (k - 1)) != 0 or
            # From the Handbook of combinatorial designs:
            #
            # With lambda>1 other exceptions are
            # (15,5,2),(21,6,2),(22,7,2),(22,8,4).
        (k == 6 and v in [36, 46]) or (k == 7 and v == 43) or
            # Fisher's inequality
        (v * (v - 1)) / (k * (k - 1)) < v):
        if existence:
            return False
        raise EmptySetError("There exists no ({},{},{})-BIBD".format(
            v, k, lmbd))

    if k == 2:
        if existence:
            return True
        from itertools import combinations
        return BalancedIncompleteBlockDesign(v,
                                             combinations(range(v), 2),
                                             check=False,
                                             copy=True)
    if k == 3:
        if existence:
            return v % 6 == 1 or v % 6 == 3
        return steiner_triple_system(v)
    if k == 4:
        if existence:
            return v % 12 == 1 or v % 12 == 4
        return BalancedIncompleteBlockDesign(v, v_4_1_BIBD(v), copy=False)
    if k == 5:
        if existence:
            return v % 20 == 1 or v % 20 == 5
        return BalancedIncompleteBlockDesign(v, v_5_1_BIBD(v), copy=False)

    from difference_family import difference_family
    from database import BIBD_constructions

    if (v, k, 1) in BIBD_constructions:
        if existence:
            return True
        return BlockDesign(v, BIBD_constructions[(v, k, 1)](), copy=False)
    if BIBD_from_TD(v, k, existence=True):
        if existence:
            return True
        return BalancedIncompleteBlockDesign(v, BIBD_from_TD(v, k), copy=False)
    if v == (k - 1)**2 + k and is_prime_power(k - 1):
        if existence:
            return True
        from block_design import projective_plane
        return BalancedIncompleteBlockDesign(v,
                                             projective_plane(k - 1),
                                             copy=False)
    if difference_family(v, k, existence=True):
        if existence:
            return True
        G, D = difference_family(v, k)
        return BalancedIncompleteBlockDesign(v,
                                             BIBD_from_difference_family(
                                                 G, D, check=False),
                                             copy=False)
    if use_LJCR:
        from covering_design import best_known_covering_design_www
        B = best_known_covering_design_www(v, k, 2)

        # Is it a BIBD or just a good covering ?
        expected_n_of_blocks = binomial(v, 2) / binomial(k, 2)
        if B.low_bd() > expected_n_of_blocks:
            if existence:
                return False
            raise EmptySetError("There exists no ({},{},{})-BIBD".format(
                v, k, lmbd))
        B = B.incidence_structure()
        if B.num_blocks() == expected_n_of_blocks:
            if existence:
                return True
            else:
                return B

    if existence:
        return Unknown
    else:
        raise NotImplementedError(
            "I don't know how to build a ({},{},1)-BIBD!".format(v, k))
def orthogonal_array(k, n, t=2, check=True, existence=False):
    r"""
    Return an orthogonal array of parameters `k,n,t`.

    An orthogonal array of parameters `k,n,t` is a matrix with `k` columns
    filled with integers from `[n]` in such a way that for any `t` columns, each
    of the `n^t` possible rows occurs exactly once. In
    particular, the matrix has `n^t` rows.

    More general definitions sometimes involve a `\lambda` parameter, and we
    assume here that `\lambda=1`.

    For more information on orthogonal arrays, see
    :wikipedia:`Orthogonal_array`.

    INPUT:

    - ``k`` -- (integer) number of columns. If ``k=None`` it is set to the
      largest value available.

    - ``n`` -- (integer) number of symbols

    - ``t`` -- (integer; default: 2) -- strength of the array

    - ``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.

    - ``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.

      .. NOTE::

          When ``k=None`` and ``existence=True`` the function returns an
          integer, i.e. the largest `k` such that we can build a `TD(k,n)`.

    OUTPUT:

    The kind of output depends on the input:

    - if ``existence=False`` (the default) then the output is a list of lists
      that represent an orthogonal array with parameters ``k`` and ``n``

    - if ``existence=True`` and ``k`` is an integer, then the function returns a
      troolean: either ``True``, ``Unknown`` or ``False``

    - if ``existence=True`` and ``k=None`` then the output is the largest value
      of ``k`` for which Sage knows how to compute a `TD(k,n)`.

    .. NOTE::

        This method implements theorems from [Stinson2004]_. See the code's
        documentation for details.

    .. SEEALSO::

        When `t=2` an orthogonal array is also a transversal design (see
        :func:`transversal_design`) and a family of mutually orthogonal latin
        squares (see
        :func:`~sage.combinat.designs.latin_squares.mutually_orthogonal_latin_squares`).

    EXAMPLES::

        sage: designs.orthogonal_array(3,2)
        [[0, 0, 0], [0, 1, 1], [1, 0, 1], [1, 1, 0]]

        sage: designs.orthogonal_array(5,5)
        [[0, 0, 0, 0, 0], [0, 1, 2, 3, 4], [0, 2, 4, 1, 3],
         [0, 3, 1, 4, 2], [0, 4, 3, 2, 1], [1, 0, 4, 3, 2],
         [1, 1, 1, 1, 1], [1, 2, 3, 4, 0], [1, 3, 0, 2, 4],
         [1, 4, 2, 0, 3], [2, 0, 3, 1, 4], [2, 1, 0, 4, 3],
         [2, 2, 2, 2, 2], [2, 3, 4, 0, 1], [2, 4, 1, 3, 0],
         [3, 0, 2, 4, 1], [3, 1, 4, 2, 0], [3, 2, 1, 0, 4],
         [3, 3, 3, 3, 3], [3, 4, 0, 1, 2], [4, 0, 1, 2, 3],
         [4, 1, 3, 0, 2], [4, 2, 0, 3, 1], [4, 3, 2, 1, 0],
         [4, 4, 4, 4, 4]]

    What is the largest value of `k` for which Sage knows how to compute a
    `OA(k,14,2)`?::

        sage: designs.orthogonal_array(None,14,existence=True)
        6

    If you ask for an orthogonal array that does not exist, then the function
    either raise an ``EmptySetError`` (if it knows that such an orthogonal array
    does not exist) or a ``NotImplementedError``::

        sage: designs.orthogonal_array(4,2)
        Traceback (most recent call last):
        ...
        EmptySetError: No Orthogonal Array exists when k>=n+t except when n<=1
        sage: designs.orthogonal_array(12,20)
        Traceback (most recent call last):
        ...
        NotImplementedError: I don't know how to build an OA(12,20)!

    Note that these errors correspond respectively to the answers ``False`` and
    ``Unknown`` when the parameter ``existence`` is set to ``True``::

        sage: designs.orthogonal_array(4,2,existence=True)
        False
        sage: designs.orthogonal_array(12,20,existence=True)
        Unknown

    TESTS:

    The special cases `n=0,1`::

        sage: designs.orthogonal_array(3,0)
        []
        sage: designs.orthogonal_array(3,1)
        [[0, 0, 0]]
        sage: designs.orthogonal_array(None,0,existence=True)
        +Infinity
        sage: designs.orthogonal_array(None,1,existence=True)
        +Infinity
        sage: designs.orthogonal_array(None,1)
        Traceback (most recent call last):
        ...
        ValueError: there is no upper bound on k when 0<=n<=1
        sage: designs.orthogonal_array(None,0)
        Traceback (most recent call last):
        ...
        ValueError: there is no upper bound on k when 0<=n<=1
        sage: designs.orthogonal_array(16,0)
        []
        sage: designs.orthogonal_array(16,1)
        [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

    when `t>2` and `k=None`::

        sage: t = 3
        sage: designs.orthogonal_array(None,5,t=t,existence=True) == t
        True
        sage: _ = designs.orthogonal_array(t,5,t)
    """

    from latin_squares import mutually_orthogonal_latin_squares
    from database import OA_constructions, MOLS_constructions
    from block_design import projective_plane, projective_plane_to_OA
    from orthogonal_arrays_recursive import find_recursive_construction

    assert n >= 0

    # If k is set to None we find the largest value available
    if k is None:
        if n == 0 or n == 1:
            if existence:
                from sage.rings.infinity import Infinity
                return Infinity
            raise ValueError("there is no upper bound on k when 0<=n<=1")
        elif t == 2 and projective_plane(n, existence=True):
            k = n + 1
        else:
            for k in range(t - 1, n + 2):
                if not orthogonal_array(k + 1, n, t=t, existence=True):
                    break
        if existence:
            return k

    if k < t:
        raise ValueError("undefined for k<t")

    if existence and _OA_cache_get(k, n) is not None and t == 2:
        return _OA_cache_get(k, n)

    may_be_available = _OA_cache_construction_available(k, n) is not False

    if n <= 1:
        if existence:
            return True
        OA = [[0] * k] * n

    elif k >= n + t:
        # When t=2 then k<n+t as it is equivalent to the existence of n-1 MOLS.
        # When t>2 the submatrix defined by the rows whose first t-2 elements
        # are 0s yields a OA with t=2 and k-(t-2) columns. Thus k-(t-2) < n+2,
        # i.e. k<n+t.
        if existence:
            return False
        raise EmptySetError(
            "No Orthogonal Array exists when k>=n+t except when n<=1")

    elif k <= t:
        if existence:
            return True

        from itertools import product
        return map(list, product(range(n), repeat=k))

    elif t != 2:
        if existence:
            return Unknown
        raise NotImplementedError(
            "Only trivial orthogonal arrays are implemented for t>=2")

    elif k <= 3:
        if existence:
            return True
        return [[i, j, (i + j) % n] for i in xrange(n) for j in xrange(n)]

    # projective spaces are equivalent to OA(n+1,n,2)
    elif (projective_plane(n, existence=True)
          or (k == n + 1 and projective_plane(n, existence=True) is False)):
        _OA_cache_set(n + 1, n, projective_plane(n, existence=True))
        if k == n + 1:
            if existence:
                return projective_plane(n, existence=True)
            p = projective_plane(n, check=False)
            OA = projective_plane_to_OA(p, check=False)
        else:
            if existence:
                return True
            p = projective_plane(n, check=False)
            OA = [l[:k] for l in projective_plane_to_OA(p, check=False)]

    # Constructions from the database
    elif may_be_available and n in OA_constructions and k <= OA_constructions[
            n][0]:
        _OA_cache_set(OA_constructions[n][0], n, True)
        if existence:
            return True
        _, construction = OA_constructions[n]

        OA = OA_from_wider_OA(construction(), k)

    # Constructions from the database II
    elif may_be_available and k <= 6 and n == 12:
        _OA_cache_set(6, 12, True)

        if existence:
            return True
        else:
            from database import TD_6_12
            TD = TD_6_12()
            OA = [[x % n for x in R] for R in TD]

    # Constructions from the database III
    # Section 6.5.1 from [Stinson2004]
    elif may_be_available and n in MOLS_constructions and k - 2 <= MOLS_constructions[
            n][0]:
        _OA_cache_set(MOLS_constructions[n][0] + 2, n, True)

        if existence:
            return True
        else:
            construction = MOLS_constructions[n][1]
            mols = construction()
            OA = [[i, j] + [m[i, j] for m in mols] for i in range(n)
                  for j in range(n)]
            OA = OA_from_wider_OA(OA, k)

    elif may_be_available and find_recursive_construction(k, n):
        _OA_cache_set(k, n, True)
        if existence:
            return True
        f, args = find_recursive_construction(k, n)
        OA = f(*args)

    else:
        _OA_cache_set(k, n, Unknown)
        if existence:
            return Unknown
        raise NotImplementedError(
            "I don't know how to build an OA({},{})!".format(k, n))

    if check:
        assert is_orthogonal_array(OA, k, n, t)

    return OA
Exemple #7
0
def BalancedIncompleteBlockDesign(v,k,existence=False,use_LJCR=False):
    r"""
    Returns a BIBD of parameters `v,k`.

    A Balanced Incomplete Block Design of parameters `v,k` is a collection
    `\mathcal C` of `k`-subsets of `V=\{0,\dots,v-1\}` such that for any two
    distinct elements `x,y\in V` there is a unique element `S\in \mathcal C`
    such that `x,y\in S`.

    More general definitions sometimes involve a `\lambda` parameter, and we
    assume here that `\lambda=1`.

    For more information on BIBD, see the
    :wikipedia:`corresponding Wikipedia entry <Block_design#Definition_of_a_BIBD_.28or_2-design.29>`.

    INPUT:

    - ``v,k`` (integers)

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

        - ``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.

    - ``use_LJCR`` (boolean) -- whether to query the La Jolla Covering
      Repository for the design when Sage does not know how to build it (see
      :meth:`~sage.combinat.designs.covering_design.best_known_covering_design_www`). This
      requires internet.

    .. SEEALSO::

        * :func:`steiner_triple_system`
        * :func:`v_4_1_BIBD`
        * :func:`v_5_1_BIBD`

    TODO:

        * Implement other constructions from the Handbook of Combinatorial
          Designs.

    EXAMPLES::

        sage: designs.BalancedIncompleteBlockDesign(7,3).blocks()
        [[0, 1, 3], [0, 2, 4], [0, 5, 6], [1, 2, 6], [1, 4, 5], [2, 3, 5], [3, 4, 6]]
        sage: B = designs.BalancedIncompleteBlockDesign(21,5, use_LJCR=True) # optional - internet
        sage: B                                                              # optional - internet
        Incidence structure with 21 points and 21 blocks
        sage: B.blocks()                                                     # optional - internet
        [[0, 1, 2, 3, 20], [0, 4, 8, 12, 16], [0, 5, 10, 15, 19],
         [0, 6, 11, 13, 17], [0, 7, 9, 14, 18], [1, 4, 11, 14, 19],
         [1, 5, 9, 13, 16], [1, 6, 8, 15, 18], [1, 7, 10, 12, 17],
         [2, 4, 9, 15, 17], [2, 5, 11, 12, 18], [2, 6, 10, 14, 16],
         [2, 7, 8, 13, 19], [3, 4, 10, 13, 18], [3, 5, 8, 14, 17],
         [3, 6, 9, 12, 19], [3, 7, 11, 15, 16], [4, 5, 6, 7, 20],
         [8, 9, 10, 11, 20], [12, 13, 14, 15, 20], [16, 17, 18, 19, 20]]
        sage: designs.BalancedIncompleteBlockDesign(20,5, use_LJCR=True) # optional - internet
        Traceback (most recent call last):
        ...
        ValueError: No such design exists !
        sage: designs.BalancedIncompleteBlockDesign(16,6)
        Traceback (most recent call last):
        ...
        NotImplementedError: I don't know how to build this design.

    TESTS::

        sage: designs.BalancedIncompleteBlockDesign(85,5,existence=True)
        True
        sage: _ = designs.BalancedIncompleteBlockDesign(85,5)

    A BIBD from a Finite Projective Plane::

        sage: _ = designs.BalancedIncompleteBlockDesign(21,5)

    Some trivial BIBD::

        sage: designs.BalancedIncompleteBlockDesign(10,10)
        Incidence structure with 10 points and 1 blocks
        sage: designs.BalancedIncompleteBlockDesign(1,10)
        Incidence structure with 1 points and 0 blocks

    Existence of BIBD with `k=3,4,5`::

        sage: [v for v in xrange(50) if designs.BalancedIncompleteBlockDesign(v,3,existence=True)]
        [1, 3, 7, 9, 13, 15, 19, 21, 25, 27, 31, 33, 37, 39, 43, 45, 49]
        sage: [v for v in xrange(100) if designs.BalancedIncompleteBlockDesign(v,4,existence=True)]
        [1, 4, 13, 16, 25, 28, 37, 40, 49, 52, 61, 64, 73, 76, 85, 88, 97]
        sage: [v for v in xrange(150) if designs.BalancedIncompleteBlockDesign(v,5,existence=True)]
        [1, 5, 21, 25, 41, 45, 61, 65, 81, 85, 101, 105, 121, 125, 141, 145]

    For `k > 5` there are currently very few constructions::

        sage: [v for v in xrange(150) if designs.BalancedIncompleteBlockDesign(v,6,existence=True) is True]
        [1, 6, 31]
        sage: [v for v in xrange(150) if designs.BalancedIncompleteBlockDesign(v,6,existence=True) is Unknown]
        [16, 21, 36, 46, 51, 61, 66, 76, 81, 91, 96, 106, 111, 121, 126, 136, 141]
    """
    if v == 1:
        if existence:
            return True
        return BlockDesign(v, [], test=False)

    if k == v:
        if existence:
            return True
        return BlockDesign(v, [range(v)], test=False)

    if v < k or k < 2 or (v-1) % (k-1) != 0 or (v*(v-1)) % (k*(k-1)) != 0:
        if existence:
            return False
        raise EmptySetError("No such design exists !")

    if k == 2:
        if existence:
            return True
        from itertools import combinations
        return BlockDesign(v, combinations(range(v),2), test = False)
    if k == 3:
        if existence:
            return v%6 == 1 or v%6 == 3
        return steiner_triple_system(v)
    if k == 4:
        if existence:
            return v%12 == 1 or v%12 == 4
        return BlockDesign(v, v_4_1_BIBD(v), test = False)
    if k == 5:
        if existence:
            return v%20 == 1 or v%20 == 5
        return BlockDesign(v, v_5_1_BIBD(v), test = False)

    if BIBD_from_TD(v,k,existence=True):
        if existence:
            return True
        return BlockDesign(v, BIBD_from_TD(v,k))
    if v == (k-1)**2+k and is_prime_power(k-1):
        if existence:
            return True
        from block_design import projective_plane
        return projective_plane(k-1)
    if use_LJCR:
        from covering_design import best_known_covering_design_www
        B = best_known_covering_design_www(v,k,2)

        # Is it a BIBD or just a good covering ?
        expected_n_of_blocks = binomial(v,2)/binomial(k,2)
        if B.low_bd() > expected_n_of_blocks:
            if existence:
                return False
            raise EmptySetError("No such design exists !")
        B = B.incidence_structure()
        if len(B.blcks) == expected_n_of_blocks:
            if existence:
                return True
            else:
                return B

    if existence:
        return Unknown
    else:
        raise NotImplementedError("I don't know how to build this design.")
Exemple #8
0
def v_4_1_BIBD(v, check=True):
    r"""
    Return a `(v,4,1)`-BIBD.

    A `(v,4,1)`-BIBD is an edge-decomposition of the complete graph `K_v` into
    copies of `K_4`. For more information, see
    :func:`balanced_incomplete_block_design`. It exists if and only if `v\equiv 1,4
    \pmod {12}`.

    See page 167 of [Stinson2004]_ for the construction details.

    .. SEEALSO::

        * :func:`balanced_incomplete_block_design`

    INPUT:

    - ``v`` (integer) -- number of points.

    - ``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.

    EXAMPLES::

        sage: from sage.combinat.designs.bibd import v_4_1_BIBD  # long time
        sage: for n in range(13,100):                            # long time
        ....:    if n%12 in [1,4]:                               # long time
        ....:       _ = v_4_1_BIBD(n, check = True)              # long time

    TESTS:

    Check that the `(25,4)` and `(37,4)`-difference family are available::

        sage: assert designs.difference_family(25,4,existence=True)
        sage: _ = designs.difference_family(25,4)
        sage: assert designs.difference_family(37,4,existence=True)
        sage: _ = designs.difference_family(37,4)

    Check some larger `(v,4,1)`-BIBD (see :trac:`17557`)::

        sage: for v in range(400):                                      # long time
        ....:     if v%12 in [1,4]:                                     # long time
        ....:         _ = designs.balanced_incomplete_block_design(v,4) # long time
    """
    k = 4
    if v == 0:
        return []
    if v <= 12 or v % 12 not in [1, 4]:
        raise EmptySetError(
            "A K_4-decomposition of K_v exists iif v=2,4 mod 12, v>12 or v==0")

    # Step 1. Base cases.
    if v == 13:
        # note: this construction can also be obtained from difference_family
        from block_design import projective_plane
        return projective_plane(3)._blocks
    if v == 16:
        from block_design import AffineGeometryDesign
        from sage.rings.finite_rings.constructor import FiniteField
        return AffineGeometryDesign(2, 1, FiniteField(4, 'x'))._blocks
    if v == 25 or v == 37:
        from difference_family import difference_family
        G, D = difference_family(v, 4)
        return BIBD_from_difference_family(G, D, check=False)
    if v == 28:
        return [[0, 1, 23, 26], [0, 2, 10, 11], [0, 3, 16, 18], [0, 4, 15, 20],
                [0, 5, 8, 9], [0, 6, 22, 25], [0, 7, 14, 21], [0, 12, 17, 27],
                [0, 13, 19, 24],
                [1, 2, 24, 27], [1, 3, 11, 12], [1, 4, 17, 19], [1, 5, 14, 16],
                [1, 6, 9, 10], [1, 7, 20, 25], [1, 8, 15, 22], [1, 13, 18, 21],
                [2, 3, 21, 25], [2, 4, 12, 13], [2, 5, 18, 20], [2, 6, 15, 17],
                [2, 7, 19, 22], [2, 8, 14, 26], [2, 9, 16, 23], [3, 4, 22, 26],
                [3, 5, 7, 13], [3, 6, 14, 19], [3, 8, 20, 23], [3, 9, 15, 27],
                [3, 10, 17, 24], [4, 5, 23, 27], [4, 6, 7, 8], [4, 9, 14, 24],
                [4, 10, 16, 21], [4, 11, 18, 25], [5, 6, 21, 24],
                [5, 10, 15, 25], [5, 11, 17, 22], [5, 12, 19, 26],
                [6, 11, 16, 26], [6, 12, 18, 23], [6, 13, 20, 27],
                [7, 9, 17, 18], [7, 10, 26, 27], [7, 11, 23, 24],
                [7, 12, 15, 16], [8, 10, 18, 19], [8, 11, 21, 27],
                [8, 12, 24, 25], [8, 13, 16, 17], [9, 11, 19, 20],
                [9, 12, 21, 22], [9, 13, 25, 26], [10, 12, 14, 20],
                [10, 13, 22, 23], [11, 13, 14, 15], [14, 17, 23, 25],
                [14, 18, 22, 27], [15, 18, 24, 26], [15, 19, 21, 23],
                [16, 19, 25, 27], [16, 20, 22, 24], [17, 20, 21, 26]]

    # Step 2 : this is function PBD_4_5_8_9_12
    PBD = PBD_4_5_8_9_12((v - 1) / (k - 1), check=False)

    # Step 3 : Theorem 7.20
    bibd = BIBD_from_PBD(PBD, v, k, check=False)

    if check:
        assert is_pairwise_balanced_design(bibd, v, [k])

    return bibd
Exemple #9
0
def v_4_1_BIBD(v, check=True):
    r"""
    Returns a `(v,4,1)`-BIBD.

    A `(v,4,1)`-BIBD is an edge-decomposition of the complete graph `K_v` into
    copies of `K_4`. For more information, see
    :meth:`BalancedIncompleteBlockDesign`. It exists if and only if `v\equiv 1,4
    \pmod {12}`.

    See page 167 of [Stinson2004]_ for the construction details.

    .. SEEALSO::

        * :meth:`BalancedIncompleteBlockDesign`

    INPUT:

    - ``v`` (integer) -- number of points.

    - ``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.

    EXAMPLES::

        sage: from sage.combinat.designs.bibd import v_4_1_BIBD  # long time
        sage: for n in range(13,100):                            # long time
        ....:    if n%12 in [1,4]:                               # long time
        ....:       _ = v_4_1_BIBD(n, check = True)              # long time
    """
    from sage.rings.finite_rings.constructor import FiniteField
    k = 4
    if v == 0:
        return []
    if v <= 12 or v%12 not in [1,4]:
        raise EmptySetError("A K_4-decomposition of K_v exists iif v=2,4 mod 12, v>12 or v==0")

    # Step 1. Base cases.
    if v == 13:
        from block_design import projective_plane
        return projective_plane(3).blocks()
    if v == 16:
        from block_design import AffineGeometryDesign
        return AffineGeometryDesign(2,1,FiniteField(4,'x')).blocks()
    if v == 25:
        return [[0, 1, 17, 22], [0, 2, 11, 21], [0, 3, 15, 18], [0, 4, 7, 13],
                [0, 5, 12, 14], [0, 6, 19, 23], [0, 8, 16, 24], [0, 9, 10, 20],
                [1, 2, 3, 4], [1, 5, 6, 7], [1, 8, 12, 15], [1, 9, 13, 16],
                [1, 10, 11, 14], [1, 18, 20, 23], [1, 19, 21, 24], [2, 5, 15, 24],
                [2, 6, 9, 17], [2, 7, 14, 18], [2, 8, 22, 23], [2, 10, 12, 13],
                [2, 16, 19, 20], [3, 5, 16, 22], [3, 6, 11, 20], [3, 7, 12, 19],
                [3, 8, 9, 14], [3, 10, 17, 24], [3, 13, 21, 23], [4, 5, 10, 23],
                [4, 6, 8, 21], [4, 9, 18, 24], [4, 11, 15, 16], [4, 12, 17, 20],
                [4, 14, 19, 22], [5, 8, 13, 20], [5, 9, 11, 19], [5, 17, 18, 21],
                [6, 10, 15, 22], [6, 12, 16, 18], [6, 13, 14, 24], [7, 8, 11, 17],
                [7, 9, 15, 23], [7, 10, 16, 21], [7, 20, 22, 24], [8, 10, 18, 19],
                [9, 12, 21, 22], [11, 12, 23, 24], [11, 13, 18, 22], [13, 15, 17, 19],
                [14, 15, 20, 21], [14, 16, 17, 23]]
    if v == 28:
        return [[0, 1, 23, 26], [0, 2, 10, 11], [0, 3, 16, 18], [0, 4, 15, 20],
                [0, 5, 8, 9], [0, 6, 22, 25], [0, 7, 14, 21], [0, 12, 17, 27],
                [0, 13, 19, 24], [1, 2, 24, 27], [1, 3, 11, 12], [1, 4, 17, 19],
                [1, 5, 14, 16], [1, 6, 9, 10], [1, 7, 20, 25], [1, 8, 15, 22],
                [1, 13, 18, 21], [2, 3, 21, 25], [2, 4, 12, 13], [2, 5, 18, 20],
                [2, 6, 15, 17], [2, 7, 19, 22], [2, 8, 14, 26], [2, 9, 16, 23],
                [3, 4, 22, 26], [3, 5, 7, 13], [3, 6, 14, 19], [3, 8, 20, 23],
                [3, 9, 15, 27], [3, 10, 17, 24], [4, 5, 23, 27], [4, 6, 7, 8],
                [4, 9, 14, 24], [4, 10, 16, 21], [4, 11, 18, 25], [5, 6, 21, 24],
                [5, 10, 15, 25], [5, 11, 17, 22], [5, 12, 19, 26], [6, 11, 16, 26],
                [6, 12, 18, 23], [6, 13, 20, 27], [7, 9, 17, 18], [7, 10, 26, 27],
                [7, 11, 23, 24], [7, 12, 15, 16], [8, 10, 18, 19], [8, 11, 21, 27],
                [8, 12, 24, 25], [8, 13, 16, 17], [9, 11, 19, 20], [9, 12, 21, 22],
                [9, 13, 25, 26], [10, 12, 14, 20], [10, 13, 22, 23], [11, 13, 14, 15],
                [14, 17, 23, 25], [14, 18, 22, 27], [15, 18, 24, 26], [15, 19, 21, 23],
                [16, 19, 25, 27], [16, 20, 22, 24], [17, 20, 21, 26]]
    if v == 37:
        return [[0, 1, 3, 24], [0, 2, 23, 36], [0, 4, 26, 32], [0, 5, 9, 31],
                [0, 6, 11, 15], [0, 7, 17, 25], [0, 8, 20, 27], [0, 10, 18, 30],
                [0, 12, 19, 29], [0, 13, 14, 16], [0, 21, 34, 35], [0, 22, 28, 33],
                [1, 2, 4, 25], [1, 5, 27, 33], [1, 6, 10, 32], [1, 7, 12, 16],
                [1, 8, 18, 26], [1, 9, 21, 28], [1, 11, 19, 31], [1, 13, 20, 30],
                [1, 14, 15, 17], [1, 22, 35, 36], [1, 23, 29, 34], [2, 3, 5, 26],
                [2, 6, 28, 34], [2, 7, 11, 33], [2, 8, 13, 17], [2, 9, 19, 27],
                [2, 10, 22, 29], [2, 12, 20, 32], [2, 14, 21, 31], [2, 15, 16, 18],
                [2, 24, 30, 35], [3, 4, 6, 27], [3, 7, 29, 35], [3, 8, 12, 34],
                [3, 9, 14, 18], [3, 10, 20, 28], [3, 11, 23, 30], [3, 13, 21, 33],
                [3, 15, 22, 32], [3, 16, 17, 19], [3, 25, 31, 36], [4, 5, 7, 28],
                [4, 8, 30, 36], [4, 9, 13, 35], [4, 10, 15, 19], [4, 11, 21, 29],
                [4, 12, 24, 31], [4, 14, 22, 34], [4, 16, 23, 33], [4, 17, 18, 20],
                [5, 6, 8, 29], [5, 10, 14, 36], [5, 11, 16, 20], [5, 12, 22, 30],
                [5, 13, 25, 32], [5, 15, 23, 35], [5, 17, 24, 34], [5, 18, 19, 21],
                [6, 7, 9, 30], [6, 12, 17, 21], [6, 13, 23, 31], [6, 14, 26, 33],
                [6, 16, 24, 36], [6, 18, 25, 35], [6, 19, 20, 22], [7, 8, 10, 31],
                [7, 13, 18, 22], [7, 14, 24, 32], [7, 15, 27, 34], [7, 19, 26, 36],
                [7, 20, 21, 23], [8, 9, 11, 32], [8, 14, 19, 23], [8, 15, 25, 33],
                [8, 16, 28, 35], [8, 21, 22, 24], [9, 10, 12, 33], [9, 15, 20, 24],
                [9, 16, 26, 34], [9, 17, 29, 36], [9, 22, 23, 25], [10, 11, 13, 34],
                [10, 16, 21, 25], [10, 17, 27, 35], [10, 23, 24, 26], [11, 12, 14, 35],
                [11, 17, 22, 26], [11, 18, 28, 36], [11, 24, 25, 27], [12, 13, 15, 36],
                [12, 18, 23, 27], [12, 25, 26, 28], [13, 19, 24, 28], [13, 26, 27, 29],
                [14, 20, 25, 29], [14, 27, 28, 30], [15, 21, 26, 30], [15, 28, 29, 31],
                [16, 22, 27, 31], [16, 29, 30, 32], [17, 23, 28, 32], [17, 30, 31, 33],
                [18, 24, 29, 33], [18, 31, 32, 34], [19, 25, 30, 34], [19, 32, 33, 35],
                [20, 26, 31, 35], [20, 33, 34, 36], [21, 27, 32, 36]]

    # Step 2 : this is function PBD_4_5_8_9_12
    PBD = PBD_4_5_8_9_12((v-1)/(k-1),check=False)

    # Step 3 : Theorem 7.20
    bibd = BIBD_from_PBD(PBD,v,k,check=False)

    if check:
        _check_pbd(bibd,v,[k])

    return bibd
Exemple #10
0
def BalancedIncompleteBlockDesign(v, k, existence=False, use_LJCR=False):
    r"""
    Returns a BIBD of parameters `v,k`.

    A Balanced Incomplete Block Design of parameters `v,k` is a collection
    `\mathcal C` of `k`-subsets of `V=\{0,\dots,v-1\}` such that for any two
    distinct elements `x,y\in V` there is a unique element `S\in \mathcal C`
    such that `x,y\in S`.

    More general definitions sometimes involve a `\lambda` parameter, and we
    assume here that `\lambda=1`.

    For more information on BIBD, see the
    :wikipedia:`corresponding Wikipedia entry <Block_design#Definition_of_a_BIBD_.28or_2-design.29>`.

    INPUT:

    - ``v,k`` (integers)

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

        - ``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.

    - ``use_LJCR`` (boolean) -- whether to query the La Jolla Covering
      Repository for the design when Sage does not know how to build it (see
      :meth:`~sage.combinat.designs.covering_design.best_known_covering_design_www`). This
      requires internet.

    .. SEEALSO::

        * :func:`steiner_triple_system`
        * :func:`v_4_1_BIBD`
        * :func:`v_5_1_BIBD`

    TODO:

        * Implement other constructions from the Handbook of Combinatorial
          Designs.

    EXAMPLES::

        sage: designs.BalancedIncompleteBlockDesign(7,3).blocks()
        [[0, 1, 3], [0, 2, 4], [0, 5, 6], [1, 2, 6], [1, 4, 5], [2, 3, 5], [3, 4, 6]]
        sage: B = designs.BalancedIncompleteBlockDesign(21,5, use_LJCR=True) # optional - internet
        sage: B                                                              # optional - internet
        Incidence structure with 21 points and 21 blocks
        sage: B.blocks()                                                     # optional - internet
        [[0, 1, 2, 3, 20], [0, 4, 8, 12, 16], [0, 5, 10, 15, 19],
         [0, 6, 11, 13, 17], [0, 7, 9, 14, 18], [1, 4, 11, 14, 19],
         [1, 5, 9, 13, 16], [1, 6, 8, 15, 18], [1, 7, 10, 12, 17],
         [2, 4, 9, 15, 17], [2, 5, 11, 12, 18], [2, 6, 10, 14, 16],
         [2, 7, 8, 13, 19], [3, 4, 10, 13, 18], [3, 5, 8, 14, 17],
         [3, 6, 9, 12, 19], [3, 7, 11, 15, 16], [4, 5, 6, 7, 20],
         [8, 9, 10, 11, 20], [12, 13, 14, 15, 20], [16, 17, 18, 19, 20]]
        sage: designs.BalancedIncompleteBlockDesign(20,5, use_LJCR=True) # optional - internet
        Traceback (most recent call last):
        ...
        ValueError: No such design exists !
        sage: designs.BalancedIncompleteBlockDesign(16,6)
        Traceback (most recent call last):
        ...
        NotImplementedError: I don't know how to build this design.

    TESTS::

        sage: designs.BalancedIncompleteBlockDesign(85,5,existence=True)
        True
        sage: _ = designs.BalancedIncompleteBlockDesign(85,5)

    A BIBD from a Finite Projective Plane::

        sage: _ = designs.BalancedIncompleteBlockDesign(21,5)

    Some trivial BIBD::

        sage: designs.BalancedIncompleteBlockDesign(10,10)
        Incidence structure with 10 points and 1 blocks
        sage: designs.BalancedIncompleteBlockDesign(1,10)
        Incidence structure with 1 points and 0 blocks

    Existence of BIBD with `k=3,4,5`::

        sage: [v for v in xrange(50) if designs.BalancedIncompleteBlockDesign(v,3,existence=True)]
        [1, 3, 7, 9, 13, 15, 19, 21, 25, 27, 31, 33, 37, 39, 43, 45, 49]
        sage: [v for v in xrange(100) if designs.BalancedIncompleteBlockDesign(v,4,existence=True)]
        [1, 4, 13, 16, 25, 28, 37, 40, 49, 52, 61, 64, 73, 76, 85, 88, 97]
        sage: [v for v in xrange(150) if designs.BalancedIncompleteBlockDesign(v,5,existence=True)]
        [1, 5, 21, 25, 41, 45, 61, 65, 81, 85, 101, 105, 121, 125, 141, 145]

    For `k > 5` there are currently very few constructions::

        sage: [v for v in xrange(150) if designs.BalancedIncompleteBlockDesign(v,6,existence=True) is True]
        [1, 6, 31]
        sage: [v for v in xrange(150) if designs.BalancedIncompleteBlockDesign(v,6,existence=True) is Unknown]
        [16, 21, 36, 46, 51, 61, 66, 76, 81, 91, 96, 106, 111, 121, 126, 136, 141]
    """
    if v == 1:
        if existence:
            return True
        return BlockDesign(v, [], test=False)

    if k == v:
        if existence:
            return True
        return BlockDesign(v, [range(v)], test=False)

    if v < k or k < 2 or (v - 1) % (k - 1) != 0 or (v *
                                                    (v - 1)) % (k *
                                                                (k - 1)) != 0:
        if existence:
            return False
        raise EmptySetError("No such design exists !")

    if k == 2:
        if existence:
            return True
        from itertools import combinations
        return BlockDesign(v, combinations(range(v), 2), test=False)
    if k == 3:
        if existence:
            return v % 6 == 1 or v % 6 == 3
        return steiner_triple_system(v)
    if k == 4:
        if existence:
            return v % 12 == 1 or v % 12 == 4
        return BlockDesign(v, v_4_1_BIBD(v), test=False)
    if k == 5:
        if existence:
            return v % 20 == 1 or v % 20 == 5
        return BlockDesign(v, v_5_1_BIBD(v), test=False)

    if BIBD_from_TD(v, k, existence=True):
        if existence:
            return True
        return BlockDesign(v, BIBD_from_TD(v, k))
    if v == (k - 1)**2 + k and is_prime_power(k - 1):
        if existence:
            return True
        from block_design import projective_plane
        return projective_plane(k - 1)
    if use_LJCR:
        from covering_design import best_known_covering_design_www
        B = best_known_covering_design_www(v, k, 2)

        # Is it a BIBD or just a good covering ?
        expected_n_of_blocks = binomial(v, 2) / binomial(k, 2)
        if B.low_bd() > expected_n_of_blocks:
            if existence:
                return False
            raise EmptySetError("No such design exists !")
        B = B.incidence_structure()
        if len(B.blcks) == expected_n_of_blocks:
            if existence:
                return True
            else:
                return B

    if existence:
        return Unknown
    else:
        raise NotImplementedError("I don't know how to build this design.")
Exemple #11
0
def v_4_1_BIBD(v, check=True):
    r"""
    Returns a `(v,4,1)`-BIBD.

    A `(v,4,1)`-BIBD is an edge-decomposition of the complete graph `K_v` into
    copies of `K_4`. For more information, see
    :meth:`BalancedIncompleteBlockDesign`. It exists if and only if `v\equiv 1,4
    \pmod {12}`.

    See page 167 of [Stinson2004]_ for the construction details.

    .. SEEALSO::

        * :meth:`BalancedIncompleteBlockDesign`

    INPUT:

    - ``v`` (integer) -- number of points.

    - ``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.

    EXAMPLES::

        sage: from sage.combinat.designs.bibd import v_4_1_BIBD  # long time
        sage: for n in range(13,100):                            # long time
        ....:    if n%12 in [1,4]:                               # long time
        ....:       _ = v_4_1_BIBD(n, check = True)              # long time
    """
    from sage.rings.finite_rings.constructor import FiniteField
    k = 4
    if v == 0:
        return []
    if v <= 12 or v % 12 not in [1, 4]:
        raise EmptySetError(
            "A K_4-decomposition of K_v exists iif v=2,4 mod 12, v>12 or v==0")

    # Step 1. Base cases.
    if v == 13:
        from block_design import projective_plane
        return projective_plane(3).blocks()
    if v == 16:
        from block_design import AffineGeometryDesign
        return AffineGeometryDesign(2, 1, FiniteField(4, 'x')).blocks()
    if v == 25:
        return [[0, 1, 17, 22], [0, 2, 11, 21], [0, 3, 15, 18], [0, 4, 7, 13],
                [0, 5, 12, 14], [0, 6, 19, 23], [0, 8, 16, 24], [0, 9, 10, 20],
                [1, 2, 3, 4], [1, 5, 6, 7], [1, 8, 12, 15], [1, 9, 13, 16],
                [1, 10, 11, 14], [1, 18, 20, 23], [1, 19, 21, 24],
                [2, 5, 15, 24], [2, 6, 9, 17], [2, 7, 14, 18], [2, 8, 22, 23],
                [2, 10, 12, 13], [2, 16, 19, 20], [3, 5, 16, 22],
                [3, 6, 11, 20], [3, 7, 12, 19], [3, 8, 9, 14], [3, 10, 17, 24],
                [3, 13, 21, 23], [4, 5, 10, 23], [4, 6, 8, 21], [4, 9, 18, 24],
                [4, 11, 15, 16], [4, 12, 17, 20], [4, 14, 19, 22],
                [5, 8, 13, 20], [5, 9, 11, 19], [5, 17, 18,
                                                 21], [6, 10, 15, 22],
                [6, 12, 16, 18], [6, 13, 14, 24], [7, 8, 11,
                                                   17], [7, 9, 15, 23],
                [7, 10, 16, 21], [7, 20, 22, 24], [8, 10, 18, 19],
                [9, 12, 21, 22], [11, 12, 23, 24], [11, 13, 18, 22],
                [13, 15, 17, 19], [14, 15, 20, 21], [14, 16, 17, 23]]
    if v == 28:
        return [[0, 1, 23, 26], [0, 2, 10, 11], [0, 3, 16, 18], [0, 4, 15, 20],
                [0, 5, 8, 9], [0, 6, 22, 25], [0, 7, 14, 21], [0, 12, 17, 27],
                [0, 13, 19, 24],
                [1, 2, 24, 27], [1, 3, 11, 12], [1, 4, 17, 19], [1, 5, 14, 16],
                [1, 6, 9, 10], [1, 7, 20, 25], [1, 8, 15, 22], [1, 13, 18, 21],
                [2, 3, 21, 25], [2, 4, 12, 13], [2, 5, 18, 20], [2, 6, 15, 17],
                [2, 7, 19, 22], [2, 8, 14, 26], [2, 9, 16, 23], [3, 4, 22, 26],
                [3, 5, 7, 13], [3, 6, 14, 19], [3, 8, 20, 23], [3, 9, 15, 27],
                [3, 10, 17, 24], [4, 5, 23, 27], [4, 6, 7, 8], [4, 9, 14, 24],
                [4, 10, 16, 21], [4, 11, 18, 25], [5, 6, 21, 24],
                [5, 10, 15, 25], [5, 11, 17, 22], [5, 12, 19, 26],
                [6, 11, 16, 26], [6, 12, 18, 23], [6, 13, 20, 27],
                [7, 9, 17, 18], [7, 10, 26, 27], [7, 11, 23, 24],
                [7, 12, 15, 16], [8, 10, 18, 19], [8, 11, 21, 27],
                [8, 12, 24, 25], [8, 13, 16, 17], [9, 11, 19, 20],
                [9, 12, 21, 22], [9, 13, 25, 26], [10, 12, 14, 20],
                [10, 13, 22, 23], [11, 13, 14, 15], [14, 17, 23, 25],
                [14, 18, 22, 27], [15, 18, 24, 26], [15, 19, 21, 23],
                [16, 19, 25, 27], [16, 20, 22, 24], [17, 20, 21, 26]]
    if v == 37:
        return [[0, 1, 3, 24], [0, 2, 23, 36], [0, 4, 26, 32], [0, 5, 9, 31],
                [0, 6, 11, 15], [0, 7, 17, 25], [0, 8, 20,
                                                 27], [0, 10, 18, 30],
                [0, 12, 19, 29], [0, 13, 14, 16], [0, 21, 34, 35],
                [0, 22, 28, 33], [1, 2, 4, 25], [1, 5, 27, 33], [1, 6, 10, 32],
                [1, 7, 12, 16], [1, 8, 18, 26], [1, 9, 21,
                                                 28], [1, 11, 19, 31],
                [1, 13, 20, 30], [1, 14, 15, 17], [1, 22, 35, 36],
                [1, 23, 29, 34], [2, 3, 5, 26], [2, 6, 28, 34], [2, 7, 11, 33],
                [2, 8, 13, 17], [2, 9, 19, 27], [2, 10, 22, 29],
                [2, 12, 20, 32], [2, 14, 21, 31], [2, 15, 16, 18],
                [2, 24, 30, 35], [3, 4, 6, 27], [3, 7, 29, 35], [3, 8, 12, 34],
                [3, 9, 14, 18], [3, 10, 20, 28], [3, 11, 23, 30],
                [3, 13, 21, 33], [3, 15, 22, 32], [3, 16, 17, 19],
                [3, 25, 31, 36], [4, 5, 7, 28], [4, 8, 30, 36], [4, 9, 13, 35],
                [4, 10, 15, 19], [4, 11, 21, 29], [4, 12, 24, 31],
                [4, 14, 22, 34], [4, 16, 23, 33], [4, 17, 18,
                                                   20], [5, 6, 8, 29],
                [5, 10, 14, 36], [5, 11, 16, 20], [5, 12, 22, 30],
                [5, 13, 25, 32], [5, 15, 23, 35], [5, 17, 24, 34],
                [5, 18, 19, 21], [6, 7, 9, 30], [6, 12, 17,
                                                 21], [6, 13, 23, 31],
                [6, 14, 26, 33], [6, 16, 24, 36], [6, 18, 25, 35],
                [6, 19, 20, 22], [7, 8, 10, 31], [7, 13, 18, 22],
                [7, 14, 24, 32], [7, 15, 27, 34], [7, 19, 26, 36],
                [7, 20, 21, 23], [8, 9, 11, 32], [8, 14, 19, 23],
                [8, 15, 25, 33], [8, 16, 28, 35], [8, 21, 22, 24],
                [9, 10, 12, 33], [9, 15, 20, 24], [9, 16, 26, 34],
                [9, 17, 29, 36], [9, 22, 23, 25], [10, 11, 13, 34],
                [10, 16, 21, 25], [10, 17, 27, 35], [10, 23, 24, 26],
                [11, 12, 14, 35], [11, 17, 22, 26], [11, 18, 28, 36],
                [11, 24, 25, 27], [12, 13, 15, 36], [12, 18, 23, 27],
                [12, 25, 26, 28], [13, 19, 24, 28], [13, 26, 27, 29],
                [14, 20, 25, 29], [14, 27, 28, 30], [15, 21, 26, 30],
                [15, 28, 29, 31], [16, 22, 27, 31], [16, 29, 30, 32],
                [17, 23, 28, 32], [17, 30, 31, 33], [18, 24, 29, 33],
                [18, 31, 32, 34], [19, 25, 30, 34], [19, 32, 33, 35],
                [20, 26, 31, 35], [20, 33, 34, 36], [21, 27, 32, 36]]

    # Step 2 : this is function PBD_4_5_8_9_12
    PBD = PBD_4_5_8_9_12((v - 1) / (k - 1), check=False)

    # Step 3 : Theorem 7.20
    bibd = BIBD_from_PBD(PBD, v, k, check=False)

    if check:
        _check_pbd(bibd, v, [k])

    return bibd
Exemple #12
0
def BalancedIncompleteBlockDesign(v, k, existence=False, use_LJCR=False):
    r"""
    Returns a BIBD of parameters `v,k`.

    A Balanced Incomplete Block Design of parameters `v,k` is a collection
    `\mathcal C` of `k`-subsets of `V=\{0,\dots,v-1\}` such that for any two
    distinct elements `x,y\in V` there is a unique element `S\in \mathcal C`
    such that `x,y\in S`.

    More general definitions sometimes involve a `\lambda` parameter, and we
    assume here that `\lambda=1`.

    For more information on BIBD, see the
    :wikipedia:`corresponding Wikipedia entry <Block_design#Definition_of_a_BIBD_.28or_2-design.29>`.

    INPUT:

    - ``v,k`` (integers)

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

        - ``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.

    - ``use_LJCR`` (boolean) -- whether to query the La Jolla Covering
      Repository for the design when Sage does not know how to build it (see
      :meth:`~sage.combinat.designs.covering_design.best_known_covering_design_www`). This
      requires internet.

    .. SEEALSO::

        * :meth:`steiner_triple_system`
        * :meth:`v_4_1_BIBD`

    TODO:

        * Implement `(v,5,1)`-BIBD using `this text <http://www.argilo.net/files/bibd.pdf>`_.
        * Implement other constructions from the Handbook of Combinatorial
          Designs.

    EXAMPLES::

        sage: designs.BalancedIncompleteBlockDesign(7,3).blocks()
        [[0, 1, 3], [0, 2, 4], [0, 5, 6], [1, 2, 6], [1, 4, 5], [2, 3, 5], [3, 4, 6]]
        sage: B = designs.BalancedIncompleteBlockDesign(21,5, use_LJCR=True) # optional - internet
        sage: B                                                              # optional - internet
        Incidence structure with 21 points and 21 blocks
        sage: B.blocks()                                                     # optional - internet
        [[0, 1, 2, 3, 20], [0, 4, 8, 12, 16], [0, 5, 10, 15, 19],
         [0, 6, 11, 13, 17], [0, 7, 9, 14, 18], [1, 4, 11, 14, 19],
         [1, 5, 9, 13, 16], [1, 6, 8, 15, 18], [1, 7, 10, 12, 17],
         [2, 4, 9, 15, 17], [2, 5, 11, 12, 18], [2, 6, 10, 14, 16],
         [2, 7, 8, 13, 19], [3, 4, 10, 13, 18], [3, 5, 8, 14, 17],
         [3, 6, 9, 12, 19], [3, 7, 11, 15, 16], [4, 5, 6, 7, 20],
         [8, 9, 10, 11, 20], [12, 13, 14, 15, 20], [16, 17, 18, 19, 20]]
        sage: designs.BalancedIncompleteBlockDesign(20,5, use_LJCR=True) # optional - internet
        Traceback (most recent call last):
        ...
        ValueError: No such design exists !

    TESTS::

        sage: designs.BalancedIncompleteBlockDesign(85,5,existence=True)
        True
        sage: _ = designs.BalancedIncompleteBlockDesign(85,5)

    A BIBD from a Finite Projective Plane::

        sage: _ = designs.BalancedIncompleteBlockDesign(21,5)

    A trivial BIBD::

        sage: designs.BalancedIncompleteBlockDesign(10,10)
        Incidence structure with 10 points and 1 blocks
    """
    if ((binomial(v, 2) % binomial(k, 2) != 0) or (v - 1) % (k - 1) != 0):
        if existence:
            return False
        raise ValueError("No such design exists !")

    if k == v:
        if existence:
            return True
        return BlockDesign(v, [range(v)], test=False)

    if k == 2:
        if existence:
            return True
        from itertools import combinations
        return BlockDesign(v, combinations(range(v), 2), test=False)
    if k == 3:
        if existence:
            return bool((n % 6) in [1, 3])
        return steiner_triple_system(v)
    if k == 4:
        if existence:
            return bool((n % 12) in [1, 4])
        return BlockDesign(v, v_4_1_BIBD(v), test=False)
    if BIBD_from_TD(v, k, existence=True):
        if existence:
            return True
        return BlockDesign(v, BIBD_from_TD(v, k))
    if v == (k - 1)**2 + k and is_prime_power(k - 1):
        if existence:
            return True
        from block_design import projective_plane
        return projective_plane(k - 1)
    if use_LJCR:
        from covering_design import best_known_covering_design_www
        B = best_known_covering_design_www(v, k, 2)

        # Is it a BIBD or just a good covering ?
        expected_n_of_blocks = binomial(v, 2) / binomial(k, 2)
        if B.low_bd() > expected_n_of_blocks:
            if existence:
                return False
            raise ValueError("No such design exists !")
        B = B.incidence_structure()
        if len(B.blcks) == expected_n_of_blocks:
            if existence:
                return True
            else:
                return B

    if existence:
        from sage.misc.unknown import Unknown
        return Unknown
    else:
        raise ValueError("I don't know how to build this design.")
def orthogonal_array(k,n,t=2,check=True,existence=False,who_asked=tuple()):
    r"""
    Return an orthogonal array of parameters `k,n,t`.

    An orthogonal array of parameters `k,n,t` is a matrix with `k` columns
    filled with integers from `[n]` in such a way that for any `t` columns, each
    of the `n^t` possible rows occurs exactly once. In
    particular, the matrix has `n^t` rows.

    More general definitions sometimes involve a `\lambda` parameter, and we
    assume here that `\lambda=1`.

    For more information on orthogonal arrays, see
    :wikipedia:`Orthogonal_array`.

    INPUT:

    - ``k`` -- (integer) number of columns. If ``k=None`` it is set to the
      largest value available.

    - ``n`` -- (integer) number of symbols

    - ``t`` -- (integer; default: 2) -- strength of the array

    - ``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.

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

        - ``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.

      .. NOTE::

          When ``k=None`` and ``existence=True`` the function returns an
          integer, i.e. the largest `k` such that we can build a `TD(k,n)`.

    - ``who_asked`` (internal use only) -- because of the equivalence between
      OA/TD/MOLS, each of the three constructors calls the others. We must keep
      track of who calls who in order to avoid infinite loops. ``who_asked`` is
      the tuple of the other functions that were called before this one on the
      same input `k,n`.

    OUTPUT:

    The kind of output depends on the input:

    - if ``existence=False`` (the default) then the output is a list of lists
      that represent an orthogonal array with parameters ``k`` and ``n``

    - if ``existence=True`` and ``k`` is an integer, then the function returns a
      troolean: either ``True``, ``Unknown`` or ``False``

    - if ``existence=True`` and ``k=None`` then the output is the largest value
      of ``k`` for which Sage knows how to compute a `TD(k,n)`.

    .. NOTE::

        This method implements theorems from [Stinson2004]_. See the code's
        documentation for details.

    .. SEEALSO::

        When `t=2` an orthogonal array is also a transversal design (see
        :func:`transversal_design`) and a family of mutually orthogonal latin
        squares (see
        :func:`~sage.combinat.designs.latin_squares.mutually_orthogonal_latin_squares`).

    EXAMPLES::

        sage: designs.orthogonal_array(3,2)
        [[0, 0, 0], [0, 1, 1], [1, 0, 1], [1, 1, 0]]

        sage: designs.orthogonal_array(5,5)
        [[0, 0, 0, 0, 0], [0, 1, 2, 3, 4], [0, 2, 4, 1, 3],
         [0, 3, 1, 4, 2], [0, 4, 3, 2, 1], [1, 0, 4, 3, 2],
         [1, 1, 1, 1, 1], [1, 2, 3, 4, 0], [1, 3, 0, 2, 4],
         [1, 4, 2, 0, 3], [2, 0, 3, 1, 4], [2, 1, 0, 4, 3],
         [2, 2, 2, 2, 2], [2, 3, 4, 0, 1], [2, 4, 1, 3, 0],
         [3, 0, 2, 4, 1], [3, 1, 4, 2, 0], [3, 2, 1, 0, 4],
         [3, 3, 3, 3, 3], [3, 4, 0, 1, 2], [4, 0, 1, 2, 3],
         [4, 1, 3, 0, 2], [4, 2, 0, 3, 1], [4, 3, 2, 1, 0],
         [4, 4, 4, 4, 4]]

    What is the largest value of `k` for which Sage knows how to compute a
    `OA(k,14,2)`?::

        sage: designs.orthogonal_array(None,14,existence=True)
        6

    If you ask for an orthogonal array that does not exist, then the function
    either raise an `EmptySetError` (if it knows that such an orthogonal array
    does not exist) or a `NotImplementedError`::

        sage: designs.orthogonal_array(4,2)
        Traceback (most recent call last):
        ...
        EmptySetError: No Orthogonal Array exists when k>=n+t except when n=1
        sage: designs.orthogonal_array(12,20)
        Traceback (most recent call last):
        ...
        NotImplementedError: I don't know how to build this orthogonal array!

    Note that these errors correspond respectively to the answers ``False`` and
    ``Unknown`` when the parameter ``existence`` is set to ``True``::

        sage: designs.orthogonal_array(4,2,existence=True)
        False
        sage: designs.orthogonal_array(12,20,existence=True)
        Unknown

    TESTS:

    The special case `n=1`::

        sage: designs.orthogonal_array(3,1)
        [[0, 0, 0]]
        sage: designs.orthogonal_array(None,1,existence=True)
        +Infinity
        sage: designs.orthogonal_array(None,1)
        Traceback (most recent call last):
        ...
        ValueError: there are no bound on k when n=1.
        sage: designs.orthogonal_array(None,14,existence=True)
        6
        sage: designs.orthogonal_array(16,1)
        [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
    """

    from sage.rings.finite_rings.constructor import FiniteField
    from latin_squares import mutually_orthogonal_latin_squares
    from database import OA_constructions
    from block_design import projective_plane, projective_plane_to_OA

    # If k is set to None we find the largest value available
    if k is None:
        if n == 1:
            if existence:
                from sage.rings.infinity import Infinity
                return Infinity
            raise ValueError("there are no bound on k when n=1.")

        for k in range(1,n+2):
            if not orthogonal_array(k+1,n,existence=True):
                break
        if existence:
            return k

    if k < 2:
        raise ValueError("undefined for k less than 2")

    if n == 1:
        OA = [[0]*k]

    elif k >= n+t:
        # When t=2 then k<n+t as it is equivalent to the existence of n-1 MOLS.
        # When t>2 the submatrix defined by the rows whose first t-2 elements
        # are 0s yields a OA with t=2 and k-(t-2) columns. Thus k-(t-2) < n+2,
        # i.e. k<n+t.
        if existence:
            return False
        raise EmptySetError("No Orthogonal Array exists when k>=n+t except when n=1")

    elif k == t:
        if existence:
            return True

        from itertools import product
        OA = map(list, product(range(n), repeat=k))

    elif n in OA_constructions and k <= OA_constructions[n][0]:
        if existence:
            return True
        _, construction = OA_constructions[n]

        OA = OA_from_wider_OA(construction(),k)

    # projective spaces are equivalent to OA(n+1,n,2)
    elif (t == 2 and
          (projective_plane(n, existence=True) or
           (k == n+1 and projective_plane(n, existence=True) is False))):
        if k == n+1:
            if existence:
                return projective_plane(n, existence=True)
            p = projective_plane(n, check=False)
            OA = projective_plane_to_OA(p)
        else:
            if existence:
                return True
            p = projective_plane(n, check=False)
            OA = [l[:k] for l in projective_plane_to_OA(p)]

    # Constructions from the database
    elif n in OA_constructions and k <= OA_constructions[n][0]:
        if existence:
            return True
        _, construction = OA_constructions[n]

        OA = OA_from_wider_OA(construction(),k)

    elif (t == 2 and transversal_design not in who_asked and
          transversal_design(k,n,existence=True,who_asked=who_asked+(orthogonal_array,)) is not Unknown):

        # forward existence
        if transversal_design(k,n,existence=True,who_asked=who_asked+(orthogonal_array,)):
            if existence:
                return True
            else:
                TD = transversal_design(k,n,check=False,who_asked=who_asked+(orthogonal_array,))
                OA = [[x%n for x in R] for R in TD]

        # forward non-existence
        else:
            if existence:
                return False
            raise EmptySetError("There exists no OA"+str((k,n))+"!")

    # Section 6.5.1 from [Stinson2004]
    elif (t == 2 and mutually_orthogonal_latin_squares not in who_asked and
          mutually_orthogonal_latin_squares(n,k-2, existence=True,who_asked=who_asked+(orthogonal_array,)) is not Unknown):

        # forward existence
        if mutually_orthogonal_latin_squares(n,k-2, existence=True,who_asked=who_asked+(orthogonal_array,)):
            if existence:
                return True
            else:
                mols = mutually_orthogonal_latin_squares(n,k-2,who_asked=who_asked+(orthogonal_array,))
                OA = [[i,j]+[m[i,j] for m in mols]
                      for i in range(n) for j in range(n)]
        # forward non-existence
        else:
            if existence:
                return False
            raise EmptySetError("There exists no OA"+str((k,n))+"!")

    else:
        if existence:
            return Unknown
        raise NotImplementedError("I don't know how to build this orthogonal array!")

    if check:
        assert is_orthogonal_array(OA,k,n,t)

    return OA