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,815 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ # sage.doctest: needs sage.rings.finite_rings
3
+ r"""
4
+ Resolvable balanced incomplete block design (RBIBD)
5
+
6
+ This module contains everything related to resolvable Balanced Incomplete Block
7
+ Designs. The constructions implemented here can be obtained through the
8
+ ``designs.<tab>`` object::
9
+
10
+ designs.resolvable_balanced_incomplete_block_design(15,3)
11
+
12
+ For Balanced Incomplete Block Design (BIBD) see the module :mod:`bibd
13
+ <sage.combinat.designs.bibd>`. A BIBD
14
+ is said to be *resolvable* if its blocks can be partitionned into parallel
15
+ classes, i.e. partitions of its ground set.
16
+
17
+ The main function of this module is
18
+ :func:`resolvable_balanced_incomplete_block_design`, which calls all others.
19
+
20
+ .. csv-table::
21
+ :class: contentstable
22
+ :widths: 30, 70
23
+ :delim: |
24
+
25
+ :func:`resolvable_balanced_incomplete_block_design` | Return a resolvable BIBD of parameters `v,k`.
26
+ :func:`kirkman_triple_system` | Return a Kirkman Triple System on `v` points.
27
+ :func:`v_4_1_rbibd` | Return a `(v,4,1)`-RBIBD
28
+ :func:`PBD_4_7` | Return a `(v,\{4,7\})`-PBD
29
+ :func:`PBD_4_7_from_Y` | Return a `(3v+1,\{4,7\})`-PBD from a `(v,\{4,5,7\},\NN-\{3,6,10\})`-GDD.
30
+
31
+ References:
32
+
33
+ .. [Stinson91] \D.R. Stinson,
34
+ A survey of Kirkman triple systems and related designs,
35
+ Volume 92, Issues 1-3, 17 November 1991, Pages 371-393,
36
+ Discrete Mathematics,
37
+ :doi:`10.1016/0012-365X(91)90294-C`
38
+
39
+ .. [RCW71] \D. K. Ray-Chaudhuri, R. M. Wilson,
40
+ Solution of Kirkman's schoolgirl problem,
41
+ Volume 19, Pages 187-203,
42
+ Proceedings of Symposia in Pure Mathematics
43
+
44
+ .. [BJL99] \T. Beth, D. Jungnickel, H. Lenz,
45
+ Design Theory 2ed.
46
+ Cambridge University Press
47
+ 1999
48
+
49
+ Functions
50
+ ---------
51
+ """
52
+ from itertools import repeat
53
+ from sage.arith.misc import is_prime_power
54
+ from sage.combinat.designs.bibd import BalancedIncompleteBlockDesign
55
+ from sage.categories.sets_cat import EmptySetError
56
+ from .bibd import balanced_incomplete_block_design
57
+ from sage.misc.unknown import Unknown
58
+
59
+
60
+ def resolvable_balanced_incomplete_block_design(v, k, existence=False):
61
+ r"""
62
+ Return a resolvable BIBD of parameters `v,k`.
63
+
64
+ A BIBD is said to be *resolvable* if its blocks can be partitionned into
65
+ parallel classes, i.e. partitions of the ground set.
66
+
67
+ INPUT:
68
+
69
+ - ``v``, ``k`` -- integers
70
+
71
+ - ``existence`` -- boolean; instead of building the design, return:
72
+
73
+ - ``True`` -- meaning that Sage knows how to build the design
74
+
75
+ - ``Unknown`` -- meaning that Sage does not know how to build the
76
+ design, but that the design may exist (see :mod:`sage.misc.unknown`)
77
+
78
+ - ``False`` -- meaning that the design does not exist
79
+
80
+ .. SEEALSO::
81
+
82
+ - :meth:`IncidenceStructure.is_resolvable`
83
+ - :func:`~sage.combinat.designs.bibd.balanced_incomplete_block_design`
84
+
85
+ EXAMPLES::
86
+
87
+ sage: KTS15 = designs.resolvable_balanced_incomplete_block_design(15,3)
88
+ sage: KTS15
89
+ (15,3,1)-Balanced Incomplete Block Design
90
+ sage: KTS15.is_resolvable()
91
+ True
92
+
93
+ TESTS::
94
+
95
+ sage: bibd = designs.resolvable_balanced_incomplete_block_design
96
+ sage: for v in range(40):
97
+ ....: for k in range(v):
98
+ ....: if bibd(v,k,existence=True) is True:
99
+ ....: _ = bibd(v,k)
100
+ """
101
+ # Trivial cases
102
+ if v == 1 or k == v:
103
+ return balanced_incomplete_block_design(v, k, existence=existence)
104
+
105
+ # Non-existence of resolvable BIBD
106
+ if (v < k or
107
+ k < 2 or
108
+ v % k != 0 or
109
+ (v-1) % (k-1) != 0 or
110
+ (v*(v-1)) % (k*(k-1)) != 0 or
111
+ # From the Handbook of combinatorial designs:
112
+ #
113
+ # With lambda>1 the other exceptions is
114
+ # (15,5,2)
115
+ (k == 6 and v == 36) or
116
+ # Fisher's inequality
117
+ (v*(v-1))/(k*(k-1)) < v):
118
+ if existence:
119
+ return False
120
+ raise EmptySetError("There exists no ({},{},{})-RBIBD".format(v, k, 1))
121
+
122
+ if k == 2:
123
+ if existence:
124
+ return True
125
+ classes = [[[(c+i) % (v-1), (c+v-i) % (v-1)] for i in range(1, v//2)]
126
+ for c in range(v-1)]
127
+ for i, classs in enumerate(classes):
128
+ classs.append([v-1, i])
129
+
130
+ B = BalancedIncompleteBlockDesign(v,
131
+ sum(classes, []),
132
+ k=k,
133
+ check=True,
134
+ copy=False)
135
+ B._classes = classes
136
+ return B
137
+ elif k == 3:
138
+ return kirkman_triple_system(v, existence=existence)
139
+ elif k == 4:
140
+ return v_4_1_rbibd(v, existence=existence)
141
+
142
+ if existence:
143
+ return Unknown
144
+ raise NotImplementedError(f"I don't know how to build a ({v},3,1)-RBIBD!")
145
+
146
+
147
+ def kirkman_triple_system(v, existence=False):
148
+ r"""
149
+ Return a Kirkman Triple System on `v` points.
150
+
151
+ A Kirkman Triple System `KTS(v)` is a resolvable Steiner Triple System. It
152
+ exists if and only if `v\equiv 3\pmod{6}`.
153
+
154
+ INPUT:
155
+
156
+ - ``n`` -- integer
157
+
158
+ - ``existence`` -- boolean (default: ``False``); whether to build the
159
+ `KTS(n)` or only answer whether it exists
160
+
161
+ .. SEEALSO::
162
+
163
+ :meth:`IncidenceStructure.is_resolvable`
164
+
165
+ EXAMPLES:
166
+
167
+ A solution to Kirkmman's original problem::
168
+
169
+ sage: kts = designs.kirkman_triple_system(15)
170
+ sage: classes = kts.is_resolvable(1)[1]
171
+ sage: names = '0123456789abcde'
172
+ sage: def to_name(r_s_t):
173
+ ....: r, s, t = r_s_t
174
+ ....: return ' ' + names[r] + names[s] + names[t] + ' '
175
+ sage: rows = [' '.join(('Day {}'.format(i) for i in range(1,8)))]
176
+ sage: rows.extend(' '.join(map(to_name,row)) for row in zip(*classes))
177
+ sage: print('\n'.join(rows))
178
+ Day 1 Day 2 Day 3 Day 4 Day 5 Day 6 Day 7
179
+ 07e 18e 29e 3ae 4be 5ce 6de
180
+ 139 24a 35b 46c 05d 167 028
181
+ 26b 03c 14d 257 368 049 15a
182
+ 458 569 06a 01b 12c 23d 347
183
+ acd 7bd 78c 89d 79a 8ab 9bc
184
+
185
+ TESTS::
186
+
187
+ sage: for i in range(3,300,6): # needs sage.combinat
188
+ ....: _ = designs.kirkman_triple_system(i)
189
+ """
190
+ if v % 6 != 3:
191
+ if existence:
192
+ return False
193
+ raise ValueError("There is no KTS({}) as v!=3 mod(6)".format(v))
194
+
195
+ if existence:
196
+ return False
197
+
198
+ elif v == 3:
199
+ return BalancedIncompleteBlockDesign(3, [[0, 1, 2]], k=3, lambd=1)
200
+
201
+ elif v == 9:
202
+ classes = [[[0, 1, 5], [2, 6, 7], [3, 4, 8]],
203
+ [[1, 6, 8], [3, 5, 7], [0, 2, 4]],
204
+ [[1, 4, 7], [0, 3, 6], [2, 5, 8]],
205
+ [[4, 5, 6], [0, 7, 8], [1, 2, 3]]]
206
+ KTS = BalancedIncompleteBlockDesign(v, [tr for cl in classes for tr in cl],
207
+ k=3, lambd=1, copy=False)
208
+ KTS._classes = classes
209
+ return KTS
210
+
211
+ # Construction 1.1 from [Stinson91] (originally Theorem 6 from [RCW71])
212
+ #
213
+ # For all prime powers q=1 mod 6, there exists a KTS(2q+1)
214
+ elif ((v-1)//2) % 6 == 1 and is_prime_power((v-1)//2):
215
+ from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF
216
+ q = (v-1)//2
217
+ K = GF(q, 'x')
218
+ a = K.primitive_element()
219
+ t = (q - 1) // 6
220
+
221
+ # m is the solution of a^m=(a^t+1)/2
222
+ from sage.groups.generic import discrete_log
223
+ m = discrete_log((a**t+1)/2, a)
224
+ assert 2*a**m == a**t+1
225
+
226
+ # First parallel class
227
+ first_class = [[(0, 1), (0, 2), 'inf']]
228
+ b0 = K.one()
229
+ b1 = a**t
230
+ b2 = a**m
231
+ first_class.extend([(b0*a**i, 1), (b1*a**i, 1), (b2*a**i, 2)]
232
+ for i in list(range(t))+list(range(2*t, 3*t))+list(range(4*t, 5*t)))
233
+ b0 = a**(m+t)
234
+ b1 = a**(m+3*t)
235
+ b2 = a**(m+5*t)
236
+ first_class.extend([[(b0*a**i, 2), (b1*a**i, 2), (b2*a**i, 2)]
237
+ for i in range(t)])
238
+
239
+ # Action of K on the points
240
+ def action(v, x):
241
+ return (v + x[0], x[1]) if len(x) == 2 else x
242
+
243
+ # relabel to integer
244
+ relabel = {(p, x): i+(x-1)*q
245
+ for i, p in enumerate(K)
246
+ for x in [1, 2]}
247
+ relabel['inf'] = 2*q
248
+
249
+ classes = [[[relabel[action(p, x)] for x in tr] for tr in first_class]
250
+ for p in K]
251
+
252
+ KTS = BalancedIncompleteBlockDesign(v, [tr for cl in classes for tr in cl],
253
+ k=3, lambd=1, copy=False)
254
+
255
+ KTS._classes = classes
256
+ return KTS
257
+
258
+ # Construction 1.2 from [Stinson91] (originally Theorem 5 from [RCW71])
259
+ #
260
+ # For all prime powers q=1 mod 6, there exists a KTS(3q)
261
+ elif (v//3) % 6 == 1 and is_prime_power(v//3):
262
+ from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF
263
+ q = v//3
264
+ K = GF(q, 'x')
265
+ a = K.primitive_element()
266
+ t = (q - 1) // 6
267
+ A0 = [(0, 0), (0, 1), (0, 2)]
268
+ B = [[(a**i, j), (a**(i+2*t), j), (a**(i+4*t), j)] for j in range(3)
269
+ for i in range(t)]
270
+ A = [[(a**i, 0), (a**(i+2*t), 1), (a**(i+4*t), 2)] for i in range(6*t)]
271
+
272
+ # Action of K on the points
273
+ def action(v, x):
274
+ return (v + x[0], x[1])
275
+
276
+ # relabel to integer
277
+ relabel = {(p, j): i+j*q
278
+ for i, p in enumerate(K)
279
+ for j in range(3)}
280
+
281
+ B0 = [A0] + B + A[t:2*t] + A[3*t:4*t] + A[5*t:6*t]
282
+
283
+ # Classes
284
+ classes = [[[relabel[action(p, x)] for x in tr] for tr in B0]
285
+ for p in K]
286
+
287
+ for i in list(range(t))+list(range(2*t, 3*t))+list(range(4*t, 5*t)):
288
+ classes.append([[relabel[action(p, x)] for x in A[i]] for p in K])
289
+
290
+ KTS = BalancedIncompleteBlockDesign(v, [tr for cl in classes for tr in cl],
291
+ k=3, lambd=1, copy=False)
292
+ KTS._classes = classes
293
+ return KTS
294
+
295
+ else:
296
+ # This is Lemma IX.6.4 from [BJL99].
297
+ #
298
+ # This construction takes a (v,{4,7})-PBD. All points are doubled (x has
299
+ # a copy x'), and an infinite point \infty is added.
300
+ #
301
+ # On all blocks of 2*4 points we "paste" a KTS(2*4+1) using the infinite
302
+ # point, in such a way that all {x,x',infty} are set of the design. We
303
+ # do the same for blocks with 2*7 points using a KTS(2*7+1).
304
+ #
305
+ # Note that the triples of points equal to {x,x',\infty} will be added
306
+ # several times.
307
+ #
308
+ # As all those subdesigns are resolvable, each class of the KTS(n) is
309
+ # obtained by considering a set {x,x',\infty} and all sets of all
310
+ # parallel classes of the subdesign which contain this set.
311
+
312
+ # We create the small KTS(n') we need, and relabel them such that
313
+ # 01(n'-1),23(n'-1),... are blocks of the design.
314
+ gdd4 = kirkman_triple_system(9)
315
+ gdd7 = kirkman_triple_system(15)
316
+
317
+ X = [B for B in gdd4 if 8 in B]
318
+ for b in X:
319
+ b.remove(8)
320
+ X = sum(X, []) + [8]
321
+ gdd4.relabel({v: i for i, v in enumerate(X)})
322
+ gdd4 = gdd4.is_resolvable(True)[1] # the relabeled classes
323
+
324
+ X = [B for B in gdd7 if 14 in B]
325
+ for b in X:
326
+ b.remove(14)
327
+ X = sum(X, []) + [14]
328
+ gdd7.relabel({v: i for i, v in enumerate(X)})
329
+ gdd7 = gdd7.is_resolvable(True)[1] # the relabeled classes
330
+
331
+ # The first parallel class contains 01(n'-1), the second contains
332
+ # 23(n'-1), etc..
333
+ # Then remove the blocks containing (n'-1)
334
+ for B in gdd4:
335
+ for i, b in enumerate(B):
336
+ if 8 in b:
337
+ j = min(b)
338
+ del B[i]
339
+ B.insert(0, j)
340
+ break
341
+ gdd4.sort()
342
+ for B in gdd4:
343
+ B.pop(0)
344
+
345
+ for B in gdd7:
346
+ for i, b in enumerate(B):
347
+ if 14 in b:
348
+ j = min(b)
349
+ del B[i]
350
+ B.insert(0, j)
351
+ break
352
+ gdd7.sort()
353
+ for B in gdd7:
354
+ B.pop(0)
355
+
356
+ # Pasting the KTS(n') without {x,x',\infty} blocks
357
+ classes = [[] for _ in repeat(None, (v - 1) // 2)]
358
+ gdd = {4: gdd4, 7: gdd7}
359
+ for B in PBD_4_7((v-1)//2, check=False):
360
+ for i, classs in enumerate(gdd[len(B)]):
361
+ classes[B[i]].extend([2*B[x//2]+x % 2 for x in BB]
362
+ for BB in classs)
363
+
364
+ # The {x,x',\infty} blocks
365
+ for i, classs in enumerate(classes):
366
+ classs.append([2*i, 2*i+1, v-1])
367
+
368
+ KTS = BalancedIncompleteBlockDesign(v,
369
+ blocks=[tr for cl in classes for tr in cl],
370
+ k=3,
371
+ lambd=1,
372
+ check=True,
373
+ copy=False)
374
+ KTS._classes = classes
375
+ assert KTS.is_resolvable()
376
+
377
+ return KTS
378
+
379
+
380
+ def v_4_1_rbibd(v, existence=False):
381
+ r"""
382
+ Return a `(v,4,1)`-RBIBD.
383
+
384
+ INPUT:
385
+
386
+ - ``n`` -- integer
387
+
388
+ - ``existence`` -- boolean (default: ``False``); whether to build the
389
+ design or only answer whether it exists
390
+
391
+ .. SEEALSO::
392
+
393
+ - :meth:`IncidenceStructure.is_resolvable`
394
+ - :func:`resolvable_balanced_incomplete_block_design`
395
+
396
+ .. NOTE::
397
+
398
+ A resolvable `(v,4,1)`-BIBD exists whenever `1\equiv 4\pmod(12)`. This
399
+ function, however, only implements a construction of `(v,4,1)`-BIBD such
400
+ that `v=3q+1\equiv 1\pmod{3}` where `q` is a prime power (see VII.7.5.a
401
+ from [BJL99]_).
402
+
403
+ EXAMPLES::
404
+
405
+ sage: rBIBD = designs.resolvable_balanced_incomplete_block_design(28,4)
406
+ sage: rBIBD.is_resolvable()
407
+ True
408
+ sage: rBIBD.is_t_design(return_parameters=True)
409
+ (True, (2, 28, 4, 1))
410
+
411
+ TESTS::
412
+
413
+ sage: for q in prime_powers(2,30): # indirect doctest
414
+ ....: if (3*q+1)%12 == 4:
415
+ ....: _ = designs.resolvable_balanced_incomplete_block_design(3*q+1,4)
416
+ """
417
+ # Volume 1, VII.7.5.a from [BJL99]_
418
+ if v % 3 != 1 or not is_prime_power((v-1)//3):
419
+ if existence:
420
+ return Unknown
421
+ raise NotImplementedError(f"I don't know how to build a ({v},4,1)-RBIBD!")
422
+ from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF
423
+ q = (v-1)//3
424
+ nn = (q-1)//4
425
+ G = GF(q, 'x')
426
+ w = G.primitive_element()
427
+ e = w**(nn)
428
+ assert e**2 == -1
429
+
430
+ first_class = [[(w**i, j), (-w**i, j), (e*w**i, j+1), (-e*w**i, j+1)]
431
+ for i in range(nn) for j in range(3)]
432
+
433
+ first_class.append([(0, 0), (0, 1), (0, 2), 'inf'])
434
+
435
+ label = {p: i for i, p in enumerate(G)}
436
+
437
+ classes = [[[v-1 if x == 'inf' else (x[1] % 3)*q+label[x[0]+g] for x in S]
438
+ for S in first_class]
439
+ for g in G]
440
+
441
+ BIBD = BalancedIncompleteBlockDesign(v,
442
+ blocks=sum(classes, []),
443
+ k=4,
444
+ check=True,
445
+ copy=False)
446
+ BIBD._classes = classes
447
+ assert BIBD.is_resolvable()
448
+ return BIBD
449
+
450
+
451
+ def PBD_4_7(v, check=True, existence=False):
452
+ r"""
453
+ Return a `(v,\{4,7\})`-PBD.
454
+
455
+ For all `v` such that `n\equiv 1\pmod{3}` and `n\neq 10,19, 31` there
456
+ exists a `(v,\{4,7\})`-PBD. This is proved in Proposition IX.4.5
457
+ from [BJL99]_, which this method implements.
458
+
459
+ This construction of PBD is used by the construction of Kirkman Triple
460
+ Systems.
461
+
462
+ EXAMPLES::
463
+
464
+ sage: from sage.combinat.designs.resolvable_bibd import PBD_4_7
465
+ sage: PBD_4_7(22)
466
+ Pairwise Balanced Design on 22 points with sets of sizes in [4, 7]
467
+
468
+ TESTS:
469
+
470
+ All values `\leq 300`::
471
+
472
+ sage: for i in range(1,300,3): # needs sage.schemes
473
+ ....: if i not in [10,19,31]:
474
+ ....: assert PBD_4_7(i,existence=True) is True
475
+ ....: _ = PBD_4_7(i,check=True)
476
+ """
477
+ if v % 3 != 1 or v in [10, 19, 31]:
478
+ if existence:
479
+ return Unknown
480
+ raise NotImplementedError
481
+ if existence:
482
+ return True
483
+
484
+ from .group_divisible_designs import GroupDivisibleDesign
485
+ from .group_divisible_designs import GDD_4_2
486
+ from .bibd import PairwiseBalancedDesign
487
+ from .bibd import balanced_incomplete_block_design
488
+
489
+ if v == 22:
490
+ # Beth/Jungnickel/Lenz: take KTS(15) and extend each of the 7 classes
491
+ # with a new point. Make those new points a 7-set.
492
+ KTS15 = kirkman_triple_system(15)
493
+ blocks = [S+[i+15] for i, classs in enumerate(KTS15._classes) for S in classs] + [list(range(15, 22))]
494
+
495
+ elif v == 34:
496
+ # [BJL99] (p527,vol1), but originally Brouwer
497
+ A = [(0, 0), (1, 1), (2, 0), (4, 1)]
498
+ B = [(0, 0), (1, 0), (4, 2)]
499
+ C = [(0, 0), (2, 2), (5, 0)]
500
+ D = [(0, 0), (0, 1), (0, 2)]
501
+
502
+ A = [[(x+i, y+j) for x, y in A]
503
+ for i in range(9) for j in range(3)]
504
+ B = [[(x+i, y+i+j) for x, y in B] + [27+j]
505
+ for i in range(9) for j in range(3)]
506
+ C = [[(x+i+j, y+2*i+j) for x, y in C] + [30+j]
507
+ for i in range(9) for j in range(3)]
508
+ D = [[(x+i, y+i) for x, y in D] + [33]
509
+ for i in range(9)]
510
+
511
+ blocks = [[int(x) if not isinstance(x, tuple) else (x[1] % 3)*9+(x[0] % 9)
512
+ for x in S]
513
+ for S in A+B+C+D+[list(range(27, 34))]]
514
+ elif v == 46:
515
+ # [BJL99] (p527,vol1), but originally Brouwer
516
+ A = [(1, 0), (3, 0), (9, 0), (0, 1)]
517
+ B = [(2, 0), (6, 0), (5, 0), (0, 1)]
518
+ C = [(0, 0), (1, 1), (4, 2)]
519
+ D = [(0, 0), (2, 1), (7, 2)]
520
+ E = [(0, 0), (0, 1), (0, 2)]
521
+
522
+ A = [[(x+i, y+j) for x, y in A]
523
+ for i in range(13) for j in range(3)]
524
+ B = [[(x+i, y+j) for x, y in B]
525
+ for i in range(13) for j in range(3)]
526
+ C = [[(x+i, y+j) for x, y in C] + [39+j]
527
+ for i in range(13) for j in range(3)]
528
+ D = [[(x+i, y+j) for x, y in D] + [42+j]
529
+ for i in range(13) for j in range(3)]
530
+ E = [[(x+i, y+i) for x, y in E] + [45]
531
+ for i in range(13)]
532
+
533
+ blocks = [[int(x) if not isinstance(x, tuple) else (x[1] % 3)*13+(x[0] % 13)
534
+ for x in S]
535
+ for S in A+B+C+D+E+[list(range(39, 46))]]
536
+
537
+ elif v == 58:
538
+ # [BJL99] (p527,vol1), but originally Brouwer
539
+ A = [(0, 0), (1, 0), (4, 0), (5, 1)]
540
+ B = [(0, 0), (2, 0), (8, 0), (11, 1)]
541
+ C = [(0, 0), (5, 0), (2, 1), (12, 1)]
542
+ D = [(0, 0), (8, 1), (7, 2)]
543
+ E = [(0, 0), (6, 1), (4, 2)]
544
+ F = [(0, 0), (0, 1), (0, 2)]
545
+
546
+ A = [[(x+i, y+j) for x, y in A]
547
+ for i in range(17) for j in range(3)]
548
+ B = [[(x+i, y+j) for x, y in B]
549
+ for i in range(17) for j in range(3)]
550
+ C = [[(x+i, y+j) for x, y in C]
551
+ for i in range(17) for j in range(3)]
552
+ D = [[(x+i, y+j) for x, y in D] + [51+j]
553
+ for i in range(17) for j in range(3)]
554
+ E = [[(x+i, y+j) for x, y in E] + [54+j]
555
+ for i in range(17) for j in range(3)]
556
+ F = [[(x+i, y+i) for x, y in F] + [57]
557
+ for i in range(17)]
558
+
559
+ blocks = [[int(x) if not isinstance(x, tuple) else (x[1] % 3)*17+(x[0] % 17)
560
+ for x in S]
561
+ for S in A+B+C+D+E+F+[list(range(51, 58))]]
562
+
563
+ elif v == 70:
564
+ # [BJL99] (p527,vol1), but originally Brouwer
565
+ A = [(0, 0), (1, 0), (5, 1), (13, 1)]
566
+ B = [(0, 0), (4, 0), (20, 1), (10, 1)]
567
+ C = [(0, 0), (16, 0), (17, 1), (19, 1)]
568
+ D = [(0, 0), (2, 1), (8, 1), (11, 1)]
569
+ E = [(0, 0), (3, 2), (9, 1)]
570
+ F = [(0, 0), (7, 0), (14, 1)]
571
+ H = [(0, 0), (0, 1), (0, 2)]
572
+
573
+ A = [[(x+i, y+j) for x, y in A]
574
+ for i in range(21) for j in range(3)]
575
+ B = [[(x+i, y+j) for x, y in B]
576
+ for i in range(21) for j in range(3)]
577
+ C = [[(x+i, y+j) for x, y in C]
578
+ for i in range(21) for j in range(3)]
579
+ D = [[(x+i, y+j) for x, y in D]
580
+ for i in range(21) for j in range(3)]
581
+ E = [[(x+i, y+j) for x, y in E] + [63+j]
582
+ for i in range(21) for j in range(3)]
583
+ F = [[(x+3*i+j, y+ii+j) for x, y in F] + [66+j]
584
+ for i in range(7) for j in range(3) for ii in range(3)]
585
+ H = [[(x+i, y+i) for x, y in H] + [69]
586
+ for i in range(21)]
587
+
588
+ blocks = [[int(x) if not isinstance(x, tuple) else (x[1] % 3)*21+(x[0] % 21)
589
+ for x in S]
590
+ for S in A+B+C+D+E+F+H+[list(range(63, 70))]]
591
+
592
+ elif v == 82:
593
+ # This construction is Theorem IX.3.16 from [BJL99] (p.627).
594
+ #
595
+ # A (15,{4},{3})-GDD from a (16,4)-BIBD
596
+ from .group_divisible_designs import group_divisible_design
597
+ from .orthogonal_arrays import transversal_design
598
+ GDD = group_divisible_design(3*5, K=[4], G=[3], check=False)
599
+ TD = transversal_design(5, 5)
600
+
601
+ # A (75,{4},{15})-GDD
602
+ GDD2 = [[3*B[x//3]+x % 3 for x in BB] for B in TD for BB in GDD]
603
+
604
+ # We now complete the (75,{4},{15})-GDD into a (82,{4,7})-PBD. For this,
605
+ # we add 7 new points that are added to all groups of size 15.
606
+ #
607
+ # On these groups a (15+7,{4,7})-PBD is pasted, in such a way that the 7
608
+ # new points are a set of the final PBD
609
+ PBD22 = PBD_4_7(15+7)
610
+ S = next(SS for SS in PBD22 if len(SS) == 7) # a set of size 7
611
+ PBD22.relabel({v: i for i, v in enumerate([i for i in range(15+7)
612
+ if i not in S] + S)})
613
+
614
+ for B in PBD22:
615
+ if B == S:
616
+ continue
617
+ for i in range(5):
618
+ GDD2.append([x+i*15 if x < 15 else x+60 for x in B])
619
+
620
+ GDD2.append(list(range(75, 82)))
621
+ blocks = GDD2
622
+
623
+ elif v == 94:
624
+ # IX.4.5.l from [BJL99].
625
+ #
626
+ # take 4 parallel lines from an affine plane of order 7, and a 5th
627
+ # one. This is a (31,{4,5,7})-BIBD. And 94=3*31+1.
628
+ from sage.combinat.designs.block_design import AffineGeometryDesign
629
+ AF = AffineGeometryDesign(2, 1, 7)
630
+ parall = []
631
+ plus_one = None
632
+ for S in AF:
633
+ if all(x not in SS for SS in parall for x in S):
634
+ parall.append(S)
635
+ elif plus_one is None:
636
+ plus_one = S
637
+ if len(parall) == 4 and plus_one is not None:
638
+ break
639
+ X = set(sum(parall, plus_one))
640
+
641
+ S_4_5_7 = [X.intersection(S) for S in AF]
642
+ S_4_5_7 = [S for S in S_4_5_7 if len(S) > 1]
643
+ S_4_5_7 = PairwiseBalancedDesign(X,
644
+ blocks=S_4_5_7,
645
+ K=[4, 5, 7],
646
+ check=False)
647
+ S_4_5_7.relabel()
648
+ return PBD_4_7_from_Y(S_4_5_7, check=check)
649
+
650
+ elif v == 127 or v == 142:
651
+ # IX.4.5.o from [BJL99].
652
+ #
653
+ # Attach two or seven infinite points to a (40,4)-RBIBD to get a
654
+ # (42,{4,5},{1,2,7})-GDD or a (47,{4,5},{1,2,7})-GDD
655
+ points_to_add = 2 if v == 127 else 7
656
+ rBIBD4 = v_4_1_rbibd(40)
657
+ GDD = [S+[40+i] if i < points_to_add else S
658
+ for i, classs in enumerate(rBIBD4._classes)
659
+ for S in classs]
660
+ if points_to_add == 7:
661
+ GDD.append(list(range(40, 40 + points_to_add)))
662
+ groups = [[x] for x in range(40+points_to_add)]
663
+ else:
664
+ groups = [[x] for x in range(40)]
665
+ groups.append(list(range(40, 40+points_to_add)))
666
+ GDD = GroupDivisibleDesign(40+points_to_add,
667
+ groups=groups,
668
+ blocks=GDD,
669
+ K=[2, 4, 5, 7],
670
+ check=False,
671
+ copy=False)
672
+
673
+ return PBD_4_7_from_Y(GDD, check=check)
674
+
675
+ elif v % 6 == 1 and GDD_4_2((v - 1) // 6, existence=True) is True:
676
+ # VII.5.17 from [BJL99]
677
+ gdd = GDD_4_2((v - 1) // 6)
678
+ return PBD_4_7_from_Y(gdd, check=check)
679
+
680
+ elif v == 202:
681
+ # IV.4.5.p from [BJL99]
682
+ PBD = PBD_4_7(22, check=False)
683
+ PBD = PBD_4_7_from_Y(PBD, check=False)
684
+ return PBD_4_7_from_Y(PBD, check=check)
685
+
686
+ elif balanced_incomplete_block_design(v, 4, existence=True) is True:
687
+ return balanced_incomplete_block_design(v, 4)
688
+ elif balanced_incomplete_block_design(v, 7, existence=True) is True:
689
+ return balanced_incomplete_block_design(v, 7)
690
+ else:
691
+ from sage.combinat.designs.orthogonal_arrays import orthogonal_array
692
+ # IX.4.5.m from [BJL99].
693
+ #
694
+ # This construction takes a TD(5,g) and truncates its last column to
695
+ # size u: it yields a (4g+u,{4,5},{g,u})-GDD. If there exists a
696
+ # (3g+1,{4,7})-PBD and a (3u+1,{4,7})-PBD, then we can apply the x->3x+1
697
+ # construction on the truncated transversal design (which is a GDD).
698
+ #
699
+ # We write vv = 4g+u while satisfying the hypotheses.
700
+ vv = (v - 1) // 3
701
+ for g in range((vv + 5 - 1) // 5, vv // 4 + 1):
702
+ u = vv-4*g
703
+ if (orthogonal_array(5, g, existence=True) is True and
704
+ PBD_4_7(3*g+1, existence=True) is True and
705
+ PBD_4_7(3*u+1, existence=True) is True):
706
+ from .orthogonal_arrays import transversal_design
707
+ domain = set(range(vv))
708
+ GDD = transversal_design(5, g)
709
+ GDD = GroupDivisibleDesign(vv,
710
+ groups=[[x for x in gr if x in domain] for gr in GDD.groups()],
711
+ blocks=[[x for x in B if x in domain] for B in GDD],
712
+ G=set([g, u]),
713
+ K=[4, 5],
714
+ check=False)
715
+ return PBD_4_7_from_Y(GDD, check=check)
716
+
717
+ return PairwiseBalancedDesign(v,
718
+ blocks=blocks,
719
+ K=[4, 7],
720
+ check=check,
721
+ copy=False)
722
+
723
+
724
+ def PBD_4_7_from_Y(gdd, check=True):
725
+ r"""
726
+ Return a `(3v+1,\{4,7\})`-PBD from a `(v,\{4,5,7\},\NN-\{3,6,10\})`-GDD.
727
+
728
+ This implements Lemma IX.3.11 from [BJL99]_ (p.625). All points of the GDD
729
+ are tripled, and a `+\infty` point is added to the design.
730
+
731
+ - A group of size `s\in Y=\NN-\{3,6,10\}` becomes a set of size `3s`. Adding
732
+ `\infty` to it gives it size `3s+1`, and this set is then replaced by a
733
+ `(3s+1,\{4,7\})`-PBD.
734
+
735
+ - A block of size `s\in\{4,5,7\}` becomes a `(3s,\{4,7\},\{3\})`-GDD.
736
+
737
+ This lemma is part of the existence proof of `(v,\{4,7\})`-PBD as explained
738
+ in IX.4.5 from [BJL99]_).
739
+
740
+ INPUT:
741
+
742
+ - ``gdd`` -- a `(v,\{4,5,7\},Y)`-GDD where `Y=\NN-\{3,6,10\}`
743
+
744
+ - ``check`` -- boolean (default: ``True``); whether to check that output is
745
+ correct before returning it. As this is expected to be useless, you may
746
+ want to disable it whenever you want speed.
747
+
748
+ EXAMPLES::
749
+
750
+ sage: from sage.combinat.designs.resolvable_bibd import PBD_4_7_from_Y
751
+ sage: PBD_4_7_from_Y(designs.transversal_design(7,8)) # needs sage.schemes
752
+ Pairwise Balanced Design on 169 points with sets of sizes in [4, 7]
753
+
754
+ TESTS::
755
+
756
+ sage: PBD_4_7_from_Y(designs.balanced_incomplete_block_design(10,10)) # needs sage.schemes
757
+ Traceback (most recent call last):
758
+ ...
759
+ ValueError: The GDD should only contain blocks of size {4,5,7} but there are other: [10]
760
+ sage: PBD_4_7_from_Y(designs.transversal_design(4,3)) # needs sage.schemes
761
+ Traceback (most recent call last):
762
+ ...
763
+ RuntimeError: A group has size 3 but I do not know how to build a (10,[4,7])-PBD
764
+ """
765
+ from .group_divisible_designs import group_divisible_design
766
+ from .bibd import PairwiseBalancedDesign
767
+ block_sizes = set(map(len, gdd._blocks))
768
+ group_sizes = set(map(len, gdd._groups))
769
+ if not block_sizes.issubset([4, 5, 7]):
770
+ txt = list(block_sizes.difference([4, 5, 7]))
771
+ raise ValueError("The GDD should only contain blocks of size {{4,5,7}} "
772
+ "but there are other: {}".format(txt))
773
+
774
+ for gs in group_sizes:
775
+ if PBD_4_7(3*gs+1, existence=True) is not True:
776
+ raise RuntimeError("A group has size {} but I do not know how to "
777
+ "build a ({},[4,7])-PBD".format(gs, 3*gs+1))
778
+
779
+ GDD = {} # the GDD we will need
780
+ if 4 in block_sizes:
781
+ # GDD[4] = GDD_from_BIBD(3*4,4)
782
+ GDD[4] = group_divisible_design(3*4, K=[4], G=[3])
783
+ if 5 in block_sizes:
784
+ # GDD[5] = GDD_from_BIBD(3*5,4)
785
+ GDD[5] = group_divisible_design(3*5, K=[4], G=[3])
786
+ if 7 in block_sizes:
787
+ # It is obtained from a PBD_4_7(22) by removing a point only contained
788
+ # in sets of size 4
789
+ GDD[7] = PBD_4_7(22)
790
+ x = set(range(22)).difference(*[S for S in GDD[7] if len(S) != 4]).pop()
791
+ relabel = sum((S for S in GDD[7] if x in S), []) # the groups must be 012,345,...
792
+ relabel = [xx for xx in relabel if xx != x] + [x]
793
+ GDD[7].relabel({v: i for i, v in enumerate(relabel)})
794
+ GDD[7] = [S for S in GDD[7] if 21 not in S]
795
+
796
+ PBD = []
797
+
798
+ # The blocks
799
+ for B in gdd:
800
+ for B_GDD in GDD[len(B)]:
801
+ PBD.append([3*B[x//3]+(x % 3) for x in B_GDD])
802
+
803
+ # The groups
804
+ group_PBD = {gs: PBD_4_7(3*gs+1) for gs in group_sizes}
805
+ for G in gdd.groups():
806
+ gs = len(G)
807
+ for B in group_PBD[gs]:
808
+ PBD.append([3*G[x//3]+(x % 3) if x < 3*gs else 3*gdd.num_points()
809
+ for x in B])
810
+
811
+ return PairwiseBalancedDesign(3*gdd.num_points()+1,
812
+ blocks=PBD,
813
+ K=[4, 7],
814
+ check=check,
815
+ copy=False)