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.
Files changed (260) hide show
  1. passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
  2. passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
  3. passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
  4. passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
  5. passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
  6. passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
  7. passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
  8. sage/all__sagemath_graphs.py +39 -0
  9. sage/combinat/abstract_tree.py +2723 -0
  10. sage/combinat/all__sagemath_graphs.py +34 -0
  11. sage/combinat/binary_tree.py +5306 -0
  12. sage/combinat/cluster_algebra_quiver/all.py +22 -0
  13. sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
  14. sage/combinat/cluster_algebra_quiver/interact.py +124 -0
  15. sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
  16. sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
  17. sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
  18. sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
  19. sage/combinat/designs/MOLS_handbook_data.py +570 -0
  20. sage/combinat/designs/all.py +58 -0
  21. sage/combinat/designs/bibd.py +1655 -0
  22. sage/combinat/designs/block_design.py +1071 -0
  23. sage/combinat/designs/covering_array.py +269 -0
  24. sage/combinat/designs/covering_design.py +530 -0
  25. sage/combinat/designs/database.py +5615 -0
  26. sage/combinat/designs/design_catalog.py +122 -0
  27. sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  28. sage/combinat/designs/designs_pyx.pxd +21 -0
  29. sage/combinat/designs/designs_pyx.pyx +993 -0
  30. sage/combinat/designs/difference_family.py +3951 -0
  31. sage/combinat/designs/difference_matrices.py +279 -0
  32. sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  33. sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
  34. sage/combinat/designs/ext_rep.py +1064 -0
  35. sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
  36. sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
  37. sage/combinat/designs/group_divisible_designs.py +361 -0
  38. sage/combinat/designs/incidence_structures.py +2357 -0
  39. sage/combinat/designs/latin_squares.py +581 -0
  40. sage/combinat/designs/orthogonal_arrays.py +2244 -0
  41. sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
  42. sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
  43. sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
  44. sage/combinat/designs/resolvable_bibd.py +815 -0
  45. sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
  46. sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
  47. sage/combinat/designs/subhypergraph_search.pyx +530 -0
  48. sage/combinat/designs/twographs.py +306 -0
  49. sage/combinat/finite_state_machine.py +14874 -0
  50. sage/combinat/finite_state_machine_generators.py +2006 -0
  51. sage/combinat/graph_path.py +448 -0
  52. sage/combinat/interval_posets.py +3908 -0
  53. sage/combinat/nu_tamari_lattice.py +269 -0
  54. sage/combinat/ordered_tree.py +1446 -0
  55. sage/combinat/posets/all.py +46 -0
  56. sage/combinat/posets/bubble_shuffle.py +247 -0
  57. sage/combinat/posets/cartesian_product.py +493 -0
  58. sage/combinat/posets/d_complete.py +182 -0
  59. sage/combinat/posets/elements.py +273 -0
  60. sage/combinat/posets/forest.py +30 -0
  61. sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
  62. sage/combinat/posets/hasse_cython.pyx +174 -0
  63. sage/combinat/posets/hasse_diagram.py +3672 -0
  64. sage/combinat/posets/hochschild_lattice.py +158 -0
  65. sage/combinat/posets/incidence_algebras.py +794 -0
  66. sage/combinat/posets/lattices.py +5117 -0
  67. sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
  68. sage/combinat/posets/linear_extension_iterator.pyx +292 -0
  69. sage/combinat/posets/linear_extensions.py +1037 -0
  70. sage/combinat/posets/mobile.py +275 -0
  71. sage/combinat/posets/moebius_algebra.py +776 -0
  72. sage/combinat/posets/poset_examples.py +2178 -0
  73. sage/combinat/posets/posets.py +9360 -0
  74. sage/combinat/rooted_tree.py +1070 -0
  75. sage/combinat/shard_order.py +239 -0
  76. sage/combinat/tamari_lattices.py +384 -0
  77. sage/combinat/yang_baxter_graph.py +923 -0
  78. sage/databases/all__sagemath_graphs.py +1 -0
  79. sage/databases/knotinfo_db.py +1231 -0
  80. sage/ext_data/all__sagemath_graphs.py +1 -0
  81. sage/ext_data/graphs/graph_plot_js.html +330 -0
  82. sage/ext_data/kenzo/CP2.txt +45 -0
  83. sage/ext_data/kenzo/CP3.txt +349 -0
  84. sage/ext_data/kenzo/CP4.txt +4774 -0
  85. sage/ext_data/kenzo/README.txt +49 -0
  86. sage/ext_data/kenzo/S4.txt +20 -0
  87. sage/graphs/all.py +42 -0
  88. sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
  89. sage/graphs/asteroidal_triples.pyx +320 -0
  90. sage/graphs/base/all.py +1 -0
  91. sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  92. sage/graphs/base/boost_graph.pxd +106 -0
  93. sage/graphs/base/boost_graph.pyx +3045 -0
  94. sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  95. sage/graphs/base/c_graph.pxd +106 -0
  96. sage/graphs/base/c_graph.pyx +5096 -0
  97. sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  98. sage/graphs/base/dense_graph.pxd +28 -0
  99. sage/graphs/base/dense_graph.pyx +801 -0
  100. sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
  101. sage/graphs/base/graph_backends.pxd +5 -0
  102. sage/graphs/base/graph_backends.pyx +797 -0
  103. sage/graphs/base/overview.py +85 -0
  104. sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  105. sage/graphs/base/sparse_graph.pxd +90 -0
  106. sage/graphs/base/sparse_graph.pyx +1653 -0
  107. sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  108. sage/graphs/base/static_dense_graph.pxd +5 -0
  109. sage/graphs/base/static_dense_graph.pyx +1032 -0
  110. sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
  111. sage/graphs/base/static_sparse_backend.pxd +27 -0
  112. sage/graphs/base/static_sparse_backend.pyx +1583 -0
  113. sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  114. sage/graphs/base/static_sparse_graph.pxd +37 -0
  115. sage/graphs/base/static_sparse_graph.pyx +1375 -0
  116. sage/graphs/bipartite_graph.py +2732 -0
  117. sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
  118. sage/graphs/centrality.pyx +1038 -0
  119. sage/graphs/cographs.py +519 -0
  120. sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
  121. sage/graphs/comparability.pyx +851 -0
  122. sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  123. sage/graphs/connectivity.pxd +157 -0
  124. sage/graphs/connectivity.pyx +4813 -0
  125. sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
  126. sage/graphs/convexity_properties.pxd +16 -0
  127. sage/graphs/convexity_properties.pyx +870 -0
  128. sage/graphs/digraph.py +4754 -0
  129. sage/graphs/digraph_generators.py +1993 -0
  130. sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
  131. sage/graphs/distances_all_pairs.pxd +12 -0
  132. sage/graphs/distances_all_pairs.pyx +2938 -0
  133. sage/graphs/domination.py +1363 -0
  134. sage/graphs/dot2tex_utils.py +100 -0
  135. sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  136. sage/graphs/edge_connectivity.pyx +1215 -0
  137. sage/graphs/generators/all.py +1 -0
  138. sage/graphs/generators/basic.py +1769 -0
  139. sage/graphs/generators/chessboard.py +538 -0
  140. sage/graphs/generators/classical_geometries.py +1611 -0
  141. sage/graphs/generators/degree_sequence.py +235 -0
  142. sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
  143. sage/graphs/generators/distance_regular.pyx +2846 -0
  144. sage/graphs/generators/families.py +4759 -0
  145. sage/graphs/generators/intersection.py +565 -0
  146. sage/graphs/generators/platonic_solids.py +262 -0
  147. sage/graphs/generators/random.py +2623 -0
  148. sage/graphs/generators/smallgraphs.py +5741 -0
  149. sage/graphs/generators/world_map.py +724 -0
  150. sage/graphs/generic_graph.py +26867 -0
  151. sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  152. sage/graphs/generic_graph_pyx.pxd +34 -0
  153. sage/graphs/generic_graph_pyx.pyx +1673 -0
  154. sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
  155. sage/graphs/genus.pyx +622 -0
  156. sage/graphs/graph.py +9645 -0
  157. sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
  158. sage/graphs/graph_coloring.pyx +2284 -0
  159. sage/graphs/graph_database.py +1177 -0
  160. sage/graphs/graph_decompositions/all.py +1 -0
  161. sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  162. sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
  163. sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
  164. sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
  165. sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  166. sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
  167. sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
  168. sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
  169. sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
  170. sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
  171. sage/graphs/graph_decompositions/graph_products.pyx +508 -0
  172. sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  173. sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
  174. sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
  175. sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  176. sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
  177. sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
  178. sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  179. sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
  180. sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
  181. sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
  182. sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
  183. sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
  184. sage/graphs/graph_editor.py +82 -0
  185. sage/graphs/graph_generators.py +3314 -0
  186. sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  187. sage/graphs/graph_generators_pyx.pyx +95 -0
  188. sage/graphs/graph_input.py +812 -0
  189. sage/graphs/graph_latex.py +2064 -0
  190. sage/graphs/graph_list.py +410 -0
  191. sage/graphs/graph_plot.py +1756 -0
  192. sage/graphs/graph_plot_js.py +338 -0
  193. sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
  194. sage/graphs/hyperbolicity.pyx +1704 -0
  195. sage/graphs/hypergraph_generators.py +364 -0
  196. sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  197. sage/graphs/independent_sets.pxd +13 -0
  198. sage/graphs/independent_sets.pyx +402 -0
  199. sage/graphs/isgci.py +1033 -0
  200. sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
  201. sage/graphs/isoperimetric_inequalities.pyx +489 -0
  202. sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  203. sage/graphs/line_graph.pyx +743 -0
  204. sage/graphs/lovasz_theta.py +77 -0
  205. sage/graphs/matching.py +1633 -0
  206. sage/graphs/matching_covered_graph.py +3590 -0
  207. sage/graphs/orientations.py +1489 -0
  208. sage/graphs/partial_cube.py +459 -0
  209. sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
  210. sage/graphs/path_enumeration.pyx +2040 -0
  211. sage/graphs/pq_trees.py +1129 -0
  212. sage/graphs/print_graphs.py +201 -0
  213. sage/graphs/schnyder.py +865 -0
  214. sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
  215. sage/graphs/spanning_tree.pyx +1457 -0
  216. sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
  217. sage/graphs/strongly_regular_db.pyx +3340 -0
  218. sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
  219. sage/graphs/traversals.pxd +9 -0
  220. sage/graphs/traversals.pyx +1872 -0
  221. sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
  222. sage/graphs/trees.pxd +15 -0
  223. sage/graphs/trees.pyx +310 -0
  224. sage/graphs/tutte_polynomial.py +713 -0
  225. sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
  226. sage/graphs/views.pyx +794 -0
  227. sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
  228. sage/graphs/weakly_chordal.pyx +604 -0
  229. sage/groups/all__sagemath_graphs.py +1 -0
  230. sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
  231. sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
  232. sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
  233. sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
  234. sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
  235. sage/knots/all.py +6 -0
  236. sage/knots/free_knotinfo_monoid.py +507 -0
  237. sage/knots/gauss_code.py +291 -0
  238. sage/knots/knot.py +682 -0
  239. sage/knots/knot_table.py +284 -0
  240. sage/knots/knotinfo.py +2900 -0
  241. sage/knots/link.py +4715 -0
  242. sage/sandpiles/all.py +13 -0
  243. sage/sandpiles/examples.py +225 -0
  244. sage/sandpiles/sandpile.py +6365 -0
  245. sage/topology/all.py +22 -0
  246. sage/topology/cell_complex.py +1214 -0
  247. sage/topology/cubical_complex.py +1976 -0
  248. sage/topology/delta_complex.py +1806 -0
  249. sage/topology/filtered_simplicial_complex.py +744 -0
  250. sage/topology/moment_angle_complex.py +823 -0
  251. sage/topology/simplicial_complex.py +5160 -0
  252. sage/topology/simplicial_complex_catalog.py +92 -0
  253. sage/topology/simplicial_complex_examples.py +1680 -0
  254. sage/topology/simplicial_complex_homset.py +205 -0
  255. sage/topology/simplicial_complex_morphism.py +836 -0
  256. sage/topology/simplicial_set.py +4102 -0
  257. sage/topology/simplicial_set_catalog.py +55 -0
  258. sage/topology/simplicial_set_constructions.py +2954 -0
  259. sage/topology/simplicial_set_examples.py +865 -0
  260. 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