passagemath-graphs 10.6.1rc1__cp310-cp310-musllinux_1_2_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
- passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
- passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
- passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
- passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
- passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
- passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
- sage/all__sagemath_graphs.py +39 -0
- sage/combinat/abstract_tree.py +2723 -0
- sage/combinat/all__sagemath_graphs.py +34 -0
- sage/combinat/binary_tree.py +5306 -0
- sage/combinat/cluster_algebra_quiver/all.py +22 -0
- sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
- sage/combinat/cluster_algebra_quiver/interact.py +124 -0
- sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
- sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
- sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
- sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
- sage/combinat/designs/MOLS_handbook_data.py +570 -0
- sage/combinat/designs/all.py +58 -0
- sage/combinat/designs/bibd.py +1655 -0
- sage/combinat/designs/block_design.py +1071 -0
- sage/combinat/designs/covering_array.py +269 -0
- sage/combinat/designs/covering_design.py +530 -0
- sage/combinat/designs/database.py +5615 -0
- sage/combinat/designs/design_catalog.py +122 -0
- sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/designs_pyx.pxd +21 -0
- sage/combinat/designs/designs_pyx.pyx +993 -0
- sage/combinat/designs/difference_family.py +3951 -0
- sage/combinat/designs/difference_matrices.py +279 -0
- sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
- sage/combinat/designs/ext_rep.py +1064 -0
- sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
- sage/combinat/designs/group_divisible_designs.py +361 -0
- sage/combinat/designs/incidence_structures.py +2357 -0
- sage/combinat/designs/latin_squares.py +581 -0
- sage/combinat/designs/orthogonal_arrays.py +2244 -0
- sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
- sage/combinat/designs/resolvable_bibd.py +815 -0
- sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
- sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/subhypergraph_search.pyx +530 -0
- sage/combinat/designs/twographs.py +306 -0
- sage/combinat/finite_state_machine.py +14874 -0
- sage/combinat/finite_state_machine_generators.py +2006 -0
- sage/combinat/graph_path.py +448 -0
- sage/combinat/interval_posets.py +3908 -0
- sage/combinat/nu_tamari_lattice.py +269 -0
- sage/combinat/ordered_tree.py +1446 -0
- sage/combinat/posets/all.py +46 -0
- sage/combinat/posets/bubble_shuffle.py +247 -0
- sage/combinat/posets/cartesian_product.py +493 -0
- sage/combinat/posets/d_complete.py +182 -0
- sage/combinat/posets/elements.py +273 -0
- sage/combinat/posets/forest.py +30 -0
- sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/hasse_cython.pyx +174 -0
- sage/combinat/posets/hasse_diagram.py +3672 -0
- sage/combinat/posets/hochschild_lattice.py +158 -0
- sage/combinat/posets/incidence_algebras.py +794 -0
- sage/combinat/posets/lattices.py +5117 -0
- sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/linear_extension_iterator.pyx +292 -0
- sage/combinat/posets/linear_extensions.py +1037 -0
- sage/combinat/posets/mobile.py +275 -0
- sage/combinat/posets/moebius_algebra.py +776 -0
- sage/combinat/posets/poset_examples.py +2178 -0
- sage/combinat/posets/posets.py +9360 -0
- sage/combinat/rooted_tree.py +1070 -0
- sage/combinat/shard_order.py +239 -0
- sage/combinat/tamari_lattices.py +384 -0
- sage/combinat/yang_baxter_graph.py +923 -0
- sage/databases/all__sagemath_graphs.py +1 -0
- sage/databases/knotinfo_db.py +1231 -0
- sage/ext_data/all__sagemath_graphs.py +1 -0
- sage/ext_data/graphs/graph_plot_js.html +330 -0
- sage/ext_data/kenzo/CP2.txt +45 -0
- sage/ext_data/kenzo/CP3.txt +349 -0
- sage/ext_data/kenzo/CP4.txt +4774 -0
- sage/ext_data/kenzo/README.txt +49 -0
- sage/ext_data/kenzo/S4.txt +20 -0
- sage/graphs/all.py +42 -0
- sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/asteroidal_triples.pyx +320 -0
- sage/graphs/base/all.py +1 -0
- sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/boost_graph.pxd +106 -0
- sage/graphs/base/boost_graph.pyx +3045 -0
- sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/c_graph.pxd +106 -0
- sage/graphs/base/c_graph.pyx +5096 -0
- sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/dense_graph.pxd +28 -0
- sage/graphs/base/dense_graph.pyx +801 -0
- sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/graph_backends.pxd +5 -0
- sage/graphs/base/graph_backends.pyx +797 -0
- sage/graphs/base/overview.py +85 -0
- sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/sparse_graph.pxd +90 -0
- sage/graphs/base/sparse_graph.pyx +1653 -0
- sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_dense_graph.pxd +5 -0
- sage/graphs/base/static_dense_graph.pyx +1032 -0
- sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_backend.pxd +27 -0
- sage/graphs/base/static_sparse_backend.pyx +1583 -0
- sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_graph.pxd +37 -0
- sage/graphs/base/static_sparse_graph.pyx +1375 -0
- sage/graphs/bipartite_graph.py +2732 -0
- sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/centrality.pyx +1038 -0
- sage/graphs/cographs.py +519 -0
- sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/comparability.pyx +851 -0
- sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/connectivity.pxd +157 -0
- sage/graphs/connectivity.pyx +4813 -0
- sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/convexity_properties.pxd +16 -0
- sage/graphs/convexity_properties.pyx +870 -0
- sage/graphs/digraph.py +4754 -0
- sage/graphs/digraph_generators.py +1993 -0
- sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/distances_all_pairs.pxd +12 -0
- sage/graphs/distances_all_pairs.pyx +2938 -0
- sage/graphs/domination.py +1363 -0
- sage/graphs/dot2tex_utils.py +100 -0
- sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/edge_connectivity.pyx +1215 -0
- sage/graphs/generators/all.py +1 -0
- sage/graphs/generators/basic.py +1769 -0
- sage/graphs/generators/chessboard.py +538 -0
- sage/graphs/generators/classical_geometries.py +1611 -0
- sage/graphs/generators/degree_sequence.py +235 -0
- sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generators/distance_regular.pyx +2846 -0
- sage/graphs/generators/families.py +4759 -0
- sage/graphs/generators/intersection.py +565 -0
- sage/graphs/generators/platonic_solids.py +262 -0
- sage/graphs/generators/random.py +2623 -0
- sage/graphs/generators/smallgraphs.py +5741 -0
- sage/graphs/generators/world_map.py +724 -0
- sage/graphs/generic_graph.py +26867 -0
- sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generic_graph_pyx.pxd +34 -0
- sage/graphs/generic_graph_pyx.pyx +1673 -0
- sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/genus.pyx +622 -0
- sage/graphs/graph.py +9645 -0
- sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_coloring.pyx +2284 -0
- sage/graphs/graph_database.py +1177 -0
- sage/graphs/graph_decompositions/all.py +1 -0
- sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
- sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
- sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
- sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
- sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
- sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/graph_products.pyx +508 -0
- sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
- sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
- sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
- sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
- sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
- sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
- sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
- sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
- sage/graphs/graph_editor.py +82 -0
- sage/graphs/graph_generators.py +3314 -0
- sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_generators_pyx.pyx +95 -0
- sage/graphs/graph_input.py +812 -0
- sage/graphs/graph_latex.py +2064 -0
- sage/graphs/graph_list.py +410 -0
- sage/graphs/graph_plot.py +1756 -0
- sage/graphs/graph_plot_js.py +338 -0
- sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/hyperbolicity.pyx +1704 -0
- sage/graphs/hypergraph_generators.py +364 -0
- sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/independent_sets.pxd +13 -0
- sage/graphs/independent_sets.pyx +402 -0
- sage/graphs/isgci.py +1033 -0
- sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/isoperimetric_inequalities.pyx +489 -0
- sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/line_graph.pyx +743 -0
- sage/graphs/lovasz_theta.py +77 -0
- sage/graphs/matching.py +1633 -0
- sage/graphs/matching_covered_graph.py +3590 -0
- sage/graphs/orientations.py +1489 -0
- sage/graphs/partial_cube.py +459 -0
- sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/path_enumeration.pyx +2040 -0
- sage/graphs/pq_trees.py +1129 -0
- sage/graphs/print_graphs.py +201 -0
- sage/graphs/schnyder.py +865 -0
- sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/spanning_tree.pyx +1457 -0
- sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/strongly_regular_db.pyx +3340 -0
- sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/traversals.pxd +9 -0
- sage/graphs/traversals.pyx +1872 -0
- sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/trees.pxd +15 -0
- sage/graphs/trees.pyx +310 -0
- sage/graphs/tutte_polynomial.py +713 -0
- sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/views.pyx +794 -0
- sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/weakly_chordal.pyx +604 -0
- sage/groups/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
- sage/knots/all.py +6 -0
- sage/knots/free_knotinfo_monoid.py +507 -0
- sage/knots/gauss_code.py +291 -0
- sage/knots/knot.py +682 -0
- sage/knots/knot_table.py +284 -0
- sage/knots/knotinfo.py +2900 -0
- sage/knots/link.py +4715 -0
- sage/sandpiles/all.py +13 -0
- sage/sandpiles/examples.py +225 -0
- sage/sandpiles/sandpile.py +6365 -0
- sage/topology/all.py +22 -0
- sage/topology/cell_complex.py +1214 -0
- sage/topology/cubical_complex.py +1976 -0
- sage/topology/delta_complex.py +1806 -0
- sage/topology/filtered_simplicial_complex.py +744 -0
- sage/topology/moment_angle_complex.py +823 -0
- sage/topology/simplicial_complex.py +5160 -0
- sage/topology/simplicial_complex_catalog.py +92 -0
- sage/topology/simplicial_complex_examples.py +1680 -0
- sage/topology/simplicial_complex_homset.py +205 -0
- sage/topology/simplicial_complex_morphism.py +836 -0
- sage/topology/simplicial_set.py +4102 -0
- sage/topology/simplicial_set_catalog.py +55 -0
- sage/topology/simplicial_set_constructions.py +2954 -0
- sage/topology/simplicial_set_examples.py +865 -0
- sage/topology/simplicial_set_morphism.py +1464 -0
@@ -0,0 +1,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)
|