passagemath-graphs 10.6.1rc1__cp310-cp310-musllinux_1_2_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
- passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
- passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
- passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
- passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
- passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
- passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
- sage/all__sagemath_graphs.py +39 -0
- sage/combinat/abstract_tree.py +2723 -0
- sage/combinat/all__sagemath_graphs.py +34 -0
- sage/combinat/binary_tree.py +5306 -0
- sage/combinat/cluster_algebra_quiver/all.py +22 -0
- sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
- sage/combinat/cluster_algebra_quiver/interact.py +124 -0
- sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
- sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
- sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
- sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
- sage/combinat/designs/MOLS_handbook_data.py +570 -0
- sage/combinat/designs/all.py +58 -0
- sage/combinat/designs/bibd.py +1655 -0
- sage/combinat/designs/block_design.py +1071 -0
- sage/combinat/designs/covering_array.py +269 -0
- sage/combinat/designs/covering_design.py +530 -0
- sage/combinat/designs/database.py +5615 -0
- sage/combinat/designs/design_catalog.py +122 -0
- sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/designs_pyx.pxd +21 -0
- sage/combinat/designs/designs_pyx.pyx +993 -0
- sage/combinat/designs/difference_family.py +3951 -0
- sage/combinat/designs/difference_matrices.py +279 -0
- sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
- sage/combinat/designs/ext_rep.py +1064 -0
- sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
- sage/combinat/designs/group_divisible_designs.py +361 -0
- sage/combinat/designs/incidence_structures.py +2357 -0
- sage/combinat/designs/latin_squares.py +581 -0
- sage/combinat/designs/orthogonal_arrays.py +2244 -0
- sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
- sage/combinat/designs/resolvable_bibd.py +815 -0
- sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
- sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/subhypergraph_search.pyx +530 -0
- sage/combinat/designs/twographs.py +306 -0
- sage/combinat/finite_state_machine.py +14874 -0
- sage/combinat/finite_state_machine_generators.py +2006 -0
- sage/combinat/graph_path.py +448 -0
- sage/combinat/interval_posets.py +3908 -0
- sage/combinat/nu_tamari_lattice.py +269 -0
- sage/combinat/ordered_tree.py +1446 -0
- sage/combinat/posets/all.py +46 -0
- sage/combinat/posets/bubble_shuffle.py +247 -0
- sage/combinat/posets/cartesian_product.py +493 -0
- sage/combinat/posets/d_complete.py +182 -0
- sage/combinat/posets/elements.py +273 -0
- sage/combinat/posets/forest.py +30 -0
- sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/hasse_cython.pyx +174 -0
- sage/combinat/posets/hasse_diagram.py +3672 -0
- sage/combinat/posets/hochschild_lattice.py +158 -0
- sage/combinat/posets/incidence_algebras.py +794 -0
- sage/combinat/posets/lattices.py +5117 -0
- sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/linear_extension_iterator.pyx +292 -0
- sage/combinat/posets/linear_extensions.py +1037 -0
- sage/combinat/posets/mobile.py +275 -0
- sage/combinat/posets/moebius_algebra.py +776 -0
- sage/combinat/posets/poset_examples.py +2178 -0
- sage/combinat/posets/posets.py +9360 -0
- sage/combinat/rooted_tree.py +1070 -0
- sage/combinat/shard_order.py +239 -0
- sage/combinat/tamari_lattices.py +384 -0
- sage/combinat/yang_baxter_graph.py +923 -0
- sage/databases/all__sagemath_graphs.py +1 -0
- sage/databases/knotinfo_db.py +1231 -0
- sage/ext_data/all__sagemath_graphs.py +1 -0
- sage/ext_data/graphs/graph_plot_js.html +330 -0
- sage/ext_data/kenzo/CP2.txt +45 -0
- sage/ext_data/kenzo/CP3.txt +349 -0
- sage/ext_data/kenzo/CP4.txt +4774 -0
- sage/ext_data/kenzo/README.txt +49 -0
- sage/ext_data/kenzo/S4.txt +20 -0
- sage/graphs/all.py +42 -0
- sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/asteroidal_triples.pyx +320 -0
- sage/graphs/base/all.py +1 -0
- sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/boost_graph.pxd +106 -0
- sage/graphs/base/boost_graph.pyx +3045 -0
- sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/c_graph.pxd +106 -0
- sage/graphs/base/c_graph.pyx +5096 -0
- sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/dense_graph.pxd +28 -0
- sage/graphs/base/dense_graph.pyx +801 -0
- sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/graph_backends.pxd +5 -0
- sage/graphs/base/graph_backends.pyx +797 -0
- sage/graphs/base/overview.py +85 -0
- sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/sparse_graph.pxd +90 -0
- sage/graphs/base/sparse_graph.pyx +1653 -0
- sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_dense_graph.pxd +5 -0
- sage/graphs/base/static_dense_graph.pyx +1032 -0
- sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_backend.pxd +27 -0
- sage/graphs/base/static_sparse_backend.pyx +1583 -0
- sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_graph.pxd +37 -0
- sage/graphs/base/static_sparse_graph.pyx +1375 -0
- sage/graphs/bipartite_graph.py +2732 -0
- sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/centrality.pyx +1038 -0
- sage/graphs/cographs.py +519 -0
- sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/comparability.pyx +851 -0
- sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/connectivity.pxd +157 -0
- sage/graphs/connectivity.pyx +4813 -0
- sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/convexity_properties.pxd +16 -0
- sage/graphs/convexity_properties.pyx +870 -0
- sage/graphs/digraph.py +4754 -0
- sage/graphs/digraph_generators.py +1993 -0
- sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/distances_all_pairs.pxd +12 -0
- sage/graphs/distances_all_pairs.pyx +2938 -0
- sage/graphs/domination.py +1363 -0
- sage/graphs/dot2tex_utils.py +100 -0
- sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/edge_connectivity.pyx +1215 -0
- sage/graphs/generators/all.py +1 -0
- sage/graphs/generators/basic.py +1769 -0
- sage/graphs/generators/chessboard.py +538 -0
- sage/graphs/generators/classical_geometries.py +1611 -0
- sage/graphs/generators/degree_sequence.py +235 -0
- sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generators/distance_regular.pyx +2846 -0
- sage/graphs/generators/families.py +4759 -0
- sage/graphs/generators/intersection.py +565 -0
- sage/graphs/generators/platonic_solids.py +262 -0
- sage/graphs/generators/random.py +2623 -0
- sage/graphs/generators/smallgraphs.py +5741 -0
- sage/graphs/generators/world_map.py +724 -0
- sage/graphs/generic_graph.py +26867 -0
- sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generic_graph_pyx.pxd +34 -0
- sage/graphs/generic_graph_pyx.pyx +1673 -0
- sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/genus.pyx +622 -0
- sage/graphs/graph.py +9645 -0
- sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_coloring.pyx +2284 -0
- sage/graphs/graph_database.py +1177 -0
- sage/graphs/graph_decompositions/all.py +1 -0
- sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
- sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
- sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
- sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
- sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
- sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/graph_products.pyx +508 -0
- sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
- sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
- sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
- sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
- sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
- sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
- sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
- sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
- sage/graphs/graph_editor.py +82 -0
- sage/graphs/graph_generators.py +3314 -0
- sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_generators_pyx.pyx +95 -0
- sage/graphs/graph_input.py +812 -0
- sage/graphs/graph_latex.py +2064 -0
- sage/graphs/graph_list.py +410 -0
- sage/graphs/graph_plot.py +1756 -0
- sage/graphs/graph_plot_js.py +338 -0
- sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/hyperbolicity.pyx +1704 -0
- sage/graphs/hypergraph_generators.py +364 -0
- sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/independent_sets.pxd +13 -0
- sage/graphs/independent_sets.pyx +402 -0
- sage/graphs/isgci.py +1033 -0
- sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/isoperimetric_inequalities.pyx +489 -0
- sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/line_graph.pyx +743 -0
- sage/graphs/lovasz_theta.py +77 -0
- sage/graphs/matching.py +1633 -0
- sage/graphs/matching_covered_graph.py +3590 -0
- sage/graphs/orientations.py +1489 -0
- sage/graphs/partial_cube.py +459 -0
- sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/path_enumeration.pyx +2040 -0
- sage/graphs/pq_trees.py +1129 -0
- sage/graphs/print_graphs.py +201 -0
- sage/graphs/schnyder.py +865 -0
- sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/spanning_tree.pyx +1457 -0
- sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/strongly_regular_db.pyx +3340 -0
- sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/traversals.pxd +9 -0
- sage/graphs/traversals.pyx +1872 -0
- sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/trees.pxd +15 -0
- sage/graphs/trees.pyx +310 -0
- sage/graphs/tutte_polynomial.py +713 -0
- sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/views.pyx +794 -0
- sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/weakly_chordal.pyx +604 -0
- sage/groups/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
- sage/knots/all.py +6 -0
- sage/knots/free_knotinfo_monoid.py +507 -0
- sage/knots/gauss_code.py +291 -0
- sage/knots/knot.py +682 -0
- sage/knots/knot_table.py +284 -0
- sage/knots/knotinfo.py +2900 -0
- sage/knots/link.py +4715 -0
- sage/sandpiles/all.py +13 -0
- sage/sandpiles/examples.py +225 -0
- sage/sandpiles/sandpile.py +6365 -0
- sage/topology/all.py +22 -0
- sage/topology/cell_complex.py +1214 -0
- sage/topology/cubical_complex.py +1976 -0
- sage/topology/delta_complex.py +1806 -0
- sage/topology/filtered_simplicial_complex.py +744 -0
- sage/topology/moment_angle_complex.py +823 -0
- sage/topology/simplicial_complex.py +5160 -0
- sage/topology/simplicial_complex_catalog.py +92 -0
- sage/topology/simplicial_complex_examples.py +1680 -0
- sage/topology/simplicial_complex_homset.py +205 -0
- sage/topology/simplicial_complex_morphism.py +836 -0
- sage/topology/simplicial_set.py +4102 -0
- sage/topology/simplicial_set_catalog.py +55 -0
- sage/topology/simplicial_set_constructions.py +2954 -0
- sage/topology/simplicial_set_examples.py +865 -0
- sage/topology/simplicial_set_morphism.py +1464 -0
@@ -0,0 +1,1655 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-graphs
|
2
|
+
r"""
|
3
|
+
Balanced incomplete block designs (BIBD)
|
4
|
+
|
5
|
+
This module gathers everything related to Balanced Incomplete Block Designs. One can build a
|
6
|
+
BIBD (or check that it can be built) with :func:`balanced_incomplete_block_design`::
|
7
|
+
|
8
|
+
sage: BIBD = designs.balanced_incomplete_block_design(7,3,1) # needs sage.schemes
|
9
|
+
|
10
|
+
In particular, Sage can build a `(v,k,1)`-BIBD when one exists for all `k\leq
|
11
|
+
5`. The following functions are available:
|
12
|
+
|
13
|
+
|
14
|
+
.. csv-table::
|
15
|
+
:class: contentstable
|
16
|
+
:widths: 30, 70
|
17
|
+
:delim: |
|
18
|
+
|
19
|
+
:func:`balanced_incomplete_block_design` | Return a BIBD of parameters `v,k,\lambda`.
|
20
|
+
:func:`BIBD_from_TD` | Return a BIBD through TD-based constructions.
|
21
|
+
:func:`BIBD_from_difference_family` | Return the BIBD associated to the difference family ``D`` on the group ``G``.
|
22
|
+
:func:`BIBD_from_PBD` | Return a `(v,k,1)`-BIBD from a `(r,K)`-PBD where `r=(v-1)/(k-1)`.
|
23
|
+
:func:`PBD_from_TD` | Return a `(kt,\{k,t\})`-PBD if `u=0` and a `(kt+u,\{k,k+1,t,u\})`-PBD otherwise.
|
24
|
+
:func:`steiner_triple_system` | Return a Steiner Triple System.
|
25
|
+
:func:`v_5_1_BIBD` | Return a `(v,5,1)`-BIBD.
|
26
|
+
:func:`v_4_1_BIBD` | Return a `(v,4,1)`-BIBD.
|
27
|
+
:func:`PBD_4_5_8_9_12` | Return a `(v,\{4,5,8,9,12\})`-PBD on `v` elements.
|
28
|
+
:func:`BIBD_5q_5_for_q_prime_power` | Return a `(5q,5,1)`-BIBD with `q\equiv 1\pmod 4` a prime power.
|
29
|
+
|
30
|
+
|
31
|
+
**Construction of BIBD when** `k=4`
|
32
|
+
|
33
|
+
Decompositions of `K_v` into `K_4` (i.e. `(v,4,1)`-BIBD) are built following
|
34
|
+
Douglas Stinson's construction as presented in [Stinson2004]_ page 167. It is
|
35
|
+
based upon the construction of `(v\{4,5,8,9,12\})`-PBD (see the doc of
|
36
|
+
:func:`PBD_4_5_8_9_12`), knowing that a `(v\{4,5,8,9,12\})`-PBD on `v` points
|
37
|
+
can always be transformed into a `((k-1)v+1,4,1)`-BIBD, which covers all
|
38
|
+
possible cases of `(v,4,1)`-BIBD.
|
39
|
+
|
40
|
+
**Construction of BIBD when** `k=5`
|
41
|
+
|
42
|
+
Decompositions of `K_v` into `K_4` (i.e. `(v,4,1)`-BIBD) are built following
|
43
|
+
Clayton Smith's construction [ClaytonSmith]_.
|
44
|
+
|
45
|
+
.. [ClaytonSmith] On the existence of `(v,5,1)`-BIBD.
|
46
|
+
http://www.argilo.net/files/bibd.pdf
|
47
|
+
Clayton Smith
|
48
|
+
|
49
|
+
|
50
|
+
Functions
|
51
|
+
---------
|
52
|
+
"""
|
53
|
+
|
54
|
+
from sage.arith.misc import binomial, is_prime_power, is_square
|
55
|
+
from sage.categories.sets_cat import EmptySetError
|
56
|
+
from sage.misc.lazy_import import lazy_import
|
57
|
+
from sage.misc.unknown import Unknown
|
58
|
+
|
59
|
+
from .design_catalog import transversal_design # type:ignore
|
60
|
+
from .designs_pyx import is_pairwise_balanced_design
|
61
|
+
from .group_divisible_designs import GroupDivisibleDesign
|
62
|
+
|
63
|
+
lazy_import('sage.schemes.plane_conics.constructor', 'Conic')
|
64
|
+
|
65
|
+
|
66
|
+
def biplane(n, existence=False):
|
67
|
+
r"""
|
68
|
+
Return a biplane of order `n`.
|
69
|
+
|
70
|
+
A biplane of order `n` is a symmetric `(1+\frac {(n+1)(n+2)} {2}, n+2, 2)`-BIBD.
|
71
|
+
A symmetric (or square) `(v,k,\lambda)`-BIBD is a `(v,k,\lambda)`-BIBD with `v` blocks.
|
72
|
+
|
73
|
+
INPUT:
|
74
|
+
|
75
|
+
- ``n`` -- integer; order of the biplane
|
76
|
+
|
77
|
+
- ``existence`` -- boolean; instead of building the design, return:
|
78
|
+
|
79
|
+
- ``True`` -- meaning that Sage knows how to build the design
|
80
|
+
|
81
|
+
- ``Unknown`` -- meaning that Sage does not know how to build the
|
82
|
+
design, but that the design may exist (see :mod:`sage.misc.unknown`)
|
83
|
+
|
84
|
+
- ``False`` -- meaning that the design does not exist
|
85
|
+
|
86
|
+
.. SEEALSO::
|
87
|
+
|
88
|
+
* :func:`balanced_incomplete_block_design`
|
89
|
+
|
90
|
+
EXAMPLES::
|
91
|
+
|
92
|
+
sage: designs.biplane(4) # needs sage.rings.finite_rings
|
93
|
+
(16,6,2)-Balanced Incomplete Block Design
|
94
|
+
sage: designs.biplane(7, existence=True) # needs sage.schemes
|
95
|
+
True
|
96
|
+
sage: designs.biplane(11) # needs sage.schemes
|
97
|
+
(79,13,2)-Balanced Incomplete Block Design
|
98
|
+
|
99
|
+
TESTS::
|
100
|
+
|
101
|
+
sage: designs.biplane(9) # needs sage.libs.gap
|
102
|
+
(56,11,2)-Balanced Incomplete Block Design
|
103
|
+
|
104
|
+
Check all known biplanes::
|
105
|
+
|
106
|
+
sage: [n for n in [0,1,2,3,4,7,9,11] # needs sage.schemes
|
107
|
+
....: if designs.biplane(n, existence=True) is True]
|
108
|
+
[0, 1, 2, 3, 4, 7, 9, 11]
|
109
|
+
"""
|
110
|
+
k = n+2
|
111
|
+
v = (k*(k-1))//2 + 1
|
112
|
+
return balanced_incomplete_block_design(v, k, lambd=2, existence=existence)
|
113
|
+
|
114
|
+
|
115
|
+
def balanced_incomplete_block_design(v, k, lambd=1, existence=False, use_LJCR=False):
|
116
|
+
r"""
|
117
|
+
Return a BIBD of parameters `v,k, \lambda`.
|
118
|
+
|
119
|
+
A Balanced Incomplete Block Design of parameters `v,k,\lambda` is a collection
|
120
|
+
`\mathcal C` of `k`-subsets of `V=\{0,\dots,v-1\}` such that for any two
|
121
|
+
distinct elements `x,y\in V` there are `\lambda` elements `S\in \mathcal C`
|
122
|
+
such that `x,y\in S`.
|
123
|
+
|
124
|
+
For more information on BIBD, see the
|
125
|
+
:wikipedia:`corresponding Wikipedia entry <Block_design#Definition_of_a_BIBD_.28or_2-design.29>`.
|
126
|
+
|
127
|
+
INPUT:
|
128
|
+
|
129
|
+
- ``v``, ``k``, ``lambd`` -- integers
|
130
|
+
|
131
|
+
- ``existence`` -- boolean; instead of building the design, return:
|
132
|
+
|
133
|
+
- ``True`` -- meaning that Sage knows how to build the design
|
134
|
+
|
135
|
+
- ``Unknown`` -- meaning that Sage does not know how to build the
|
136
|
+
design, but that the design may exist (see :mod:`sage.misc.unknown`)
|
137
|
+
|
138
|
+
- ``False`` -- meaning that the design does not exist
|
139
|
+
|
140
|
+
- ``use_LJCR`` -- boolean; whether to query the La Jolla Covering
|
141
|
+
Repository for the design when Sage does not know how to build it (see
|
142
|
+
:func:`~sage.combinat.designs.covering_design.best_known_covering_design_www`). This
|
143
|
+
requires internet.
|
144
|
+
|
145
|
+
.. SEEALSO::
|
146
|
+
|
147
|
+
* :func:`steiner_triple_system`
|
148
|
+
* :func:`v_4_1_BIBD`
|
149
|
+
* :func:`v_5_1_BIBD`
|
150
|
+
|
151
|
+
.. TODO::
|
152
|
+
|
153
|
+
Implement other constructions from the Handbook of Combinatorial
|
154
|
+
Designs.
|
155
|
+
|
156
|
+
EXAMPLES::
|
157
|
+
|
158
|
+
sage: designs.balanced_incomplete_block_design(7, 3, 1).blocks() # needs sage.schemes
|
159
|
+
[[0, 1, 3], [0, 2, 4], [0, 5, 6], [1, 2, 6], [1, 4, 5], [2, 3, 5], [3, 4, 6]]
|
160
|
+
sage: B = designs.balanced_incomplete_block_design(66, 6, 1, # optional - internet
|
161
|
+
....: use_LJCR=True)
|
162
|
+
sage: B # optional - internet
|
163
|
+
(66,6,1)-Balanced Incomplete Block Design
|
164
|
+
sage: B.blocks() # optional - internet
|
165
|
+
[[0, 1, 2, 3, 4, 65], [0, 5, 22, 32, 38, 58], [0, 6, 21, 30, 43, 48], ...
|
166
|
+
sage: designs.balanced_incomplete_block_design(216, 6, 1)
|
167
|
+
Traceback (most recent call last):
|
168
|
+
...
|
169
|
+
NotImplementedError: I don't know how to build a (216,6,1)-BIBD!
|
170
|
+
|
171
|
+
TESTS::
|
172
|
+
|
173
|
+
sage: designs.balanced_incomplete_block_design(85,5,existence=True)
|
174
|
+
True
|
175
|
+
sage: _ = designs.balanced_incomplete_block_design(85,5) # needs sage.libs.pari
|
176
|
+
|
177
|
+
A BIBD from a Finite Projective Plane::
|
178
|
+
|
179
|
+
sage: _ = designs.balanced_incomplete_block_design(21,5) # needs sage.schemes
|
180
|
+
|
181
|
+
Some trivial BIBD::
|
182
|
+
|
183
|
+
sage: designs.balanced_incomplete_block_design(10,10)
|
184
|
+
(10,10,1)-Balanced Incomplete Block Design
|
185
|
+
sage: designs.balanced_incomplete_block_design(1,10)
|
186
|
+
(1,0,1)-Balanced Incomplete Block Design
|
187
|
+
|
188
|
+
Existence of BIBD with `k=3,4,5`::
|
189
|
+
|
190
|
+
sage: [v for v in range(50) if designs.balanced_incomplete_block_design(v,3,existence=True)] # needs sage.schemes
|
191
|
+
[1, 3, 7, 9, 13, 15, 19, 21, 25, 27, 31, 33, 37, 39, 43, 45, 49]
|
192
|
+
sage: [v for v in range(100) if designs.balanced_incomplete_block_design(v,4,existence=True)] # needs sage.schemes
|
193
|
+
[1, 4, 13, 16, 25, 28, 37, 40, 49, 52, 61, 64, 73, 76, 85, 88, 97]
|
194
|
+
sage: [v for v in range(150) if designs.balanced_incomplete_block_design(v,5,existence=True)] # needs sage.schemes
|
195
|
+
[1, 5, 21, 25, 41, 45, 61, 65, 81, 85, 101, 105, 121, 125, 141, 145]
|
196
|
+
|
197
|
+
For `k > 5` there are currently very few constructions::
|
198
|
+
|
199
|
+
sage: [v for v in range(300) if designs.balanced_incomplete_block_design(v,6,existence=True) is True] # needs sage.schemes
|
200
|
+
[1, 6, 31, 66, 76, 91, 96, 106, 111, 121, 126, 136, 141, 151, 156, 171, 181, 186, 196, 201, 211, 241, 271]
|
201
|
+
sage: [v for v in range(300) if designs.balanced_incomplete_block_design(v,6,existence=True) is Unknown] # needs sage.schemes
|
202
|
+
[51, 61, 81, 166, 216, 226, 231, 246, 256, 261, 276, 286, 291]
|
203
|
+
|
204
|
+
Here are some constructions with `k \geq 7` and `v` a prime power::
|
205
|
+
|
206
|
+
sage: # needs sage.libs.pari
|
207
|
+
sage: designs.balanced_incomplete_block_design(169,7)
|
208
|
+
(169,7,1)-Balanced Incomplete Block Design
|
209
|
+
sage: designs.balanced_incomplete_block_design(617,8)
|
210
|
+
(617,8,1)-Balanced Incomplete Block Design
|
211
|
+
sage: designs.balanced_incomplete_block_design(433,9)
|
212
|
+
(433,9,1)-Balanced Incomplete Block Design
|
213
|
+
sage: designs.balanced_incomplete_block_design(1171,10)
|
214
|
+
(1171,10,1)-Balanced Incomplete Block Design
|
215
|
+
|
216
|
+
And we know some nonexistence results::
|
217
|
+
|
218
|
+
sage: designs.balanced_incomplete_block_design(21,6,existence=True)
|
219
|
+
False
|
220
|
+
|
221
|
+
Some BIBDs with `\lambda \neq 1`::
|
222
|
+
|
223
|
+
sage: designs.balanced_incomplete_block_design(176, 50, 14, existence=True)
|
224
|
+
True
|
225
|
+
sage: designs.balanced_incomplete_block_design(64,28,12) # needs sage.libs.pari
|
226
|
+
(64,28,12)-Balanced Incomplete Block Design
|
227
|
+
sage: designs.balanced_incomplete_block_design(37,9,8) # needs sage.libs.pari
|
228
|
+
(37,9,8)-Balanced Incomplete Block Design
|
229
|
+
sage: designs.balanced_incomplete_block_design(15,7,3) # needs sage.schemes
|
230
|
+
(15,7,3)-Balanced Incomplete Block Design
|
231
|
+
|
232
|
+
Some BIBDs from the recursive construction ::
|
233
|
+
|
234
|
+
sage: designs.balanced_incomplete_block_design(76,16,4) # needs sage.libs.pari
|
235
|
+
(76,16,4)-Balanced Incomplete Block Design
|
236
|
+
sage: designs.balanced_incomplete_block_design(10,4,2) # needs sage.libs.pari
|
237
|
+
(10,4,2)-Balanced Incomplete Block Design
|
238
|
+
sage: designs.balanced_incomplete_block_design(50,25,24) # needs sage.schemes
|
239
|
+
(50,25,24)-Balanced Incomplete Block Design
|
240
|
+
sage: designs.balanced_incomplete_block_design(29,15,15) # needs sage.libs.pari sage.schemes
|
241
|
+
(29,15,15)-Balanced Incomplete Block Design
|
242
|
+
"""
|
243
|
+
# Trivial BIBD
|
244
|
+
if v == 1:
|
245
|
+
if existence:
|
246
|
+
return True
|
247
|
+
return BIBD(v, [], check=False)
|
248
|
+
|
249
|
+
if k == v:
|
250
|
+
if existence:
|
251
|
+
return True
|
252
|
+
return BIBD(v, [list(range(v)) for _ in range(lambd)],lambd=lambd, check=False, copy=False)
|
253
|
+
|
254
|
+
# Non-existence of BIBD
|
255
|
+
if (v < k or
|
256
|
+
k < 2 or
|
257
|
+
(lambd*(v-1)) % (k-1) != 0 or
|
258
|
+
(lambd*v*(v-1)) % (k*(k-1)) != 0 or
|
259
|
+
# From the Handbook of combinatorial designs:
|
260
|
+
#
|
261
|
+
# With lambda>1 other exceptions are
|
262
|
+
# (15,5,2),(21,6,2),(22,7,2),(22,8,4).
|
263
|
+
(k == 6 and v in [36,46]) or
|
264
|
+
(k == 7 and v == 43) or
|
265
|
+
# Fisher's inequality
|
266
|
+
(lambd*v*(v-1))/(k*(k-1)) < v):
|
267
|
+
if existence:
|
268
|
+
return False
|
269
|
+
raise EmptySetError("There exists no ({},{},{})-BIBD".format(v, k, lambd))
|
270
|
+
|
271
|
+
# Non-existence by BRC Theorem
|
272
|
+
if BruckRyserChowla_check(v, k, lambd) is False:
|
273
|
+
if existence:
|
274
|
+
return False
|
275
|
+
raise EmptySetError("There exists no ({},{},{})-BIBD by Bruck-Ryser-Chowla Theorem".format(v,k,lambd))
|
276
|
+
|
277
|
+
if k == 2:
|
278
|
+
if existence:
|
279
|
+
return True
|
280
|
+
return BIBD(v, [[x, y] for _ in range(lambd) for x in range(v) for y in range(x+1, v) if x != y], lambd=lambd, check=False, copy=True)
|
281
|
+
if k == 3 and lambd == 1:
|
282
|
+
if existence:
|
283
|
+
return v % 6 == 1 or v % 6 == 3
|
284
|
+
return steiner_triple_system(v)
|
285
|
+
if k == 4 and lambd == 1:
|
286
|
+
if existence:
|
287
|
+
return v % 12 == 1 or v % 12 == 4
|
288
|
+
return BIBD(v, v_4_1_BIBD(v), copy=False)
|
289
|
+
if k == 5 and lambd == 1:
|
290
|
+
if existence:
|
291
|
+
return v % 20 == 1 or v % 20 == 5
|
292
|
+
return BIBD(v, v_5_1_BIBD(v), copy=False)
|
293
|
+
|
294
|
+
from .difference_family import difference_family
|
295
|
+
from .database import BIBD_constructions
|
296
|
+
|
297
|
+
if (v, k, lambd) in BIBD_constructions:
|
298
|
+
if existence:
|
299
|
+
return True
|
300
|
+
return BIBD(v,BIBD_constructions[(v, k, lambd)](), lambd=lambd, copy=False)
|
301
|
+
if lambd == 1 and BIBD_from_arc_in_desarguesian_projective_plane(v, k, existence=True):
|
302
|
+
if existence:
|
303
|
+
return True
|
304
|
+
B = BIBD_from_arc_in_desarguesian_projective_plane(v, k)
|
305
|
+
return BIBD(v, B, copy=False)
|
306
|
+
if lambd == 1 and BIBD_from_TD(v, k, existence=True) is True:
|
307
|
+
if existence:
|
308
|
+
return True
|
309
|
+
return BIBD(v, BIBD_from_TD(v, k), copy=False)
|
310
|
+
if lambd == 1 and v == (k-1)**2+k and is_prime_power(k-1):
|
311
|
+
if existence:
|
312
|
+
return True
|
313
|
+
from .block_design import projective_plane
|
314
|
+
return BIBD(v, projective_plane(k-1),copy=False)
|
315
|
+
if difference_family(v, k, l=lambd, existence=True) is True:
|
316
|
+
if existence:
|
317
|
+
return True
|
318
|
+
G, D = difference_family(v, k, l=lambd)
|
319
|
+
return BIBD(v, BIBD_from_difference_family(G, D, check=False), lambd=lambd, copy=False)
|
320
|
+
if lambd == 1 and use_LJCR:
|
321
|
+
from .covering_design import best_known_covering_design_www
|
322
|
+
values_in_db = False
|
323
|
+
try:
|
324
|
+
B = best_known_covering_design_www(v, k, 2)
|
325
|
+
values_in_db = True
|
326
|
+
except ValueError:
|
327
|
+
# the parameters are not in the LJCR database
|
328
|
+
pass
|
329
|
+
|
330
|
+
if values_in_db:
|
331
|
+
# Is it a BIBD or just a good covering?
|
332
|
+
expected_n_of_blocks = binomial(v, 2) // binomial(k, 2)
|
333
|
+
if B.low_bd() > expected_n_of_blocks:
|
334
|
+
if existence:
|
335
|
+
return False
|
336
|
+
raise EmptySetError(f"there exists no ({v},{k},{lambd})-BIBD")
|
337
|
+
B = B.incidence_structure()
|
338
|
+
if B.num_blocks() == expected_n_of_blocks:
|
339
|
+
if existence:
|
340
|
+
return True
|
341
|
+
else:
|
342
|
+
return BIBD(B.ground_set(), B.blocks(), k=k, lambd=1, copy=False)
|
343
|
+
|
344
|
+
if ( (k+lambd)*(k+lambd-1) == lambd*(v+k+lambd-1) and
|
345
|
+
balanced_incomplete_block_design(v+k+lambd, k+lambd, lambd, existence=True) is True):
|
346
|
+
# By removing a block and all points of that block from the
|
347
|
+
# symmetric (v+k+lambd, k+lambd, lambd) BIBD
|
348
|
+
# we get a (v, k, lambd) BIBD
|
349
|
+
if existence:
|
350
|
+
return True
|
351
|
+
|
352
|
+
D = balanced_incomplete_block_design(v+k+lambd, k+lambd, lambd)
|
353
|
+
Br = D.blocks()[0] # block to remove
|
354
|
+
blocks = D.blocks()[1:]
|
355
|
+
|
356
|
+
blocks = [set(B).difference(Br) for B in blocks]
|
357
|
+
points = set(D.ground_set()).difference(Br)
|
358
|
+
|
359
|
+
return BalancedIncompleteBlockDesign(points, blocks, k=k, lambd=lambd, copy=False)
|
360
|
+
|
361
|
+
if existence:
|
362
|
+
return Unknown
|
363
|
+
else:
|
364
|
+
raise NotImplementedError("I don't know how to build a ({},{},{})-BIBD!".format(v, k, lambd))
|
365
|
+
|
366
|
+
|
367
|
+
def BruckRyserChowla_check(v, k, lambd):
|
368
|
+
r"""
|
369
|
+
Check whether the parameters passed satisfy the Bruck-Ryser-Chowla theorem.
|
370
|
+
|
371
|
+
For more information on the theorem, see the
|
372
|
+
:wikipedia:`corresponding Wikipedia entry <Bruck–Ryser–Chowla_theorem>`.
|
373
|
+
|
374
|
+
INPUT:
|
375
|
+
|
376
|
+
- ``v``, ``k``, ``lambd`` -- integers; parameters to check
|
377
|
+
|
378
|
+
OUTPUT: ``True`` -- the parameters satisfy the theorem
|
379
|
+
|
380
|
+
- ``False`` -- the theorem fails for the given parameters
|
381
|
+
|
382
|
+
- ``Unknown`` -- the preconditions of the theorem are not met
|
383
|
+
|
384
|
+
EXAMPLES:
|
385
|
+
|
386
|
+
sage: from sage.combinat.designs.bibd import BruckRyserChowla_check
|
387
|
+
sage: BruckRyserChowla_check(22,7,2)
|
388
|
+
False
|
389
|
+
|
390
|
+
Nonexistence of projective planes of order 6 and 14
|
391
|
+
|
392
|
+
sage: from sage.combinat.designs.bibd import BruckRyserChowla_check
|
393
|
+
sage: BruckRyserChowla_check(43,7,1) # needs sage.schemes
|
394
|
+
False
|
395
|
+
sage: BruckRyserChowla_check(211,15,1) # needs sage.schemes
|
396
|
+
False
|
397
|
+
|
398
|
+
Existence of symmetric BIBDs with parameters `(79,13,2)` and `(56,11,2)`
|
399
|
+
|
400
|
+
sage: from sage.combinat.designs.bibd import BruckRyserChowla_check
|
401
|
+
sage: BruckRyserChowla_check(79,13,2) # needs sage.schemes
|
402
|
+
True
|
403
|
+
sage: BruckRyserChowla_check(56,11,2)
|
404
|
+
True
|
405
|
+
|
406
|
+
TESTS:
|
407
|
+
|
408
|
+
Test some non-symmetric parameters::
|
409
|
+
|
410
|
+
sage: from sage.combinat.designs.bibd import BruckRyserChowla_check
|
411
|
+
sage: BruckRyserChowla_check(89,11,3)
|
412
|
+
Unknown
|
413
|
+
sage: BruckRyserChowla_check(25,23,2)
|
414
|
+
Unknown
|
415
|
+
|
416
|
+
Clearly wrong parameters satisfying the theorem::
|
417
|
+
|
418
|
+
sage: from sage.combinat.designs.bibd import BruckRyserChowla_check
|
419
|
+
sage: BruckRyserChowla_check(13,25,50) # needs sage.schemes
|
420
|
+
True
|
421
|
+
"""
|
422
|
+
from sage.rings.rational_field import QQ
|
423
|
+
|
424
|
+
# design is not symmetric
|
425
|
+
if k*(k-1) != lambd*(v-1):
|
426
|
+
return Unknown
|
427
|
+
|
428
|
+
if v % 2 == 0:
|
429
|
+
return is_square(k-lambd)
|
430
|
+
|
431
|
+
g = 1 if v % 4 == 1 else -1
|
432
|
+
C = Conic(QQ, [1, lambd - k, -g * lambd])
|
433
|
+
|
434
|
+
(flag, sol) = C.has_rational_point(point=True)
|
435
|
+
|
436
|
+
return flag
|
437
|
+
|
438
|
+
|
439
|
+
def steiner_triple_system(n):
|
440
|
+
r"""
|
441
|
+
Return a Steiner Triple System.
|
442
|
+
|
443
|
+
A Steiner Triple System (STS) of a set `\{0,...,n-1\}`
|
444
|
+
is a family `S` of 3-sets such that for any `i \not = j`
|
445
|
+
there exists exactly one set of `S` in which they are
|
446
|
+
both contained.
|
447
|
+
|
448
|
+
It can alternatively be thought of as a factorization of
|
449
|
+
the complete graph `K_n` with triangles.
|
450
|
+
|
451
|
+
A Steiner Triple System of a `n`-set exists if and only if
|
452
|
+
`n \equiv 1 \pmod 6` or `n \equiv 3 \pmod 6`, in which case
|
453
|
+
one can be found through Bose's and Skolem's constructions,
|
454
|
+
respectively [AndHonk97]_.
|
455
|
+
|
456
|
+
INPUT:
|
457
|
+
|
458
|
+
- ``n`` -- return a Steiner Triple System of `\{0,...,n-1\}`
|
459
|
+
|
460
|
+
EXAMPLES:
|
461
|
+
|
462
|
+
A Steiner Triple System on `9` elements ::
|
463
|
+
|
464
|
+
sage: sts = designs.steiner_triple_system(9)
|
465
|
+
sage: sts
|
466
|
+
(9,3,1)-Balanced Incomplete Block Design
|
467
|
+
sage: list(sts)
|
468
|
+
[[0, 1, 5], [0, 2, 4], [0, 3, 6], [0, 7, 8], [1, 2, 3],
|
469
|
+
[1, 4, 7], [1, 6, 8], [2, 5, 8], [2, 6, 7], [3, 4, 8],
|
470
|
+
[3, 5, 7], [4, 5, 6]]
|
471
|
+
|
472
|
+
As any pair of vertices is covered once, its parameters are ::
|
473
|
+
|
474
|
+
sage: sts.is_t_design(return_parameters=True)
|
475
|
+
(True, (2, 9, 3, 1))
|
476
|
+
|
477
|
+
An exception is raised for invalid values of ``n`` ::
|
478
|
+
|
479
|
+
sage: designs.steiner_triple_system(10)
|
480
|
+
Traceback (most recent call last):
|
481
|
+
...
|
482
|
+
EmptySetError: Steiner triple systems only exist for n = 1 mod 6 or n = 3 mod 6
|
483
|
+
|
484
|
+
REFERENCE:
|
485
|
+
|
486
|
+
.. [AndHonk97] A short course in Combinatorial Designs,
|
487
|
+
Ian Anderson, Iiro Honkala,
|
488
|
+
Internet Editions, Spring 1997,
|
489
|
+
http://www.utu.fi/~honkala/designs.ps
|
490
|
+
"""
|
491
|
+
|
492
|
+
name = "Steiner Triple System on "+str(n)+" elements"
|
493
|
+
|
494
|
+
if n % 6 == 3:
|
495
|
+
t = (n-3) // 6
|
496
|
+
Z = list(range(2 * t + 1))
|
497
|
+
|
498
|
+
T = lambda x_y : x_y[0] + (2*t+1)*x_y[1]
|
499
|
+
|
500
|
+
sts = [[(i,0),(i,1),(i,2)] for i in Z] + \
|
501
|
+
[[(i,k),(j,k),(((t+1)*(i+j)) % (2*t+1),(k+1) % 3)] for k in range(3) for i in Z for j in Z if i != j]
|
502
|
+
|
503
|
+
elif n % 6 == 1:
|
504
|
+
|
505
|
+
t = (n-1) // 6
|
506
|
+
N = list(range(2 * t))
|
507
|
+
T = lambda x_y : x_y[0]+x_y[1]*t*2 if x_y != (-1,-1) else n-1
|
508
|
+
|
509
|
+
L1 = lambda i,j : (i+j) % ((n-1)//3)
|
510
|
+
L = lambda i,j : L1(i,j)//2 if L1(i,j) % 2 == 0 else t+(L1(i,j)-1)//2
|
511
|
+
|
512
|
+
sts = [[(i,0),(i,1),(i,2)] for i in range(t)] + \
|
513
|
+
[[(-1,-1),(i,k),(i-t,(k+1) % 3)] for i in range(t,2*t) for k in [0,1,2]] + \
|
514
|
+
[[(i,k),(j,k),(L(i,j),(k+1) % 3)] for k in [0,1,2] for i in N for j in N if i < j]
|
515
|
+
|
516
|
+
else:
|
517
|
+
raise EmptySetError("Steiner triple systems only exist for n = 1 mod 6 or n = 3 mod 6")
|
518
|
+
|
519
|
+
# apply T and remove duplicates
|
520
|
+
sts = set(frozenset(T(xx) for xx in x) for x in sts)
|
521
|
+
|
522
|
+
return BIBD(n, sts, name=name,check=False)
|
523
|
+
|
524
|
+
|
525
|
+
def BIBD_from_TD(v, k, existence=False):
|
526
|
+
r"""
|
527
|
+
Return a BIBD through TD-based constructions.
|
528
|
+
|
529
|
+
INPUT:
|
530
|
+
|
531
|
+
- ``v``, ``k`` -- integers; computes a `(v,k,1)`-BIBD
|
532
|
+
|
533
|
+
- ``existence`` -- boolean; instead of building the design, return:
|
534
|
+
|
535
|
+
- ``True`` -- meaning that Sage knows how to build the design
|
536
|
+
|
537
|
+
- ``Unknown`` -- meaning that Sage does not know how to build the
|
538
|
+
design, but that the design may exist (see :mod:`sage.misc.unknown`)
|
539
|
+
|
540
|
+
- ``False`` -- meaning that the design does not exist
|
541
|
+
|
542
|
+
This method implements three constructions:
|
543
|
+
|
544
|
+
- If there exists a `TD(k,v)` and a `(v,k,1)`-BIBD then there exists a
|
545
|
+
`(kv,k,1)`-BIBD.
|
546
|
+
|
547
|
+
The BIBD is obtained from all blocks of the `TD`, and from the blocks of
|
548
|
+
the `(v,k,1)`-BIBDs defined over the `k` groups of the `TD`.
|
549
|
+
|
550
|
+
- If there exists a `TD(k,v)` and a `(v+1,k,1)`-BIBD then there exists a
|
551
|
+
`(kv+1,k,1)`-BIBD.
|
552
|
+
|
553
|
+
The BIBD is obtained from all blocks of the `TD`, and from the blocks of
|
554
|
+
the `(v+1,k,1)`-BIBDs defined over the sets `V_1\cup \infty,\dots,V_k\cup
|
555
|
+
\infty` where the `V_1,\dots,V_k` are the groups of the TD.
|
556
|
+
|
557
|
+
- If there exists a `TD(k,v)` and a `(v+k,k,1)`-BIBD then there exists a
|
558
|
+
`(kv+k,k,1)`-BIBD.
|
559
|
+
|
560
|
+
The BIBD is obtained from all blocks of the `TD`, and from the blocks of
|
561
|
+
the `(v+k,k,1)`-BIBDs defined over the sets `V_1\cup
|
562
|
+
\{\infty_1,\dots,\infty_k\},\dots,V_k\cup \{\infty_1,\dots,\infty_k\}`
|
563
|
+
where the `V_1,\dots,V_k` are the groups of the TD. By making sure that
|
564
|
+
all copies of the `(v+k,k,1)`-BIBD contain the block
|
565
|
+
`\{\infty_1,\dots,\infty_k\}`, the result is also a BIBD.
|
566
|
+
|
567
|
+
These constructions can be found in
|
568
|
+
`<http://www.argilo.net/files/bibd.pdf>`_.
|
569
|
+
|
570
|
+
EXAMPLES:
|
571
|
+
|
572
|
+
First construction::
|
573
|
+
|
574
|
+
sage: from sage.combinat.designs.bibd import BIBD_from_TD
|
575
|
+
sage: BIBD_from_TD(25,5,existence=True) # needs sage.schemes
|
576
|
+
True
|
577
|
+
sage: _ = BlockDesign(25,BIBD_from_TD(25,5)) # needs sage.schemes
|
578
|
+
|
579
|
+
Second construction::
|
580
|
+
|
581
|
+
sage: from sage.combinat.designs.bibd import BIBD_from_TD
|
582
|
+
sage: BIBD_from_TD(21,5,existence=True) # needs sage.schemes
|
583
|
+
True
|
584
|
+
sage: _ = BlockDesign(21,BIBD_from_TD(21,5)) # needs sage.schemes
|
585
|
+
|
586
|
+
Third construction::
|
587
|
+
|
588
|
+
sage: from sage.combinat.designs.bibd import BIBD_from_TD
|
589
|
+
sage: BIBD_from_TD(85,5,existence=True) # needs sage.schemes
|
590
|
+
True
|
591
|
+
sage: _ = BlockDesign(85,BIBD_from_TD(85,5)) # needs sage.schemes
|
592
|
+
|
593
|
+
No idea::
|
594
|
+
|
595
|
+
sage: from sage.combinat.designs.bibd import BIBD_from_TD
|
596
|
+
sage: BIBD_from_TD(20,5,existence=True)
|
597
|
+
Unknown
|
598
|
+
sage: BIBD_from_TD(20,5)
|
599
|
+
Traceback (most recent call last):
|
600
|
+
...
|
601
|
+
NotImplementedError: I do not know how to build a (20,5,1)-BIBD!
|
602
|
+
"""
|
603
|
+
# First construction
|
604
|
+
if (v % k == 0 and
|
605
|
+
balanced_incomplete_block_design(v//k, k, existence=True) is True and
|
606
|
+
transversal_design(k, v//k, existence=True) is True):
|
607
|
+
|
608
|
+
if existence:
|
609
|
+
return True
|
610
|
+
|
611
|
+
v = v//k
|
612
|
+
BIBDvk = balanced_incomplete_block_design(v,k)._blocks
|
613
|
+
TDkv = transversal_design(k,v,check=False)
|
614
|
+
|
615
|
+
BIBD = TDkv._blocks
|
616
|
+
for i in range(k):
|
617
|
+
BIBD.extend([x+i*v for x in B] for B in BIBDvk)
|
618
|
+
|
619
|
+
# Second construction
|
620
|
+
elif ((v-1) % k == 0 and
|
621
|
+
balanced_incomplete_block_design((v-1)//k+1,k,existence=True) is True and
|
622
|
+
transversal_design(k,(v-1)//k,existence=True)) is True:
|
623
|
+
|
624
|
+
if existence:
|
625
|
+
return True
|
626
|
+
|
627
|
+
v = (v-1)//k
|
628
|
+
BIBDv1k = balanced_incomplete_block_design(v+1,k)._blocks
|
629
|
+
TDkv = transversal_design(k,v,check=False)._blocks
|
630
|
+
|
631
|
+
inf = v*k
|
632
|
+
BIBD = TDkv
|
633
|
+
for i in range(k):
|
634
|
+
BIBD.extend([inf if x == v else x+i*v for x in B] for B in BIBDv1k)
|
635
|
+
|
636
|
+
# Third construction
|
637
|
+
elif ((v-k) % k == 0 and
|
638
|
+
balanced_incomplete_block_design((v-k)//k+k,k,existence=True) is True
|
639
|
+
and transversal_design(k,(v-k)//k,existence=True) is True):
|
640
|
+
if existence:
|
641
|
+
return True
|
642
|
+
|
643
|
+
v = (v-k)//k
|
644
|
+
BIBDvpkk = balanced_incomplete_block_design(v+k,k)
|
645
|
+
TDkv = transversal_design(k,v,check=False)._blocks
|
646
|
+
inf = v*k
|
647
|
+
BIBD = TDkv
|
648
|
+
|
649
|
+
# makes sure that [v,...,v+k-1] is a block of BIBDvpkk. Then, we remove it.
|
650
|
+
BIBDvpkk = _relabel_bibd(BIBDvpkk,v+k)
|
651
|
+
BIBDvpkk = [B for B in BIBDvpkk if min(B) < v]
|
652
|
+
|
653
|
+
for i in range(k):
|
654
|
+
BIBD.extend([(x-v)+inf if x >= v else x+i*v for x in B]
|
655
|
+
for B in BIBDvpkk)
|
656
|
+
|
657
|
+
BIBD.append(list(range(k * v, v * k + k)))
|
658
|
+
|
659
|
+
# No idea ...
|
660
|
+
else:
|
661
|
+
if existence:
|
662
|
+
return Unknown
|
663
|
+
else:
|
664
|
+
raise NotImplementedError("I do not know how to build a ({},{},1)-BIBD!".format(v,k))
|
665
|
+
|
666
|
+
return BIBD
|
667
|
+
|
668
|
+
|
669
|
+
def BIBD_from_difference_family(G, D, lambd=None, check=True):
|
670
|
+
r"""
|
671
|
+
Return the BIBD associated to the difference family ``D`` on the group ``G``.
|
672
|
+
|
673
|
+
Let `G` be a group. A `(G,k,\lambda)`-*difference family* is a family `B =
|
674
|
+
\{B_1,B_2,\ldots,B_b\}` of `k`-subsets of `G` such that for each element of
|
675
|
+
`G \backslash \{0\}` there exists exactly `\lambda` pairs of elements
|
676
|
+
`(x,y)`, `x` and `y` belonging to the same block, such that `x - y = g` (or
|
677
|
+
x y^{-1} = g` in multiplicative notation).
|
678
|
+
|
679
|
+
If `\{B_1, B_2, \ldots, B_b\}` is a `(G,k,\lambda)`-difference family then
|
680
|
+
its set of translates `\{B_i \cdot g; i \in \{1,\ldots,b\}, g \in G\}` is a
|
681
|
+
`(v,k,\lambda)`-BIBD where `v` is the cardinality of `G`.
|
682
|
+
|
683
|
+
INPUT:
|
684
|
+
|
685
|
+
- ``G`` -- a finite additive Abelian group
|
686
|
+
|
687
|
+
- ``D`` -- a difference family on ``G`` (short blocks are allowed)
|
688
|
+
|
689
|
+
- ``lambd`` -- the `\lambda` parameter (optional, only used if ``check`` is
|
690
|
+
``True``)
|
691
|
+
|
692
|
+
- ``check`` -- boolean (default: ``True``); whether or not we check the output
|
693
|
+
|
694
|
+
EXAMPLES::
|
695
|
+
|
696
|
+
sage: G = Zmod(21)
|
697
|
+
sage: D = [[0,1,4,14,16]]
|
698
|
+
sage: sorted(G(x-y) for x in D[0] for y in D[0] if x != y)
|
699
|
+
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
|
700
|
+
|
701
|
+
sage: from sage.combinat.designs.bibd import BIBD_from_difference_family
|
702
|
+
sage: BIBD_from_difference_family(G, D)
|
703
|
+
[[0, 1, 4, 14, 16],
|
704
|
+
[1, 2, 5, 15, 17],
|
705
|
+
[2, 3, 6, 16, 18],
|
706
|
+
[3, 4, 7, 17, 19],
|
707
|
+
[4, 5, 8, 18, 20],
|
708
|
+
[5, 6, 9, 19, 0],
|
709
|
+
[6, 7, 10, 20, 1],
|
710
|
+
[7, 8, 11, 0, 2],
|
711
|
+
[8, 9, 12, 1, 3],
|
712
|
+
[9, 10, 13, 2, 4],
|
713
|
+
[10, 11, 14, 3, 5],
|
714
|
+
[11, 12, 15, 4, 6],
|
715
|
+
[12, 13, 16, 5, 7],
|
716
|
+
[13, 14, 17, 6, 8],
|
717
|
+
[14, 15, 18, 7, 9],
|
718
|
+
[15, 16, 19, 8, 10],
|
719
|
+
[16, 17, 20, 9, 11],
|
720
|
+
[17, 18, 0, 10, 12],
|
721
|
+
[18, 19, 1, 11, 13],
|
722
|
+
[19, 20, 2, 12, 14],
|
723
|
+
[20, 0, 3, 13, 15]]
|
724
|
+
"""
|
725
|
+
from .difference_family import group_law, block_stabilizer
|
726
|
+
identity, mul, inv = group_law(G)
|
727
|
+
bibd = []
|
728
|
+
Gset = set(G)
|
729
|
+
p_to_i = {g: i for i, g in enumerate(Gset)}
|
730
|
+
for b in D:
|
731
|
+
b = [G(w) for w in b]
|
732
|
+
S = block_stabilizer(G, b)
|
733
|
+
GG = Gset.copy()
|
734
|
+
while GG:
|
735
|
+
g = GG.pop()
|
736
|
+
if S:
|
737
|
+
GG.difference_update(mul(s,g) for s in S)
|
738
|
+
bibd.append([p_to_i[mul(i,g)] for i in b])
|
739
|
+
|
740
|
+
if check:
|
741
|
+
if lambd is None:
|
742
|
+
k = len(bibd[0])
|
743
|
+
v = G.cardinality()
|
744
|
+
lambd = (len(bibd) * k * (k-1)) // (v * (v-1))
|
745
|
+
assert is_pairwise_balanced_design(bibd, G.cardinality(), [len(D[0])], lambd=lambd)
|
746
|
+
|
747
|
+
return bibd
|
748
|
+
|
749
|
+
################
|
750
|
+
# (v,4,1)-BIBD #
|
751
|
+
################
|
752
|
+
|
753
|
+
|
754
|
+
def v_4_1_BIBD(v, check=True):
|
755
|
+
r"""
|
756
|
+
Return a `(v,4,1)`-BIBD.
|
757
|
+
|
758
|
+
A `(v,4,1)`-BIBD is an edge-decomposition of the complete graph `K_v` into
|
759
|
+
copies of `K_4`. For more information, see
|
760
|
+
:func:`balanced_incomplete_block_design`. It exists if and only if `v\equiv 1,4
|
761
|
+
\pmod {12}`.
|
762
|
+
|
763
|
+
See page 167 of [Stinson2004]_ for the construction details.
|
764
|
+
|
765
|
+
.. SEEALSO::
|
766
|
+
|
767
|
+
* :func:`balanced_incomplete_block_design`
|
768
|
+
|
769
|
+
INPUT:
|
770
|
+
|
771
|
+
- ``v`` -- integer; number of points
|
772
|
+
|
773
|
+
- ``check`` -- boolean (default: ``True``); whether to check that output is
|
774
|
+
correct before returning it. As this is expected to be useless, you may
|
775
|
+
want to disable it whenever you want speed.
|
776
|
+
|
777
|
+
EXAMPLES::
|
778
|
+
|
779
|
+
sage: from sage.combinat.designs.bibd import v_4_1_BIBD # long time
|
780
|
+
sage: for n in range(13,100): # long time
|
781
|
+
....: if n%12 in [1,4]:
|
782
|
+
....: _ = v_4_1_BIBD(n, check = True)
|
783
|
+
|
784
|
+
TESTS:
|
785
|
+
|
786
|
+
Check that the `(25,4)` and `(37,4)`-difference family are available::
|
787
|
+
|
788
|
+
sage: assert designs.difference_family(25,4,existence=True)
|
789
|
+
sage: _ = designs.difference_family(25,4)
|
790
|
+
sage: assert designs.difference_family(37,4,existence=True)
|
791
|
+
sage: _ = designs.difference_family(37,4)
|
792
|
+
|
793
|
+
Check some larger `(v,4,1)`-BIBD (see :issue:`17557`)::
|
794
|
+
|
795
|
+
sage: for v in range(400): # long time
|
796
|
+
....: if v%12 in [1,4]:
|
797
|
+
....: _ = designs.balanced_incomplete_block_design(v,4)
|
798
|
+
"""
|
799
|
+
k = 4
|
800
|
+
if v == 0:
|
801
|
+
return []
|
802
|
+
if v <= 12 or v % 12 not in [1,4]:
|
803
|
+
raise EmptySetError("A K_4-decomposition of K_v exists iif v=2,4 mod 12, v>12 or v==0")
|
804
|
+
|
805
|
+
# Step 1. Base cases.
|
806
|
+
if v == 13:
|
807
|
+
# note: this construction can also be obtained from difference_family
|
808
|
+
from .block_design import projective_plane
|
809
|
+
return projective_plane(3)._blocks
|
810
|
+
if v == 16:
|
811
|
+
from .block_design import AffineGeometryDesign
|
812
|
+
from sage.rings.finite_rings.finite_field_constructor import FiniteField
|
813
|
+
return AffineGeometryDesign(2,1,FiniteField(4,'x'))._blocks
|
814
|
+
if v == 25 or v == 37:
|
815
|
+
from .difference_family import difference_family
|
816
|
+
G,D = difference_family(v,4)
|
817
|
+
return BIBD_from_difference_family(G,D,check=False)
|
818
|
+
if v == 28:
|
819
|
+
return [[0, 1, 23, 26], [0, 2, 10, 11], [0, 3, 16, 18], [0, 4, 15, 20],
|
820
|
+
[0, 5, 8, 9], [0, 6, 22, 25], [0, 7, 14, 21], [0, 12, 17, 27],
|
821
|
+
[0, 13, 19, 24], [1, 2, 24, 27], [1, 3, 11, 12], [1, 4, 17, 19],
|
822
|
+
[1, 5, 14, 16], [1, 6, 9, 10], [1, 7, 20, 25], [1, 8, 15, 22],
|
823
|
+
[1, 13, 18, 21], [2, 3, 21, 25], [2, 4, 12, 13], [2, 5, 18, 20],
|
824
|
+
[2, 6, 15, 17], [2, 7, 19, 22], [2, 8, 14, 26], [2, 9, 16, 23],
|
825
|
+
[3, 4, 22, 26], [3, 5, 7, 13], [3, 6, 14, 19], [3, 8, 20, 23],
|
826
|
+
[3, 9, 15, 27], [3, 10, 17, 24], [4, 5, 23, 27], [4, 6, 7, 8],
|
827
|
+
[4, 9, 14, 24], [4, 10, 16, 21], [4, 11, 18, 25], [5, 6, 21, 24],
|
828
|
+
[5, 10, 15, 25], [5, 11, 17, 22], [5, 12, 19, 26], [6, 11, 16, 26],
|
829
|
+
[6, 12, 18, 23], [6, 13, 20, 27], [7, 9, 17, 18], [7, 10, 26, 27],
|
830
|
+
[7, 11, 23, 24], [7, 12, 15, 16], [8, 10, 18, 19], [8, 11, 21, 27],
|
831
|
+
[8, 12, 24, 25], [8, 13, 16, 17], [9, 11, 19, 20], [9, 12, 21, 22],
|
832
|
+
[9, 13, 25, 26], [10, 12, 14, 20], [10, 13, 22, 23], [11, 13, 14, 15],
|
833
|
+
[14, 17, 23, 25], [14, 18, 22, 27], [15, 18, 24, 26], [15, 19, 21, 23],
|
834
|
+
[16, 19, 25, 27], [16, 20, 22, 24], [17, 20, 21, 26]]
|
835
|
+
|
836
|
+
# Step 2 : this is function PBD_4_5_8_9_12
|
837
|
+
PBD = PBD_4_5_8_9_12((v-1)//(k-1),check=False)
|
838
|
+
|
839
|
+
# Step 3 : Theorem 7.20
|
840
|
+
bibd = BIBD_from_PBD(PBD,v,k,check=False)
|
841
|
+
|
842
|
+
if check:
|
843
|
+
assert is_pairwise_balanced_design(bibd,v,[k])
|
844
|
+
|
845
|
+
return bibd
|
846
|
+
|
847
|
+
|
848
|
+
def BIBD_from_PBD(PBD, v, k, check=True, base_cases=None):
|
849
|
+
r"""
|
850
|
+
Return a `(v,k,1)`-BIBD from a `(r,K)`-PBD where `r=(v-1)/(k-1)`.
|
851
|
+
|
852
|
+
This is Theorem 7.20 from [Stinson2004]_.
|
853
|
+
|
854
|
+
INPUT:
|
855
|
+
|
856
|
+
- ``v``, ``k`` -- integers
|
857
|
+
|
858
|
+
- ``PBD`` -- a PBD on `r=(v-1)/(k-1)` points, such that for any block of
|
859
|
+
``PBD`` of size `s` there must exist a `((k-1)s+1,k,1)`-BIBD
|
860
|
+
|
861
|
+
- ``check`` -- boolean (default: ``True``); whether to check that output is
|
862
|
+
correct before returning it. As this is expected to be useless, you may
|
863
|
+
want to disable it whenever you want speed.
|
864
|
+
|
865
|
+
- ``base_cases`` -- caching system, for internal use
|
866
|
+
|
867
|
+
EXAMPLES::
|
868
|
+
|
869
|
+
sage: from sage.combinat.designs.bibd import PBD_4_5_8_9_12
|
870
|
+
sage: from sage.combinat.designs.bibd import BIBD_from_PBD
|
871
|
+
sage: from sage.combinat.designs.bibd import is_pairwise_balanced_design
|
872
|
+
sage: PBD = PBD_4_5_8_9_12(17) # needs sage.schemes
|
873
|
+
sage: bibd = is_pairwise_balanced_design(BIBD_from_PBD(PBD,52,4),52,[4]) # needs sage.schemes
|
874
|
+
"""
|
875
|
+
if base_cases is None:
|
876
|
+
base_cases = {}
|
877
|
+
r = (v-1) // (k-1)
|
878
|
+
bibd = []
|
879
|
+
for X in PBD:
|
880
|
+
n = len(X)
|
881
|
+
N = (k-1)*n+1
|
882
|
+
if (n,k) not in base_cases:
|
883
|
+
base_cases[n,k] = _relabel_bibd(balanced_incomplete_block_design(N,k), N)
|
884
|
+
|
885
|
+
for XX in base_cases[n,k]:
|
886
|
+
if N-1 in XX:
|
887
|
+
continue
|
888
|
+
bibd.append([X[x//(k-1)] + (x % (k-1))*r for x in XX])
|
889
|
+
|
890
|
+
for x in range(r):
|
891
|
+
bibd.append([x+i*r for i in range(k-1)]+[v-1])
|
892
|
+
|
893
|
+
if check:
|
894
|
+
assert is_pairwise_balanced_design(bibd,v,[k])
|
895
|
+
|
896
|
+
return bibd
|
897
|
+
|
898
|
+
|
899
|
+
def _relabel_bibd(B, n, p=None):
|
900
|
+
r"""
|
901
|
+
Relabel the BIBD on `n` points and blocks of size k such that
|
902
|
+
`\{0,...,k-2,n-1\},\{k-1,...,2k-3,n-1\},...,\{n-k,...,n-2,n-1\}` are blocks
|
903
|
+
of the BIBD.
|
904
|
+
|
905
|
+
INPUT:
|
906
|
+
|
907
|
+
- ``B`` -- list of blocks
|
908
|
+
|
909
|
+
- ``n`` -- integer; number of points
|
910
|
+
|
911
|
+
- ``p`` -- (optional) the point that will be labeled with `n-1`
|
912
|
+
|
913
|
+
EXAMPLES::
|
914
|
+
|
915
|
+
sage: designs.balanced_incomplete_block_design(40,4).blocks() # indirect doctest # needs sage.schemes
|
916
|
+
[[0, 1, 2, 12], [0, 3, 6, 9], [0, 4, 8, 10],
|
917
|
+
[0, 5, 7, 11], [0, 13, 26, 39], [0, 14, 25, 28],
|
918
|
+
[0, 15, 27, 38], [0, 16, 22, 32], [0, 17, 23, 34],
|
919
|
+
...
|
920
|
+
"""
|
921
|
+
if p is None:
|
922
|
+
p = n-1
|
923
|
+
found = 0
|
924
|
+
last = n-1
|
925
|
+
d = {}
|
926
|
+
for X in B:
|
927
|
+
if last in X:
|
928
|
+
for x in X:
|
929
|
+
if x == last:
|
930
|
+
continue
|
931
|
+
d[x] = found
|
932
|
+
found += 1
|
933
|
+
if found == n-1:
|
934
|
+
break
|
935
|
+
d[p] = n-1
|
936
|
+
return [[d[x] for x in X] for X in B]
|
937
|
+
|
938
|
+
|
939
|
+
def PBD_4_5_8_9_12(v, check=True):
|
940
|
+
r"""
|
941
|
+
Return a `(v,\{4,5,8,9,12\})`-PBD on `v` elements.
|
942
|
+
|
943
|
+
A `(v,\{4,5,8,9,12\})`-PBD exists if and only if `v\equiv 0,1 \pmod 4`. The
|
944
|
+
construction implemented here appears page 168 in [Stinson2004]_.
|
945
|
+
|
946
|
+
INPUT:
|
947
|
+
|
948
|
+
- ``v`` -- integer congruent to `0` or `1` modulo `4`
|
949
|
+
|
950
|
+
- ``check`` -- boolean (default: ``True``); whether to check that output is
|
951
|
+
correct before returning it. As this is expected to be useless, you may
|
952
|
+
want to disable it whenever you want speed.
|
953
|
+
|
954
|
+
EXAMPLES::
|
955
|
+
|
956
|
+
sage: designs.balanced_incomplete_block_design(40,4).blocks() # indirect doctest # needs sage.schemes
|
957
|
+
[[0, 1, 2, 12], [0, 3, 6, 9], [0, 4, 8, 10],
|
958
|
+
[0, 5, 7, 11], [0, 13, 26, 39], [0, 14, 25, 28],
|
959
|
+
[0, 15, 27, 38], [0, 16, 22, 32], [0, 17, 23, 34],
|
960
|
+
...
|
961
|
+
|
962
|
+
Check that :issue:`16476` is fixed::
|
963
|
+
|
964
|
+
sage: from sage.combinat.designs.bibd import PBD_4_5_8_9_12
|
965
|
+
sage: for v in (0,1,4,5,8,9,12,13,16,17,20,21,24,25): # needs sage.schemes
|
966
|
+
....: _ = PBD_4_5_8_9_12(v)
|
967
|
+
"""
|
968
|
+
if v % 4 not in [0, 1]:
|
969
|
+
raise ValueError
|
970
|
+
if v <= 1:
|
971
|
+
PBD = []
|
972
|
+
elif v <= 12:
|
973
|
+
PBD = [list(range(v))]
|
974
|
+
elif v == 13 or v == 28:
|
975
|
+
PBD = v_4_1_BIBD(v, check=False)
|
976
|
+
elif v == 29:
|
977
|
+
TD47 = transversal_design(4,7)._blocks
|
978
|
+
four_more_sets = [[28]+[i*7+j for j in range(7)] for i in range(4)]
|
979
|
+
PBD = TD47 + four_more_sets
|
980
|
+
elif v == 41:
|
981
|
+
TD59 = transversal_design(5,9)
|
982
|
+
PBD = ([[x for x in X if x < 41] for X in TD59]
|
983
|
+
+ [[i*9+j for j in range(9)] for i in range(4)]
|
984
|
+
+ [[36,37,38,39,40]])
|
985
|
+
elif v == 44:
|
986
|
+
TD59 = transversal_design(5,9)
|
987
|
+
PBD = ([[x for x in X if x < 44] for X in TD59]
|
988
|
+
+ [[i*9+j for j in range(9)] for i in range(4)]
|
989
|
+
+ [[36,37,38,39,40,41,42,43]])
|
990
|
+
elif v == 45:
|
991
|
+
TD59 = transversal_design(5,9)._blocks
|
992
|
+
PBD = (TD59+[[i*9+j for j in range(9)] for i in range(5)])
|
993
|
+
elif v == 48:
|
994
|
+
TD4_12 = transversal_design(4,12)._blocks
|
995
|
+
PBD = (TD4_12+[[i*12+j for j in range(12)] for i in range(4)])
|
996
|
+
elif v == 49:
|
997
|
+
# Lemma 7.16 : A (49,{4,13})-PBD
|
998
|
+
TD4_12 = transversal_design(4,12)._blocks
|
999
|
+
|
1000
|
+
# Replacing the block of size 13 with a BIBD
|
1001
|
+
BIBD_13_4 = v_4_1_BIBD(13)
|
1002
|
+
for i in range(4):
|
1003
|
+
for B in BIBD_13_4:
|
1004
|
+
TD4_12.append([i*12+x if x != 12 else 48
|
1005
|
+
for x in B])
|
1006
|
+
|
1007
|
+
PBD = TD4_12
|
1008
|
+
else:
|
1009
|
+
t,u = _get_t_u(v)
|
1010
|
+
TD = transversal_design(5,t)
|
1011
|
+
TD = [[x for x in X if x < 4*t+u] for X in TD]
|
1012
|
+
for B in [list(range(t*i,t*(i+1))) for i in range(4)]:
|
1013
|
+
TD.extend(_PBD_4_5_8_9_12_closure([B]))
|
1014
|
+
|
1015
|
+
if u > 1:
|
1016
|
+
TD.extend(_PBD_4_5_8_9_12_closure([list(range(4*t,4*t+u))]))
|
1017
|
+
|
1018
|
+
PBD = TD
|
1019
|
+
|
1020
|
+
if check:
|
1021
|
+
assert is_pairwise_balanced_design(PBD,v,[4,5,8,9,12])
|
1022
|
+
|
1023
|
+
return PBD
|
1024
|
+
|
1025
|
+
|
1026
|
+
def _PBD_4_5_8_9_12_closure(B):
|
1027
|
+
r"""
|
1028
|
+
Makes sure all blocks of `B` have size in `\{4,5,8,9,12\}`.
|
1029
|
+
|
1030
|
+
This is a helper function for :func:`PBD_4_5_8_9_12`. Given that
|
1031
|
+
`\{4,5,8,9,12\}` is PBD-closed, any block of size not in `\{4,5,8,9,12\}`
|
1032
|
+
can be decomposed further.
|
1033
|
+
|
1034
|
+
EXAMPLES::
|
1035
|
+
|
1036
|
+
sage: designs.balanced_incomplete_block_design(40,4).blocks() # indirect doctest # needs sage.schemes
|
1037
|
+
[[0, 1, 2, 12], [0, 3, 6, 9], [0, 4, 8, 10],
|
1038
|
+
[0, 5, 7, 11], [0, 13, 26, 39], [0, 14, 25, 28],
|
1039
|
+
[0, 15, 27, 38], [0, 16, 22, 32], [0, 17, 23, 34],
|
1040
|
+
...
|
1041
|
+
"""
|
1042
|
+
BB = []
|
1043
|
+
for X in B:
|
1044
|
+
if len(X) not in [4,5,8,9,12]:
|
1045
|
+
PBD = PBD_4_5_8_9_12(len(X), check=False)
|
1046
|
+
X = [[X[i] for i in XX] for XX in PBD]
|
1047
|
+
BB.extend(X)
|
1048
|
+
else:
|
1049
|
+
BB.append(X)
|
1050
|
+
return BB
|
1051
|
+
|
1052
|
+
|
1053
|
+
table_7_1 = {
|
1054
|
+
0:{'t':-4,'u':16,'s':2},
|
1055
|
+
1:{'t':-4,'u':17,'s':2},
|
1056
|
+
4:{'t':1,'u':0,'s':1},
|
1057
|
+
5:{'t':1,'u':1,'s':1},
|
1058
|
+
8:{'t':1,'u':4,'s':1},
|
1059
|
+
9:{'t':1,'u':5,'s':1},
|
1060
|
+
12:{'t':1,'u':8,'s':1},
|
1061
|
+
13:{'t':1,'u':9,'s':1},
|
1062
|
+
16:{'t':4,'u':0,'s':0},
|
1063
|
+
17:{'t':4,'u':1,'s':0},
|
1064
|
+
20:{'t':5,'u':0,'s':0},
|
1065
|
+
21:{'t':5,'u':1,'s':0},
|
1066
|
+
24:{'t':5,'u':4,'s':0},
|
1067
|
+
25:{'t':5,'u':5,'s':0},
|
1068
|
+
28:{'t':5,'u':8,'s':1},
|
1069
|
+
29:{'t':5,'u':9,'s':1},
|
1070
|
+
32:{'t':8,'u':0,'s':0},
|
1071
|
+
33:{'t':8,'u':1,'s':0},
|
1072
|
+
36:{'t':8,'u':4,'s':0},
|
1073
|
+
37:{'t':8,'u':5,'s':0},
|
1074
|
+
40:{'t':8,'u':8,'s':0},
|
1075
|
+
41:{'t':8,'u':9,'s':1},
|
1076
|
+
44:{'t':8,'u':12,'s':1},
|
1077
|
+
45:{'t':8,'u':13,'s':1},
|
1078
|
+
}
|
1079
|
+
|
1080
|
+
|
1081
|
+
def _get_t_u(v):
|
1082
|
+
r"""
|
1083
|
+
Return the parameters of table 7.1 from [Stinson2004]_.
|
1084
|
+
|
1085
|
+
INPUT:
|
1086
|
+
|
1087
|
+
- ``v`` -- integer
|
1088
|
+
|
1089
|
+
EXAMPLES::
|
1090
|
+
|
1091
|
+
sage: from sage.combinat.designs.bibd import _get_t_u
|
1092
|
+
sage: _get_t_u(20)
|
1093
|
+
(5, 0)
|
1094
|
+
"""
|
1095
|
+
# Table 7.1
|
1096
|
+
v = int(v)
|
1097
|
+
global table_7_1
|
1098
|
+
d = table_7_1[v % 48]
|
1099
|
+
s = v//48
|
1100
|
+
if s < d['s']:
|
1101
|
+
raise RuntimeError("This should not have happened.")
|
1102
|
+
t = 12*s+d['t']
|
1103
|
+
u = d['u']
|
1104
|
+
return t,u
|
1105
|
+
|
1106
|
+
################
|
1107
|
+
# (v,5,1)-BIBD #
|
1108
|
+
################
|
1109
|
+
|
1110
|
+
|
1111
|
+
def v_5_1_BIBD(v, check=True):
|
1112
|
+
r"""
|
1113
|
+
Return a `(v,5,1)`-BIBD.
|
1114
|
+
|
1115
|
+
This method follows the construction from [ClaytonSmith]_.
|
1116
|
+
|
1117
|
+
INPUT:
|
1118
|
+
|
1119
|
+
- ``v`` -- integer
|
1120
|
+
|
1121
|
+
.. SEEALSO::
|
1122
|
+
|
1123
|
+
* :func:`balanced_incomplete_block_design`
|
1124
|
+
|
1125
|
+
EXAMPLES::
|
1126
|
+
|
1127
|
+
sage: from sage.combinat.designs.bibd import v_5_1_BIBD
|
1128
|
+
sage: i = 0
|
1129
|
+
sage: while i<200: # needs sage.libs.pari sage.schemes
|
1130
|
+
....: i += 20
|
1131
|
+
....: _ = v_5_1_BIBD(i+1)
|
1132
|
+
....: _ = v_5_1_BIBD(i+5)
|
1133
|
+
|
1134
|
+
TESTS:
|
1135
|
+
|
1136
|
+
Check that the needed difference families are there::
|
1137
|
+
|
1138
|
+
sage: for v in [21,41,61,81,141,161,281]: # needs sage.libs.pari
|
1139
|
+
....: assert designs.difference_family(v,5,existence=True)
|
1140
|
+
....: _ = designs.difference_family(v,5)
|
1141
|
+
"""
|
1142
|
+
v = int(v)
|
1143
|
+
|
1144
|
+
assert (v > 1)
|
1145
|
+
assert (v % 20 == 5 or v % 20 == 1) # note: equivalent to (v-1)%4 == 0 and (v*(v-1))%20 == 0
|
1146
|
+
|
1147
|
+
# Lemma 27
|
1148
|
+
if v % 5 == 0 and (v//5) % 4 == 1 and is_prime_power(v//5):
|
1149
|
+
bibd = BIBD_5q_5_for_q_prime_power(v//5)
|
1150
|
+
# Lemma 28
|
1151
|
+
elif v in [21,41,61,81,141,161,281]:
|
1152
|
+
from .difference_family import difference_family
|
1153
|
+
G,D = difference_family(v,5)
|
1154
|
+
bibd = BIBD_from_difference_family(G, D, check=False)
|
1155
|
+
# Lemma 29
|
1156
|
+
elif v == 165:
|
1157
|
+
bibd = BIBD_from_PBD(v_5_1_BIBD(41,check=False),165,5,check=False)
|
1158
|
+
elif v == 181:
|
1159
|
+
bibd = BIBD_from_PBD(v_5_1_BIBD(45,check=False),181,5,check=False)
|
1160
|
+
elif v in (201,285,301,401,421,425):
|
1161
|
+
# Call directly the BIBD_from_TD function
|
1162
|
+
# note: there are (201,5,1) and (421,5)-difference families that can be
|
1163
|
+
# obtained from the general constructor
|
1164
|
+
bibd = BIBD_from_TD(v,5)
|
1165
|
+
# Theorem 31.2
|
1166
|
+
elif (v-1)//4 in [80, 81, 85, 86, 90, 91, 95, 96, 110, 111, 115, 116, 120, 121, 250, 251, 255, 256, 260, 261, 265, 266, 270, 271]:
|
1167
|
+
r = (v-1)//4
|
1168
|
+
if r <= 96:
|
1169
|
+
k,t,u = 5, 16, r-80
|
1170
|
+
elif r <= 121:
|
1171
|
+
k,t,u = 10, 11, r-110
|
1172
|
+
else:
|
1173
|
+
k,t,u = 10, 25, r-250
|
1174
|
+
bibd = BIBD_from_PBD(PBD_from_TD(k,t,u),v,5,check=False)
|
1175
|
+
|
1176
|
+
else:
|
1177
|
+
r,s,t,u = _get_r_s_t_u(v)
|
1178
|
+
bibd = BIBD_from_PBD(PBD_from_TD(5,t,u),v,5,check=False)
|
1179
|
+
|
1180
|
+
if check:
|
1181
|
+
assert is_pairwise_balanced_design(bibd,v,[5])
|
1182
|
+
|
1183
|
+
return bibd
|
1184
|
+
|
1185
|
+
|
1186
|
+
def _get_r_s_t_u(v):
|
1187
|
+
r"""
|
1188
|
+
Implement the table from [ClaytonSmith]_.
|
1189
|
+
|
1190
|
+
Return the parameters ``r,s,t,u`` associated with an integer ``v``.
|
1191
|
+
|
1192
|
+
INPUT:
|
1193
|
+
|
1194
|
+
- ``v`` -- integer
|
1195
|
+
|
1196
|
+
EXAMPLES::
|
1197
|
+
|
1198
|
+
sage: from sage.combinat.designs.bibd import _get_r_s_t_u
|
1199
|
+
sage: _get_r_s_t_u(25)
|
1200
|
+
(6, 0, 1, 1)
|
1201
|
+
"""
|
1202
|
+
r = int((v-1)/4)
|
1203
|
+
s = r//150
|
1204
|
+
x = r % 150
|
1205
|
+
|
1206
|
+
if x == 0:
|
1207
|
+
t,u = 30*s-5, 25
|
1208
|
+
elif x == 1:
|
1209
|
+
t,u = 30*s-5, 26
|
1210
|
+
elif x <= 21:
|
1211
|
+
t,u = 30*s+1, x-5
|
1212
|
+
elif x == 25:
|
1213
|
+
t,u = 30*s+5, 0
|
1214
|
+
elif x == 26:
|
1215
|
+
t,u = 30*s+5, 1
|
1216
|
+
elif x == 30:
|
1217
|
+
t,u = 30*s+5, 5
|
1218
|
+
elif x <= 51:
|
1219
|
+
t,u = 30*s+5, x-25
|
1220
|
+
elif x <= 121:
|
1221
|
+
t,u = 30*s+11, x-55
|
1222
|
+
elif x <= 146:
|
1223
|
+
t,u = 30*s+25, x-125
|
1224
|
+
|
1225
|
+
return r,s,t,u
|
1226
|
+
|
1227
|
+
|
1228
|
+
def PBD_from_TD(k, t, u):
|
1229
|
+
r"""
|
1230
|
+
Return a `(kt,\{k,t\})`-PBD if `u=0` and a `(kt+u,\{k,k+1,t,u\})`-PBD otherwise.
|
1231
|
+
|
1232
|
+
This is theorem 23 from [ClaytonSmith]_. The PBD is obtained from the blocks
|
1233
|
+
a truncated `TD(k+1,t)`, to which are added the blocks corresponding to the
|
1234
|
+
groups of the TD. When `u=0`, a `TD(k,t)` is used instead.
|
1235
|
+
|
1236
|
+
INPUT:
|
1237
|
+
|
1238
|
+
- ``k``, ``t``, ``u`` -- integers such that `0\leq u \leq t`
|
1239
|
+
|
1240
|
+
EXAMPLES::
|
1241
|
+
|
1242
|
+
sage: from sage.combinat.designs.bibd import PBD_from_TD
|
1243
|
+
sage: from sage.combinat.designs.bibd import is_pairwise_balanced_design
|
1244
|
+
sage: PBD = PBD_from_TD(2,2,1); PBD
|
1245
|
+
[[0, 2, 4], [0, 3], [1, 2], [1, 3, 4], [0, 1], [2, 3]]
|
1246
|
+
sage: is_pairwise_balanced_design(PBD,2*2+1,[2,3])
|
1247
|
+
True
|
1248
|
+
"""
|
1249
|
+
from .orthogonal_arrays import transversal_design
|
1250
|
+
TD = transversal_design(k+bool(u),t, check=False)
|
1251
|
+
TD = [[x for x in X if x < k*t+u] for X in TD]
|
1252
|
+
for i in range(k):
|
1253
|
+
TD.append(list(range(t*i,t*i+t)))
|
1254
|
+
if u >= 2:
|
1255
|
+
TD.append(list(range(k*t,k*t+u)))
|
1256
|
+
return TD
|
1257
|
+
|
1258
|
+
|
1259
|
+
def BIBD_5q_5_for_q_prime_power(q):
|
1260
|
+
r"""
|
1261
|
+
Return a `(5q,5,1)`-BIBD with `q\equiv 1\pmod 4` a prime power.
|
1262
|
+
|
1263
|
+
See Theorem 24 [ClaytonSmith]_.
|
1264
|
+
|
1265
|
+
INPUT:
|
1266
|
+
|
1267
|
+
- ``q`` -- integer; a prime power such that `q\equiv 1\pmod 4`
|
1268
|
+
|
1269
|
+
EXAMPLES::
|
1270
|
+
|
1271
|
+
sage: from sage.combinat.designs.bibd import BIBD_5q_5_for_q_prime_power
|
1272
|
+
sage: for q in [25, 45, 65, 85, 125, 145, 185, 205, 305, 405, 605]: # long time
|
1273
|
+
....: _ = BIBD_5q_5_for_q_prime_power(q/5)
|
1274
|
+
"""
|
1275
|
+
from sage.rings.finite_rings.finite_field_constructor import FiniteField
|
1276
|
+
|
1277
|
+
if q % 4 != 1 or not is_prime_power(q):
|
1278
|
+
raise ValueError("q is not a prime power or q%4!=1.")
|
1279
|
+
|
1280
|
+
d = (q-1)//4
|
1281
|
+
B = []
|
1282
|
+
F = FiniteField(q, 'x')
|
1283
|
+
a = F.primitive_element()
|
1284
|
+
L = {b: i for i, b in enumerate(F)}
|
1285
|
+
for b, Lb in L.items():
|
1286
|
+
B.append([i*q + Lb for i in range(5)])
|
1287
|
+
for i in range(5):
|
1288
|
+
for j in range(d):
|
1289
|
+
B.append([ i*q + Lb,
|
1290
|
+
((i+1) % 5)*q + L[ a**j+b ],
|
1291
|
+
((i+1) % 5)*q + L[-a**j+b ],
|
1292
|
+
((i+4) % 5)*q + L[ a**(j+d)+b],
|
1293
|
+
((i+4) % 5)*q + L[-a**(j+d)+b],
|
1294
|
+
])
|
1295
|
+
|
1296
|
+
return B
|
1297
|
+
|
1298
|
+
|
1299
|
+
def BIBD_from_arc_in_desarguesian_projective_plane(n, k, existence=False):
|
1300
|
+
r"""
|
1301
|
+
Return a `(n,k,1)`-BIBD from a maximal arc in a projective plane.
|
1302
|
+
|
1303
|
+
This function implements a construction from Denniston [Denniston69]_, who
|
1304
|
+
describes a maximal :meth:`arc
|
1305
|
+
<sage.combinat.designs.bibd.BalancedIncompleteBlockDesign.arc>` in a
|
1306
|
+
:func:`Desarguesian Projective Plane
|
1307
|
+
<sage.combinat.designs.block_design.DesarguesianProjectivePlaneDesign>` of
|
1308
|
+
order `2^k`. From two powers of two `n,q` with `n<q`, it produces a
|
1309
|
+
`((n-1)(q+1)+1,n,1)`-BIBD.
|
1310
|
+
|
1311
|
+
INPUT:
|
1312
|
+
|
1313
|
+
- ``n``, ``k`` -- integers; must be powers of two (among other restrictions)
|
1314
|
+
|
1315
|
+
- ``existence`` -- boolean; whether to return the BIBD obtained through
|
1316
|
+
this construction (default), or to merely indicate with a boolean return
|
1317
|
+
value whether this method *can* build the requested BIBD.
|
1318
|
+
|
1319
|
+
EXAMPLES:
|
1320
|
+
|
1321
|
+
A `(232,8,1)`-BIBD::
|
1322
|
+
|
1323
|
+
sage: from sage.combinat.designs.bibd import BIBD_from_arc_in_desarguesian_projective_plane
|
1324
|
+
sage: from sage.combinat.designs.bibd import BalancedIncompleteBlockDesign
|
1325
|
+
sage: D = BIBD_from_arc_in_desarguesian_projective_plane(232,8) # needs sage.libs.gap sage.modules sage.rings.finite_rings
|
1326
|
+
sage: BalancedIncompleteBlockDesign(232,D) # needs sage.libs.gap sage.modules sage.rings.finite_rings
|
1327
|
+
(232,8,1)-Balanced Incomplete Block Design
|
1328
|
+
|
1329
|
+
A `(120,8,1)`-BIBD::
|
1330
|
+
|
1331
|
+
sage: D = BIBD_from_arc_in_desarguesian_projective_plane(120,8) # needs sage.libs.gap sage.modules sage.rings.finite_rings
|
1332
|
+
sage: BalancedIncompleteBlockDesign(120,D) # needs sage.libs.gap sage.modules sage.rings.finite_rings
|
1333
|
+
(120,8,1)-Balanced Incomplete Block Design
|
1334
|
+
|
1335
|
+
Other parameters::
|
1336
|
+
|
1337
|
+
sage: all(BIBD_from_arc_in_desarguesian_projective_plane(n,k,existence=True)
|
1338
|
+
....: for n,k in
|
1339
|
+
....: [(120, 8), (232, 8), (456, 8), (904, 8), (496, 16),
|
1340
|
+
....: (976, 16), (1936, 16), (2016, 32), (4000, 32), (8128, 64)])
|
1341
|
+
True
|
1342
|
+
|
1343
|
+
Of course, not all can be built this way::
|
1344
|
+
|
1345
|
+
sage: BIBD_from_arc_in_desarguesian_projective_plane(7,3,existence=True)
|
1346
|
+
False
|
1347
|
+
sage: BIBD_from_arc_in_desarguesian_projective_plane(7,3)
|
1348
|
+
Traceback (most recent call last):
|
1349
|
+
...
|
1350
|
+
ValueError: This function cannot produce a (7,3,1)-BIBD
|
1351
|
+
|
1352
|
+
REFERENCE:
|
1353
|
+
|
1354
|
+
.. [Denniston69] \R. H. F. Denniston,
|
1355
|
+
Some maximal arcs in finite projective planes.
|
1356
|
+
Journal of Combinatorial Theory 6, no. 3 (1969): 317-319.
|
1357
|
+
:doi:`10.1016/S0021-9800(69)80095-5`
|
1358
|
+
"""
|
1359
|
+
q = (n-1)//(k-1)-1
|
1360
|
+
if (k % 2 or
|
1361
|
+
q % 2 or
|
1362
|
+
q <= k or
|
1363
|
+
n != (k-1)*(q+1)+1 or
|
1364
|
+
not is_prime_power(k) or
|
1365
|
+
not is_prime_power(q)):
|
1366
|
+
if existence:
|
1367
|
+
return False
|
1368
|
+
raise ValueError("This function cannot produce a ({},{},1)-BIBD".format(n,k))
|
1369
|
+
|
1370
|
+
if existence:
|
1371
|
+
return True
|
1372
|
+
|
1373
|
+
n = k
|
1374
|
+
|
1375
|
+
# From now on, the code assumes the notations of [Denniston69] for n,q, so
|
1376
|
+
# that the BIBD returned by the method will have the requested parameters.
|
1377
|
+
|
1378
|
+
from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF
|
1379
|
+
from sage.libs.gap.libgap import libgap
|
1380
|
+
from sage.matrix.constructor import Matrix
|
1381
|
+
|
1382
|
+
K = GF(q,'a')
|
1383
|
+
one = K.one()
|
1384
|
+
|
1385
|
+
# An irreducible quadratic form over K[X,Y]
|
1386
|
+
GO = libgap.GeneralOrthogonalGroup(-1,2,q)
|
1387
|
+
M = libgap.InvariantQuadraticForm(GO)['matrix']
|
1388
|
+
M = Matrix(M)
|
1389
|
+
M = M.change_ring(K)
|
1390
|
+
Q = lambda xx,yy : M[0,0]*xx**2+(M[0,1]+M[1,0])*xx*yy+M[1,1]*yy**2
|
1391
|
+
|
1392
|
+
# Here, the additive subgroup H (of order n) of K mentioned in
|
1393
|
+
# [Denniston69] is the set of all elements of K of degree < log_n
|
1394
|
+
# (seeing elements of K as polynomials in 'a')
|
1395
|
+
|
1396
|
+
K_iter = list(K) # faster iterations
|
1397
|
+
log_n = is_prime_power(n, get_data=True)[1]
|
1398
|
+
C = [(x, y, one) for x in K_iter for y in K_iter
|
1399
|
+
if Q(x, y).polynomial().degree() < log_n]
|
1400
|
+
|
1401
|
+
from sage.combinat.designs.block_design import DesarguesianProjectivePlaneDesign
|
1402
|
+
return DesarguesianProjectivePlaneDesign(q).trace(C)._blocks
|
1403
|
+
|
1404
|
+
|
1405
|
+
class PairwiseBalancedDesign(GroupDivisibleDesign):
|
1406
|
+
r"""
|
1407
|
+
Pairwise Balanced Design (PBD).
|
1408
|
+
|
1409
|
+
A Pairwise Balanced Design, or `(v,K,\lambda)`-PBD, is a collection
|
1410
|
+
`\mathcal B` of blocks defined on a set `X` of size `v`, such that any block
|
1411
|
+
pair of points `p_1,p_2\in X` occurs in exactly `\lambda` blocks of
|
1412
|
+
`\mathcal B`. Besides, for every block `B\in \mathcal B` we must have
|
1413
|
+
`|B|\in K`.
|
1414
|
+
|
1415
|
+
INPUT:
|
1416
|
+
|
1417
|
+
- ``points`` -- the underlying set; if ``points`` is an integer `v`, then
|
1418
|
+
the set is considered to be `\{0, ..., v-1\}`
|
1419
|
+
|
1420
|
+
- ``blocks`` -- collection of blocks
|
1421
|
+
|
1422
|
+
- ``K`` -- list of integers of which the sizes of the blocks must be
|
1423
|
+
elements; set to ``None`` (automatic guess) by default
|
1424
|
+
|
1425
|
+
- ``lambd`` -- integer; value of `\lambda`, set to `1` by default
|
1426
|
+
|
1427
|
+
- ``check`` -- boolean; whether to check that the design is a `PBD` with
|
1428
|
+
the right parameters
|
1429
|
+
|
1430
|
+
- ``copy`` -- (use with caution) if set to ``False`` then ``blocks`` must be
|
1431
|
+
a list of lists of integers. The list will not be copied but will be
|
1432
|
+
modified in place (each block is sorted, and the whole list is
|
1433
|
+
sorted). Your ``blocks`` object will become the instance's internal data.
|
1434
|
+
"""
|
1435
|
+
def __init__(self, points, blocks, K=None, lambd=1, check=True, copy=True, **kwds):
|
1436
|
+
r"""
|
1437
|
+
Constructor.
|
1438
|
+
|
1439
|
+
EXAMPLES::
|
1440
|
+
|
1441
|
+
sage: designs.balanced_incomplete_block_design(13,3) # indirect doctest
|
1442
|
+
(13,3,1)-Balanced Incomplete Block Design
|
1443
|
+
"""
|
1444
|
+
try:
|
1445
|
+
i = int(points)
|
1446
|
+
except TypeError:
|
1447
|
+
pass
|
1448
|
+
else:
|
1449
|
+
points = list(range(i))
|
1450
|
+
|
1451
|
+
GroupDivisibleDesign.__init__(self,
|
1452
|
+
points,
|
1453
|
+
[[x] for x in points],
|
1454
|
+
blocks,
|
1455
|
+
K=K,
|
1456
|
+
lambd=lambd,
|
1457
|
+
check=check,
|
1458
|
+
copy=copy,
|
1459
|
+
**kwds)
|
1460
|
+
|
1461
|
+
def __repr__(self):
|
1462
|
+
r"""
|
1463
|
+
Return a string describing the PBD.
|
1464
|
+
|
1465
|
+
EXAMPLES::
|
1466
|
+
|
1467
|
+
sage: designs.balanced_incomplete_block_design(13,3) # indirect doctest
|
1468
|
+
(13,3,1)-Balanced Incomplete Block Design
|
1469
|
+
"""
|
1470
|
+
bsizes = list(frozenset(self.block_sizes()))
|
1471
|
+
return "Pairwise Balanced Design on {} points with sets of sizes in {}".format(self.num_points(), bsizes)
|
1472
|
+
|
1473
|
+
|
1474
|
+
class BalancedIncompleteBlockDesign(PairwiseBalancedDesign):
|
1475
|
+
r"""
|
1476
|
+
Balanced Incomplete Block Design (BIBD).
|
1477
|
+
|
1478
|
+
INPUT:
|
1479
|
+
|
1480
|
+
- ``points`` -- the underlying set. If ``points`` is an integer `v`, then
|
1481
|
+
the set is considered to be `\{0, ..., v-1\}`
|
1482
|
+
|
1483
|
+
- ``blocks`` -- collection of blocks
|
1484
|
+
|
1485
|
+
- ``k`` -- integer; size of the blocks. Set to ``None`` (automatic guess)
|
1486
|
+
by default
|
1487
|
+
|
1488
|
+
- ``lambd`` -- integer; value of `\lambda`, set to `1` by default
|
1489
|
+
|
1490
|
+
- ``check`` -- boolean; whether to check that the design is a `PBD` with
|
1491
|
+
the right parameters
|
1492
|
+
|
1493
|
+
- ``copy`` -- (use with caution) if set to ``False`` then ``blocks`` must be
|
1494
|
+
a list of lists of integers. The list will not be copied but will be
|
1495
|
+
modified in place (each block is sorted, and the whole list is
|
1496
|
+
sorted). Your ``blocks`` object will become the instance's internal data.
|
1497
|
+
|
1498
|
+
EXAMPLES::
|
1499
|
+
|
1500
|
+
sage: b=designs.balanced_incomplete_block_design(9,3); b
|
1501
|
+
(9,3,1)-Balanced Incomplete Block Design
|
1502
|
+
"""
|
1503
|
+
def __init__(self, points, blocks, k=None, lambd=1, check=True, copy=True, **kwds):
|
1504
|
+
r"""
|
1505
|
+
Constructor.
|
1506
|
+
|
1507
|
+
EXAMPLES::
|
1508
|
+
|
1509
|
+
sage: b=designs.balanced_incomplete_block_design(9,3); b
|
1510
|
+
(9,3,1)-Balanced Incomplete Block Design
|
1511
|
+
"""
|
1512
|
+
PairwiseBalancedDesign.__init__(self,
|
1513
|
+
points,
|
1514
|
+
blocks,
|
1515
|
+
K=[k] if k is not None else None,
|
1516
|
+
lambd=lambd,
|
1517
|
+
check=check,
|
1518
|
+
copy=copy,
|
1519
|
+
**kwds)
|
1520
|
+
|
1521
|
+
def __repr__(self):
|
1522
|
+
r"""
|
1523
|
+
A string to describe ``self``.
|
1524
|
+
|
1525
|
+
EXAMPLES::
|
1526
|
+
|
1527
|
+
sage: b=designs.balanced_incomplete_block_design(9,3); b
|
1528
|
+
(9,3,1)-Balanced Incomplete Block Design
|
1529
|
+
"""
|
1530
|
+
v = self.num_points()
|
1531
|
+
k = len(self._blocks[0]) if self._blocks else 0
|
1532
|
+
l = self._lambd
|
1533
|
+
return "({},{},{})-Balanced Incomplete Block Design".format(v,k,l)
|
1534
|
+
|
1535
|
+
def arc(self, s=2, solver=None, verbose=0, *, integrality_tolerance=1e-3):
|
1536
|
+
r"""
|
1537
|
+
Return the ``s``-arc with maximum cardinality.
|
1538
|
+
|
1539
|
+
A `s`-arc is a subset of points in a BIBD that intersects each block on
|
1540
|
+
at most `s` points. It is one possible generalization of independent set
|
1541
|
+
for graphs.
|
1542
|
+
|
1543
|
+
A simple counting shows that the cardinality of a `s`-arc is at most
|
1544
|
+
`(s-1) * r + 1` where `r` is the number of blocks incident to any point.
|
1545
|
+
A `s`-arc in a BIBD with cardinality `(s-1) * r + 1` is called maximal
|
1546
|
+
and is characterized by the following property: it is not empty and each
|
1547
|
+
block either contains `0` or `s` points of this arc. Equivalently, the
|
1548
|
+
trace of the BIBD on these points is again a BIBD (with block size `s`).
|
1549
|
+
|
1550
|
+
For more informations, see :wikipedia:`Arc_(projective_geometry)`.
|
1551
|
+
|
1552
|
+
INPUT:
|
1553
|
+
|
1554
|
+
- ``s`` -- (default: `2`) the maximum number of points from the arc
|
1555
|
+
in each block
|
1556
|
+
|
1557
|
+
- ``solver`` -- (default: ``None``) specify a Mixed Integer Linear
|
1558
|
+
Programming (MILP) solver to be used. If set to ``None``, the default
|
1559
|
+
one is used. For more information on MILP solvers and which default
|
1560
|
+
solver is used, see the method :meth:`solve
|
1561
|
+
<sage.numerical.mip.MixedIntegerLinearProgram.solve>` of the class
|
1562
|
+
:class:`MixedIntegerLinearProgram
|
1563
|
+
<sage.numerical.mip.MixedIntegerLinearProgram>`.
|
1564
|
+
|
1565
|
+
- ``verbose`` -- integer (default: 0); sets the level of
|
1566
|
+
verbosity. Set to 0 by default, which means quiet.
|
1567
|
+
|
1568
|
+
- ``integrality_tolerance`` -- parameter for use with MILP solvers over
|
1569
|
+
an inexact base ring; see
|
1570
|
+
:meth:`MixedIntegerLinearProgram.get_values`.
|
1571
|
+
|
1572
|
+
EXAMPLES::
|
1573
|
+
|
1574
|
+
sage: # needs sage.schemes
|
1575
|
+
sage: B = designs.balanced_incomplete_block_design(21, 5)
|
1576
|
+
sage: a2 = B.arc(); a2 # random
|
1577
|
+
[5, 9, 10, 12, 15, 20]
|
1578
|
+
sage: len(a2)
|
1579
|
+
6
|
1580
|
+
sage: a4 = B.arc(4); a4 # random
|
1581
|
+
[0, 1, 2, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20]
|
1582
|
+
sage: len(a4)
|
1583
|
+
16
|
1584
|
+
|
1585
|
+
The `2`-arc and `4`-arc above are maximal. One can check that they
|
1586
|
+
intersect the blocks in either 0 or `s` points. Or equivalently that the
|
1587
|
+
traces are again BIBD::
|
1588
|
+
|
1589
|
+
sage: r = (21-1)//(5-1)
|
1590
|
+
sage: 1 + r*1
|
1591
|
+
6
|
1592
|
+
sage: 1 + r*3
|
1593
|
+
16
|
1594
|
+
|
1595
|
+
sage: B.trace(a2).is_t_design(2, return_parameters=True) # needs sage.schemes
|
1596
|
+
(True, (2, 6, 2, 1))
|
1597
|
+
sage: B.trace(a4).is_t_design(2, return_parameters=True) # needs sage.schemes
|
1598
|
+
(True, (2, 16, 4, 1))
|
1599
|
+
|
1600
|
+
Some other examples which are not maximal::
|
1601
|
+
|
1602
|
+
sage: # needs sage.numerical.mip
|
1603
|
+
sage: B = designs.balanced_incomplete_block_design(25, 4)
|
1604
|
+
sage: a2 = B.arc(2)
|
1605
|
+
sage: r = (25-1)//(4-1)
|
1606
|
+
sage: len(a2), 1 + r
|
1607
|
+
(8, 9)
|
1608
|
+
sage: sa2 = set(a2)
|
1609
|
+
sage: set(len(sa2.intersection(b)) for b in B.blocks())
|
1610
|
+
{0, 1, 2}
|
1611
|
+
sage: B.trace(a2).is_t_design(2)
|
1612
|
+
False
|
1613
|
+
|
1614
|
+
sage: # needs sage.numerical.mip
|
1615
|
+
sage: a3 = B.arc(3)
|
1616
|
+
sage: len(a3), 1 + 2*r
|
1617
|
+
(15, 17)
|
1618
|
+
sage: sa3 = set(a3)
|
1619
|
+
sage: set(len(sa3.intersection(b)) for b in B.blocks()) == set([0,3])
|
1620
|
+
False
|
1621
|
+
sage: B.trace(a3).is_t_design(3)
|
1622
|
+
False
|
1623
|
+
|
1624
|
+
TESTS:
|
1625
|
+
|
1626
|
+
Test consistency with relabeling::
|
1627
|
+
|
1628
|
+
sage: b = designs.balanced_incomplete_block_design(7,3) # needs sage.schemes
|
1629
|
+
sage: b.relabel(list("abcdefg")) # needs sage.schemes
|
1630
|
+
sage: set(b.arc()).issubset(b.ground_set()) # needs sage.schemes
|
1631
|
+
True
|
1632
|
+
"""
|
1633
|
+
s = int(s)
|
1634
|
+
|
1635
|
+
# trivial cases
|
1636
|
+
if s <= 0:
|
1637
|
+
return []
|
1638
|
+
elif s >= max(self.block_sizes()):
|
1639
|
+
return self._points[:]
|
1640
|
+
|
1641
|
+
# integer linear program
|
1642
|
+
from sage.numerical.mip import MixedIntegerLinearProgram
|
1643
|
+
|
1644
|
+
p = MixedIntegerLinearProgram(solver=solver)
|
1645
|
+
b = p.new_variable(binary=True)
|
1646
|
+
p.set_objective(p.sum(b[i] for i in range(len(self._points))))
|
1647
|
+
for i in self._blocks:
|
1648
|
+
p.add_constraint(p.sum(b[k] for k in i) <= s)
|
1649
|
+
p.solve(log=verbose)
|
1650
|
+
|
1651
|
+
values = p.get_values(b, convert=bool, tolerance=integrality_tolerance)
|
1652
|
+
return [self._points[i] for (i,j) in values.items() if j]
|
1653
|
+
|
1654
|
+
|
1655
|
+
BIBD = BalancedIncompleteBlockDesign
|