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,3951 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-graphs
|
2
|
+
r"""
|
3
|
+
Difference families
|
4
|
+
|
5
|
+
This module gathers everything related to difference families. One can build a
|
6
|
+
difference family (or check that it can be built) with :func:`difference_family`::
|
7
|
+
|
8
|
+
sage: G,F = designs.difference_family(13,4,1) # needs sage.libs.pari sage.modules
|
9
|
+
|
10
|
+
It defines the following functions:
|
11
|
+
|
12
|
+
{INDEX_OF_FUNCTIONS}
|
13
|
+
|
14
|
+
REFERENCES:
|
15
|
+
|
16
|
+
.. [BJL99-1] \T. Beth, D. Jungnickel, H. Lenz "Design theory Vol. I."
|
17
|
+
Second edition. Encyclopedia of Mathematics and its Applications, 69. Cambridge
|
18
|
+
University Press, (1999).
|
19
|
+
|
20
|
+
.. [BLJ99-2] \T. Beth, D. Jungnickel, H. Lenz "Design theory Vol. II."
|
21
|
+
Second edition. Encyclopedia of Mathematics and its Applications, 78. Cambridge
|
22
|
+
University Press, (1999).
|
23
|
+
|
24
|
+
.. [Bo39] \R. C. Bose, "On the construction of balanced incomplete block
|
25
|
+
designs", Ann. Eugenics, 9 (1939), 353--399.
|
26
|
+
|
27
|
+
.. [Bu95] \M. Buratti "On simple radical difference families", J.
|
28
|
+
Combinatorial Designs, 3 (1995) 161--168.
|
29
|
+
|
30
|
+
.. [Tu1965] \R. J. Turyn "Character sum and difference sets"
|
31
|
+
Pacific J. Math. 15 (1965) 319--346.
|
32
|
+
|
33
|
+
.. [Tu1984] \R. J. Turyn "A special class of Williamson matrices and
|
34
|
+
difference sets" J. Combinatorial Theory (A) 36 (1984) 111--115.
|
35
|
+
|
36
|
+
.. [Wi72] \R. M. Wilson "Cyclotomy and difference families in elementary Abelian
|
37
|
+
groups", J. Number Theory, 4 (1972) 17--47.
|
38
|
+
|
39
|
+
Functions
|
40
|
+
---------
|
41
|
+
"""
|
42
|
+
# ****************************************************************************
|
43
|
+
# Copyright (C) 2014 Vincent Delecroix <20100.delecroix@gmail.com>
|
44
|
+
#
|
45
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
46
|
+
# as published by the Free Software Foundation; either version 2 of
|
47
|
+
# the License, or (at your option) any later version.
|
48
|
+
# https://www.gnu.org/licenses/
|
49
|
+
# ****************************************************************************
|
50
|
+
|
51
|
+
from sage.arith.misc import is_prime_power
|
52
|
+
from sage.misc.cachefunc import cached_function
|
53
|
+
|
54
|
+
from sage.categories.sets_cat import EmptySetError
|
55
|
+
import sage.arith.all as arith
|
56
|
+
from sage.misc.unknown import Unknown
|
57
|
+
from sage.rings.finite_rings.integer_mod_ring import Zmod
|
58
|
+
from sage.rings.integer import Integer
|
59
|
+
from sage.rings.integer_ring import ZZ
|
60
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
61
|
+
|
62
|
+
|
63
|
+
def group_law(G):
|
64
|
+
r"""
|
65
|
+
Return a triple ``(identity, operation, inverse)`` that define the
|
66
|
+
operations on the group ``G``.
|
67
|
+
|
68
|
+
EXAMPLES::
|
69
|
+
|
70
|
+
sage: from sage.combinat.designs.difference_family import group_law
|
71
|
+
sage: group_law(Zmod(3))
|
72
|
+
(0, <built-in function add>, <built-in function neg>)
|
73
|
+
sage: group_law(SymmetricGroup(5)) # needs sage.groups
|
74
|
+
((), <built-in function mul>, <built-in function inv>)
|
75
|
+
sage: group_law(VectorSpace(QQ, 3)) # needs sage.modules
|
76
|
+
((0, 0, 0), <built-in function add>, <built-in function neg>)
|
77
|
+
"""
|
78
|
+
import operator
|
79
|
+
from sage.categories.groups import Groups
|
80
|
+
from sage.categories.additive_groups import AdditiveGroups
|
81
|
+
|
82
|
+
if G in Groups(): # multiplicative groups
|
83
|
+
return (G.one(), operator.mul, operator.inv)
|
84
|
+
elif G in AdditiveGroups(): # additive groups
|
85
|
+
return (G.zero(), operator.add, operator.neg)
|
86
|
+
else:
|
87
|
+
raise ValueError("%s does not seem to be a group" % G)
|
88
|
+
|
89
|
+
|
90
|
+
def block_stabilizer(G, B):
|
91
|
+
r"""
|
92
|
+
Compute the left stabilizer of the block ``B`` under the action of ``G``.
|
93
|
+
|
94
|
+
This function return the list of all `x\in G` such that `x\cdot B=B` (as a
|
95
|
+
set).
|
96
|
+
|
97
|
+
INPUT:
|
98
|
+
|
99
|
+
- ``G`` -- a group (additive or multiplicative)
|
100
|
+
- ``B`` -- a subset of ``G``
|
101
|
+
|
102
|
+
EXAMPLES::
|
103
|
+
|
104
|
+
sage: from sage.combinat.designs.difference_family import block_stabilizer
|
105
|
+
|
106
|
+
sage: Z8 = Zmod(8)
|
107
|
+
sage: block_stabilizer(Z8, [Z8(0),Z8(2),Z8(4),Z8(6)])
|
108
|
+
[0, 2, 4, 6]
|
109
|
+
sage: block_stabilizer(Z8, [Z8(0),Z8(2)])
|
110
|
+
[0]
|
111
|
+
|
112
|
+
sage: C = cartesian_product([Zmod(4),Zmod(3)])
|
113
|
+
sage: block_stabilizer(C, [C((0,0)),C((2,0)),C((0,1)),C((2,1))])
|
114
|
+
[(0, 0), (2, 0)]
|
115
|
+
|
116
|
+
sage: b = list(map(Zmod(45),[1, 3, 7, 10, 22, 25, 30, 35, 37, 38, 44]))
|
117
|
+
sage: block_stabilizer(Zmod(45),b)
|
118
|
+
[0]
|
119
|
+
"""
|
120
|
+
if not B:
|
121
|
+
return list(G)
|
122
|
+
identity, op, inv = group_law(G)
|
123
|
+
b0 = inv(B[0])
|
124
|
+
S = []
|
125
|
+
for b in B:
|
126
|
+
# fun: if we replace +(-b) with -b it completely fails!!
|
127
|
+
bb0 = op(b, b0) # bb0 = b-B[0]
|
128
|
+
if all(op(bb0, c) in B for c in B):
|
129
|
+
S.append(bb0)
|
130
|
+
return S
|
131
|
+
|
132
|
+
|
133
|
+
def is_difference_family(G, D, v=None, k=None, l=None, verbose=False):
|
134
|
+
r"""
|
135
|
+
Check whether ``D`` forms a difference family in the group ``G``.
|
136
|
+
|
137
|
+
INPUT:
|
138
|
+
|
139
|
+
- ``G`` -- group of cardinality ``v``
|
140
|
+
- ``D`` -- set of ``k``-subsets of ``G``
|
141
|
+
- ``v``, ``k``, ``l`` -- (optional) parameters of the difference family
|
142
|
+
- ``verbose`` -- boolean (default: ``False``); whether to print additional
|
143
|
+
information
|
144
|
+
|
145
|
+
.. SEEALSO::
|
146
|
+
|
147
|
+
:func:`difference_family`
|
148
|
+
|
149
|
+
EXAMPLES::
|
150
|
+
|
151
|
+
sage: from sage.combinat.designs.difference_family import is_difference_family
|
152
|
+
sage: G = Zmod(21)
|
153
|
+
sage: D = [[0,1,4,14,16]]
|
154
|
+
sage: is_difference_family(G, D, 21, 5)
|
155
|
+
True
|
156
|
+
|
157
|
+
sage: G = Zmod(41)
|
158
|
+
sage: D = [[0,1,4,11,29],[0,2,8,17,21]]
|
159
|
+
sage: is_difference_family(G, D, verbose=True)
|
160
|
+
Too few:
|
161
|
+
5 is obtained 0 times in blocks []
|
162
|
+
14 is obtained 0 times in blocks []
|
163
|
+
27 is obtained 0 times in blocks []
|
164
|
+
36 is obtained 0 times in blocks []
|
165
|
+
Too much:
|
166
|
+
4 is obtained 2 times in blocks [0, 1]
|
167
|
+
13 is obtained 2 times in blocks [0, 1]
|
168
|
+
28 is obtained 2 times in blocks [0, 1]
|
169
|
+
37 is obtained 2 times in blocks [0, 1]
|
170
|
+
False
|
171
|
+
sage: D = [[0,1,4,11,29],[0,2,8,17,22]]
|
172
|
+
sage: is_difference_family(G, D)
|
173
|
+
True
|
174
|
+
|
175
|
+
sage: G = Zmod(61)
|
176
|
+
sage: D = [[0,1,3,13,34],[0,4,9,23,45],[0,6,17,24,32]]
|
177
|
+
sage: is_difference_family(G, D)
|
178
|
+
True
|
179
|
+
|
180
|
+
sage: # needs sage.modules
|
181
|
+
sage: G = AdditiveAbelianGroup([3]*4)
|
182
|
+
sage: a,b,c,d = G.gens()
|
183
|
+
sage: D = [[d, -a+d, -c+d, a-b-d, b+c+d],
|
184
|
+
....: [c, a+b-d, -b+c, a-b+d, a+b+c],
|
185
|
+
....: [-a-b+c+d, a-b-c-d, -a+c-d, b-c+d, a+b],
|
186
|
+
....: [-b-d, a+b+d, a-b+c-d, a-b+c, -b+c+d]]
|
187
|
+
sage: is_difference_family(G, D)
|
188
|
+
True
|
189
|
+
|
190
|
+
The following example has a third block with a non-trivial stabilizer::
|
191
|
+
|
192
|
+
sage: G = Zmod(15)
|
193
|
+
sage: D = [[0,1,4],[0,2,9],[0,5,10]]
|
194
|
+
sage: is_difference_family(G,D,verbose=True)
|
195
|
+
It is a (15,3,1)-difference family
|
196
|
+
True
|
197
|
+
|
198
|
+
The function also supports multiplicative groups (non necessarily Abelian)::
|
199
|
+
|
200
|
+
sage: # needs sage.groups
|
201
|
+
sage: G = DihedralGroup(8)
|
202
|
+
sage: x,y = G.gens()
|
203
|
+
sage: i = G.one()
|
204
|
+
sage: D1 = [[i,x,x^4], [i,x^2, y*x], [i,x^5,y], [i,x^6,y*x^2], [i,x^7,y*x^5]]
|
205
|
+
sage: is_difference_family(G, D1, 16, 3, 2)
|
206
|
+
True
|
207
|
+
sage: from sage.combinat.designs.bibd import BIBD_from_difference_family
|
208
|
+
sage: bibd = BIBD_from_difference_family(G, D1, lambd=2)
|
209
|
+
|
210
|
+
TESTS::
|
211
|
+
|
212
|
+
sage: # needs sage.rings.finite_rings
|
213
|
+
sage: K = GF(3^2,'z')
|
214
|
+
sage: z = K.gen()
|
215
|
+
sage: D = [[1,z+1,2]]
|
216
|
+
sage: _ = is_difference_family(K, D, verbose=True)
|
217
|
+
the number of differences (=6) must be a multiple of v-1=8
|
218
|
+
sage: _
|
219
|
+
False
|
220
|
+
"""
|
221
|
+
identity, mul, inv = group_law(G)
|
222
|
+
|
223
|
+
Glist = list(G)
|
224
|
+
|
225
|
+
D = [[G(_) for _ in d] for d in D]
|
226
|
+
|
227
|
+
# Check v (and define it if needed)
|
228
|
+
if v is None:
|
229
|
+
v = len(Glist)
|
230
|
+
else:
|
231
|
+
if len(Glist) != v:
|
232
|
+
if verbose:
|
233
|
+
print("G must have cardinality v (=%d)" % int(v))
|
234
|
+
return False
|
235
|
+
|
236
|
+
# Check k (and define it if needed)
|
237
|
+
if k is None:
|
238
|
+
k = len(D[0])
|
239
|
+
else:
|
240
|
+
k = int(k)
|
241
|
+
|
242
|
+
for d in D:
|
243
|
+
if len(d) != k:
|
244
|
+
if verbose:
|
245
|
+
print("the block {} does not have length {}".format(d, k))
|
246
|
+
return False
|
247
|
+
|
248
|
+
# Check l (and define it if needed)
|
249
|
+
#
|
250
|
+
# - nb_diff: the number of pairs (with multiplicity) covered by the BIBD
|
251
|
+
# generated by the DF.
|
252
|
+
#
|
253
|
+
# - stab: the stabilizer of each set.
|
254
|
+
nb_diff = 0
|
255
|
+
stab = []
|
256
|
+
for d in D:
|
257
|
+
s = block_stabilizer(G,d)
|
258
|
+
stab.append(s)
|
259
|
+
nb_diff += k*(k-1) // len(s)
|
260
|
+
if l is None:
|
261
|
+
if nb_diff % (v-1) != 0:
|
262
|
+
if verbose:
|
263
|
+
print("the number of differences (={}) must be a multiple of v-1={}".format(nb_diff, v-1))
|
264
|
+
return False
|
265
|
+
l = nb_diff // (v-1)
|
266
|
+
else:
|
267
|
+
if nb_diff != l*(v-1):
|
268
|
+
if verbose:
|
269
|
+
print("the number of differences (={}) is not equal to l*(v-1) = {}".format(nb_diff, l*(v-1)))
|
270
|
+
return False
|
271
|
+
|
272
|
+
# Check that every x \in G-{0},occurs exactly l times as a difference
|
273
|
+
counter = {g: 0 for g in Glist}
|
274
|
+
where = {g: set() for g in Glist}
|
275
|
+
del counter[identity]
|
276
|
+
|
277
|
+
for i,d in enumerate(D):
|
278
|
+
tmp_counter = {}
|
279
|
+
for b in d:
|
280
|
+
for c in d:
|
281
|
+
if b == c:
|
282
|
+
continue
|
283
|
+
gg = mul(b, inv(c)) # = b-c or bc^{-1}
|
284
|
+
if gg not in tmp_counter:
|
285
|
+
tmp_counter[gg] = 0
|
286
|
+
where[gg].add(i)
|
287
|
+
tmp_counter[gg] += 1
|
288
|
+
|
289
|
+
if sum(tmp_counter.values()) != k * (k - 1):
|
290
|
+
if verbose:
|
291
|
+
print("repeated element in the {}-th block {}".format(i, d))
|
292
|
+
return False
|
293
|
+
|
294
|
+
# Normalized number of occurrences added to counter
|
295
|
+
stabi = len(stab[i])
|
296
|
+
for gg, tmp_gg in tmp_counter.items():
|
297
|
+
counter[gg] += tmp_gg // stabi
|
298
|
+
|
299
|
+
# Check the counter and report any error
|
300
|
+
too_few = []
|
301
|
+
too_much = []
|
302
|
+
for g in Glist:
|
303
|
+
if g == identity:
|
304
|
+
continue
|
305
|
+
if counter[g] < l:
|
306
|
+
if verbose:
|
307
|
+
too_few.append(g)
|
308
|
+
else:
|
309
|
+
return False
|
310
|
+
if counter[g] > l:
|
311
|
+
if verbose:
|
312
|
+
too_much.append(g)
|
313
|
+
else:
|
314
|
+
return False
|
315
|
+
|
316
|
+
if too_few:
|
317
|
+
print("Too few:")
|
318
|
+
for g in too_few:
|
319
|
+
print(" {} is obtained {} times in blocks {}".format(
|
320
|
+
g, counter[g], sorted(where[g])))
|
321
|
+
if too_much:
|
322
|
+
print("Too much:")
|
323
|
+
for g in too_much:
|
324
|
+
print(" {} is obtained {} times in blocks {}".format(
|
325
|
+
g, counter[g], sorted(where[g])))
|
326
|
+
if too_few or too_much:
|
327
|
+
return False
|
328
|
+
|
329
|
+
if verbose:
|
330
|
+
print("It is a ({},{},{})-difference family".format(v, k, l))
|
331
|
+
return True
|
332
|
+
|
333
|
+
|
334
|
+
def singer_difference_set(q, d):
|
335
|
+
r"""
|
336
|
+
Return a difference set associated to the set of hyperplanes in a projective
|
337
|
+
space of dimension `d` over `GF(q)`.
|
338
|
+
|
339
|
+
A Singer difference set has parameters:
|
340
|
+
|
341
|
+
.. MATH::
|
342
|
+
|
343
|
+
v = \frac{q^{d+1}-1}{q-1}, \quad
|
344
|
+
k = \frac{q^d-1}{q-1}, \quad
|
345
|
+
\lambda = \frac{q^{d-1}-1}{q-1}.
|
346
|
+
|
347
|
+
The idea of the construction is as follows. One consider the finite field
|
348
|
+
`GF(q^{d+1})` as a vector space of dimension `d+1` over `GF(q)`. The set of
|
349
|
+
`GF(q)`-lines in `GF(q^{d+1})` is a projective plane and its set of
|
350
|
+
hyperplanes form a balanced incomplete block design.
|
351
|
+
|
352
|
+
Now, considering a multiplicative generator `z` of `GF(q^{d+1})`, we get a
|
353
|
+
transitive action of a cyclic group on our projective plane from which it is
|
354
|
+
possible to build a difference set.
|
355
|
+
|
356
|
+
The construction is given in details in [Stinson2004]_, section 3.3.
|
357
|
+
|
358
|
+
EXAMPLES::
|
359
|
+
|
360
|
+
sage: from sage.combinat.designs.difference_family import singer_difference_set, is_difference_family
|
361
|
+
sage: G,D = singer_difference_set(3,2) # needs sage.rings.finite_rings
|
362
|
+
sage: is_difference_family(G, D, verbose=True) # needs sage.rings.finite_rings
|
363
|
+
It is a (13,4,1)-difference family
|
364
|
+
True
|
365
|
+
|
366
|
+
sage: G,D = singer_difference_set(4,2) # needs sage.rings.finite_rings
|
367
|
+
sage: is_difference_family(G, D, verbose=True) # needs sage.rings.finite_rings
|
368
|
+
It is a (21,5,1)-difference family
|
369
|
+
True
|
370
|
+
|
371
|
+
sage: G,D = singer_difference_set(3,3) # needs sage.rings.finite_rings
|
372
|
+
sage: is_difference_family(G, D, verbose=True) # needs sage.rings.finite_rings
|
373
|
+
It is a (40,13,4)-difference family
|
374
|
+
True
|
375
|
+
|
376
|
+
sage: G,D = singer_difference_set(9,3) # needs sage.rings.finite_rings
|
377
|
+
sage: is_difference_family(G, D, verbose=True) # needs sage.rings.finite_rings
|
378
|
+
It is a (820,91,10)-difference family
|
379
|
+
True
|
380
|
+
"""
|
381
|
+
q = Integer(q)
|
382
|
+
assert q.is_prime_power()
|
383
|
+
assert d >= 2
|
384
|
+
|
385
|
+
from sage.rings.finite_rings.finite_field_constructor import GF
|
386
|
+
from sage.rings.finite_rings.conway_polynomials import conway_polynomial
|
387
|
+
from sage.rings.finite_rings.integer_mod_ring import Zmod
|
388
|
+
|
389
|
+
# build a polynomial c over GF(q) such that GF(q)[x] / (c(x)) is a
|
390
|
+
# GF(q**(d+1)) and such that x is a multiplicative generator.
|
391
|
+
p,e = q.factor()[0]
|
392
|
+
c = conway_polynomial(p, e*(d+1))
|
393
|
+
if e != 1:
|
394
|
+
# i.e. q is not a prime, so we factorize c over GF(q) and pick
|
395
|
+
# one of its factor
|
396
|
+
K = GF(q,'z')
|
397
|
+
c = c.change_ring(K).factor()[0][0]
|
398
|
+
else:
|
399
|
+
K = GF(q)
|
400
|
+
z = c.parent().gen()
|
401
|
+
|
402
|
+
# Now we consider the GF(q)-subspace V spanned by (1,z,z^2,...,z^(d-1)) inside
|
403
|
+
# GF(q^(d+1)). The multiplication by z is an automorphism of the
|
404
|
+
# GF(q)-projective space built from GF(q^(d+1)). The difference family is
|
405
|
+
# obtained by taking the integers i such that z^i belong to V.
|
406
|
+
powers = [0]
|
407
|
+
i = 1
|
408
|
+
x = z
|
409
|
+
k = (q**d-1)//(q-1)
|
410
|
+
while len(powers) < k:
|
411
|
+
if x.degree() <= (d-1):
|
412
|
+
powers.append(i)
|
413
|
+
x = (x*z).mod(c)
|
414
|
+
i += 1
|
415
|
+
|
416
|
+
return Zmod((q**(d+1)-1)//(q-1)), [powers]
|
417
|
+
|
418
|
+
|
419
|
+
def df_q_6_1(K, existence=False, check=True):
|
420
|
+
r"""
|
421
|
+
Return a `(q,6,1)`-difference family over the finite field `K`.
|
422
|
+
|
423
|
+
The construction uses Theorem 11 of [Wi72]_.
|
424
|
+
|
425
|
+
EXAMPLES::
|
426
|
+
|
427
|
+
sage: # needs sage.rings.finite_rings
|
428
|
+
sage: from sage.combinat.designs.difference_family import is_difference_family, df_q_6_1
|
429
|
+
sage: prime_powers = [v for v in range(31,500,30) if is_prime_power(v)]
|
430
|
+
sage: parameters = [v for v in prime_powers
|
431
|
+
....: if df_q_6_1(GF(v,'a'), existence=True) is True]
|
432
|
+
sage: parameters
|
433
|
+
[31, 151, 181, 211, 241, 271, 331, 361, 421]
|
434
|
+
sage: for v in parameters:
|
435
|
+
....: K = GF(v, 'a')
|
436
|
+
....: df = df_q_6_1(K, check=True)
|
437
|
+
....: assert is_difference_family(K, df, v, 6, 1)
|
438
|
+
|
439
|
+
.. TODO::
|
440
|
+
|
441
|
+
Do improvements due to Zhen and Wu 1999.
|
442
|
+
"""
|
443
|
+
v = K.cardinality()
|
444
|
+
x = K.multiplicative_generator()
|
445
|
+
one = K.one()
|
446
|
+
if v % 30 != 1:
|
447
|
+
if existence:
|
448
|
+
return False
|
449
|
+
raise EmptySetError("k(k-1)=30 should divide (v-1)")
|
450
|
+
t = (v-1) // 30 # number of blocks
|
451
|
+
|
452
|
+
r = x**((v-1)//3) # primitive cube root of unity
|
453
|
+
r2 = r*r # the other primitive cube root
|
454
|
+
|
455
|
+
# we now compute the cosets of x**i
|
456
|
+
xx = x**5
|
457
|
+
to_coset = {x**i * xx**j: i for i in range(5) for j in range((v-1)/5)}
|
458
|
+
|
459
|
+
for c in to_coset: # the loop runs through all nonzero elements of K
|
460
|
+
if c == one or c == r or c == r2:
|
461
|
+
continue
|
462
|
+
if len(set(to_coset[elt] for elt in (r-one, c*(r-one), c-one, c-r, c-r**2))) == 5:
|
463
|
+
if existence:
|
464
|
+
return True
|
465
|
+
B = [one,r,r2,c,c*r,c*r2]
|
466
|
+
D = [[xx**i * b for b in B] for i in range(t)]
|
467
|
+
break
|
468
|
+
else:
|
469
|
+
if existence:
|
470
|
+
return Unknown
|
471
|
+
raise NotImplementedError("Wilson construction failed for v={}".format(v))
|
472
|
+
|
473
|
+
if check and not is_difference_family(K, D, v, 6, 1):
|
474
|
+
raise RuntimeError("Wilson 1972 construction failed! Please e-mail sage-devel@googlegroups.com")
|
475
|
+
|
476
|
+
return D
|
477
|
+
|
478
|
+
|
479
|
+
def radical_difference_set(K, k, l=1, existence=False, check=True):
|
480
|
+
r"""
|
481
|
+
Return a difference set made of a cyclotomic coset in the finite field
|
482
|
+
``K`` and with parameters ``k`` and ``l``.
|
483
|
+
|
484
|
+
Most of these difference sets appear in chapter VI.18.48 of the Handbook of
|
485
|
+
combinatorial designs.
|
486
|
+
|
487
|
+
EXAMPLES::
|
488
|
+
|
489
|
+
sage: from sage.combinat.designs.difference_family import radical_difference_set
|
490
|
+
|
491
|
+
sage: D = radical_difference_set(GF(7), 3, 1); D # needs sage.rings.finite_rings
|
492
|
+
[[1, 2, 4]]
|
493
|
+
sage: sorted(x-y for x in D[0] for y in D[0] if x != y) # needs sage.rings.finite_rings
|
494
|
+
[1, 2, 3, 4, 5, 6]
|
495
|
+
|
496
|
+
sage: D = radical_difference_set(GF(16,'a'), 6, 2) # needs sage.rings.finite_rings
|
497
|
+
sage: sorted(x-y for x in D[0] for y in D[0] if x != y) # needs sage.rings.finite_rings
|
498
|
+
[1,
|
499
|
+
1,
|
500
|
+
a,
|
501
|
+
a,
|
502
|
+
a + 1,
|
503
|
+
a + 1,
|
504
|
+
a^2,
|
505
|
+
a^2,
|
506
|
+
...
|
507
|
+
a^3 + a^2 + a + 1,
|
508
|
+
a^3 + a^2 + a + 1]
|
509
|
+
|
510
|
+
sage: for k in range(2,50): # needs sage.rings.finite_rings
|
511
|
+
....: for l in reversed(divisors(k*(k-1))):
|
512
|
+
....: v = k*(k-1)//l + 1
|
513
|
+
....: if is_prime_power(v) and radical_difference_set(GF(v,'a'),k,l,existence=True) is True:
|
514
|
+
....: _ = radical_difference_set(GF(v,'a'),k,l)
|
515
|
+
....: print("{:3} {:3} {:3}".format(v,k,l))
|
516
|
+
3 2 1
|
517
|
+
4 3 2
|
518
|
+
7 3 1
|
519
|
+
5 4 3
|
520
|
+
7 4 2
|
521
|
+
13 4 1
|
522
|
+
11 5 2
|
523
|
+
7 6 5
|
524
|
+
11 6 3
|
525
|
+
16 6 2
|
526
|
+
8 7 6
|
527
|
+
9 8 7
|
528
|
+
19 9 4
|
529
|
+
37 9 2
|
530
|
+
73 9 1
|
531
|
+
11 10 9
|
532
|
+
19 10 5
|
533
|
+
23 11 5
|
534
|
+
13 12 11
|
535
|
+
23 12 6
|
536
|
+
27 13 6
|
537
|
+
27 14 7
|
538
|
+
16 15 14
|
539
|
+
31 15 7
|
540
|
+
...
|
541
|
+
41 40 39
|
542
|
+
79 40 20
|
543
|
+
83 41 20
|
544
|
+
43 42 41
|
545
|
+
83 42 21
|
546
|
+
47 46 45
|
547
|
+
49 48 47
|
548
|
+
197 49 12
|
549
|
+
"""
|
550
|
+
v = K.cardinality()
|
551
|
+
|
552
|
+
if l*(v-1) != k*(k-1):
|
553
|
+
if existence:
|
554
|
+
return False
|
555
|
+
raise EmptySetError("l*(v-1) is not equal to k*(k-1)")
|
556
|
+
|
557
|
+
# trivial case
|
558
|
+
if (v-1) == k:
|
559
|
+
if existence:
|
560
|
+
return True
|
561
|
+
add_zero = False
|
562
|
+
|
563
|
+
# q = 3 mod 4
|
564
|
+
elif v % 4 == 3 and k == (v-1)//2:
|
565
|
+
if existence:
|
566
|
+
return True
|
567
|
+
add_zero = False
|
568
|
+
|
569
|
+
# q = 3 mod 4
|
570
|
+
elif v % 4 == 3 and k == (v+1)//2:
|
571
|
+
if existence:
|
572
|
+
return True
|
573
|
+
add_zero = True
|
574
|
+
|
575
|
+
# q = 4t^2 + 1, t odd
|
576
|
+
elif v % 8 == 5 and k == (v-1)//4 and arith.is_square((v-1)//4):
|
577
|
+
if existence:
|
578
|
+
return True
|
579
|
+
add_zero = False
|
580
|
+
|
581
|
+
# q = 4t^2 + 9, t odd
|
582
|
+
elif v % 8 == 5 and k == (v+3)//4 and arith.is_square((v-9)//4):
|
583
|
+
if existence:
|
584
|
+
return True
|
585
|
+
add_zero = True
|
586
|
+
|
587
|
+
# exceptional case 1
|
588
|
+
elif (v,k,l) == (16,6,2):
|
589
|
+
if existence:
|
590
|
+
return True
|
591
|
+
add_zero = True
|
592
|
+
|
593
|
+
# exceptional case 2
|
594
|
+
elif (v,k,l) == (73,9,1):
|
595
|
+
if existence:
|
596
|
+
return True
|
597
|
+
add_zero = False
|
598
|
+
|
599
|
+
# are there more ??
|
600
|
+
else:
|
601
|
+
x = K.multiplicative_generator()
|
602
|
+
D = K.cyclotomic_cosets(x**((v-1)//k), [K.one()])
|
603
|
+
if is_difference_family(K, D, v, k, l):
|
604
|
+
print("** You found a new example of radical difference set **\n"
|
605
|
+
"** for the parameters (v,k,l)=({},{},{}). **\n"
|
606
|
+
"** Please contact sage-devel@googlegroups.com **\n".format(v, k, l))
|
607
|
+
if existence:
|
608
|
+
return True
|
609
|
+
add_zero = False
|
610
|
+
|
611
|
+
else:
|
612
|
+
D = K.cyclotomic_cosets(x**((v-1)//(k-1)), [K.one()])
|
613
|
+
D[0].insert(0,K.zero())
|
614
|
+
if is_difference_family(K, D, v, k, l):
|
615
|
+
print("** You found a new example of radical difference set **\n"
|
616
|
+
"** for the parameters (v,k,l)=({},{},{}). **\n"
|
617
|
+
"** Please contact sage-devel@googlegroups.com **\n".format(v, k, l))
|
618
|
+
if existence:
|
619
|
+
return True
|
620
|
+
add_zero = True
|
621
|
+
|
622
|
+
elif existence:
|
623
|
+
return False
|
624
|
+
else:
|
625
|
+
raise EmptySetError("no radical difference set exist "
|
626
|
+
"for the parameters (v,k,l) = ({},{},{}".format(v,k,l))
|
627
|
+
|
628
|
+
x = K.multiplicative_generator()
|
629
|
+
if add_zero:
|
630
|
+
r = x**((v-1)//(k-1))
|
631
|
+
D = K.cyclotomic_cosets(r, [K.one()])
|
632
|
+
D[0].insert(0, K.zero())
|
633
|
+
else:
|
634
|
+
r = x**((v-1)//k)
|
635
|
+
D = K.cyclotomic_cosets(r, [K.one()])
|
636
|
+
|
637
|
+
if check and not is_difference_family(K, D, v, k, l):
|
638
|
+
raise RuntimeError("Sage tried to build a radical difference set with "
|
639
|
+
"parameters ({},{},{}) but it seems that it failed! Please "
|
640
|
+
"e-mail sage-devel@googlegroups.com".format(v,k,l))
|
641
|
+
|
642
|
+
return D
|
643
|
+
|
644
|
+
|
645
|
+
def one_cyclic_tiling(A, n):
|
646
|
+
r"""
|
647
|
+
Given a subset ``A`` of the cyclic additive group `G = Z / nZ` return
|
648
|
+
another subset `B` so that `A + B = G` and `|A| |B| = n` (i.e. any element
|
649
|
+
of `G` is uniquely expressed as a sum `a+b` with `a` in `A` and `b` in `B`).
|
650
|
+
|
651
|
+
EXAMPLES::
|
652
|
+
|
653
|
+
sage: from sage.combinat.designs.difference_family import one_cyclic_tiling
|
654
|
+
sage: tile = [0,2,4]
|
655
|
+
sage: m = one_cyclic_tiling(tile,6); m
|
656
|
+
[0, 3]
|
657
|
+
sage: sorted((i+j)%6 for i in tile for j in m)
|
658
|
+
[0, 1, 2, 3, 4, 5]
|
659
|
+
|
660
|
+
sage: def print_tiling(tile, translat, n):
|
661
|
+
....: for x in translat:
|
662
|
+
....: print(''.join('X' if (i-x)%n in tile else '.' for i in range(n)))
|
663
|
+
|
664
|
+
sage: tile = [0, 1, 2, 7]
|
665
|
+
sage: m = one_cyclic_tiling(tile, 12)
|
666
|
+
sage: print_tiling(tile, m, 12)
|
667
|
+
XXX....X....
|
668
|
+
....XXX....X
|
669
|
+
...X....XXX.
|
670
|
+
|
671
|
+
sage: tile = [0, 1, 5]
|
672
|
+
sage: m = one_cyclic_tiling(tile, 12)
|
673
|
+
sage: print_tiling(tile, m, 12)
|
674
|
+
XX...X......
|
675
|
+
...XX...X...
|
676
|
+
......XX...X
|
677
|
+
..X......XX.
|
678
|
+
|
679
|
+
sage: tile = [0, 2]
|
680
|
+
sage: m = one_cyclic_tiling(tile, 8)
|
681
|
+
sage: print_tiling(tile, m, 8)
|
682
|
+
X.X.....
|
683
|
+
....X.X.
|
684
|
+
.X.X....
|
685
|
+
.....X.X
|
686
|
+
|
687
|
+
ALGORITHM:
|
688
|
+
|
689
|
+
Uses dancing links :mod:`sage.combinat.dlx`
|
690
|
+
"""
|
691
|
+
# we first try a naive approach which correspond to what Wilson used in his
|
692
|
+
# 1972 article
|
693
|
+
n = int(n)
|
694
|
+
d = len(A)
|
695
|
+
if len(set(a % d for a in A)) == d:
|
696
|
+
return [i*d for i in range(n//d)]
|
697
|
+
|
698
|
+
# next, we consider an exhaustive search
|
699
|
+
from sage.combinat.dlx import DLXMatrix
|
700
|
+
|
701
|
+
rows = []
|
702
|
+
for i in range(n):
|
703
|
+
rows.append([i+1, [(i+a) % n+1 for a in A]])
|
704
|
+
M = DLXMatrix(rows)
|
705
|
+
for c in M:
|
706
|
+
return [i-1 for i in c]
|
707
|
+
|
708
|
+
|
709
|
+
def one_radical_difference_family(K, k):
|
710
|
+
r"""
|
711
|
+
Search for a radical difference family on ``K`` using dancing links
|
712
|
+
algorithm.
|
713
|
+
|
714
|
+
For the definition of radical difference family, see
|
715
|
+
:func:`radical_difference_family`. Here, we consider only radical difference
|
716
|
+
family with `\lambda = 1`.
|
717
|
+
|
718
|
+
INPUT:
|
719
|
+
|
720
|
+
- ``K`` -- a finite field of cardinality `q`
|
721
|
+
- ``k`` -- positive integer so that `k(k-1)` divides `q-1`
|
722
|
+
|
723
|
+
OUTPUT: either a difference family or ``None`` if it does not exist
|
724
|
+
|
725
|
+
ALGORITHM:
|
726
|
+
|
727
|
+
The existence of a radical difference family is equivalent to a one
|
728
|
+
dimensional tiling (or packing) problem in a cyclic group. This subsequent
|
729
|
+
problem is solved by a call to the function :func:`one_cyclic_tiling`.
|
730
|
+
|
731
|
+
Let `K^*` be the multiplicative group of the finite field `K`. A radical
|
732
|
+
family has the form `\mathcal B = \{x_1 B, \ldots, x_k B\}`, where
|
733
|
+
`B=\{x:x^{k}=1\}` (for `k` odd) or `B=\{x:x^{k-1}=1\}\cup \{0\}` (for
|
734
|
+
`k` even). Equivalently, `K^*` decomposes as:
|
735
|
+
|
736
|
+
.. MATH::
|
737
|
+
|
738
|
+
K^* = \Delta (x_1 B) \cup \cdots \cup \Delta (x_k B)
|
739
|
+
= x_1 \Delta B \cup \cdots \cup x_k \Delta B.
|
740
|
+
|
741
|
+
We observe that `C=B\backslash 0` is a subgroup of the (cyclic) group
|
742
|
+
`K^*`, that can thus be generated by some element `r`. Furthermore, we
|
743
|
+
observe that `\Delta B` is always a union of cosets of `\pm C` (which is
|
744
|
+
twice larger than `C`).
|
745
|
+
|
746
|
+
.. MATH::
|
747
|
+
|
748
|
+
\begin{array}{llll}
|
749
|
+
(k\text{ odd} ) & \Delta B &= \{r^i-r^j:r^i\neq r^j\} &= \pm C\cdot \{r^i-1: 0 < i \leq m\}\\
|
750
|
+
(k\text{ even}) & \Delta B &= \{r^i-r^j:r^i\neq r^j\}\cup C &= \pm C\cdot \{r^i-1: 0 < i < m\}\cup \pm C
|
751
|
+
\end{array}
|
752
|
+
|
753
|
+
where
|
754
|
+
|
755
|
+
.. MATH::
|
756
|
+
|
757
|
+
(k\text{ odd})\ m = (k-1)/2 \quad \text{and} \quad (k\text{ even})\ m = k/2.
|
758
|
+
|
759
|
+
Consequently, `\mathcal B = \{x_1 B, \ldots, x_k B\}` is a radical
|
760
|
+
difference family if and only if `\{x_1 (\Delta B/(\pm C)), \ldots, x_k
|
761
|
+
(\Delta B/(\pm C))\}` is a partition of the cyclic group `K^*/(\pm C)`.
|
762
|
+
|
763
|
+
EXAMPLES::
|
764
|
+
|
765
|
+
sage: from sage.combinat.designs.difference_family import (
|
766
|
+
....: one_radical_difference_family,
|
767
|
+
....: is_difference_family)
|
768
|
+
|
769
|
+
sage: one_radical_difference_family(GF(13),4) # needs sage.rings.finite_rings
|
770
|
+
[[0, 1, 3, 9]]
|
771
|
+
|
772
|
+
The parameters that appear in [Bu95]_::
|
773
|
+
|
774
|
+
sage: df = one_radical_difference_family(GF(449), 8); df # needs sage.rings.finite_rings
|
775
|
+
[[0, 1, 18, 25, 176, 324, 359, 444],
|
776
|
+
[0, 9, 88, 162, 222, 225, 237, 404],
|
777
|
+
[0, 11, 140, 198, 275, 357, 394, 421],
|
778
|
+
[0, 40, 102, 249, 271, 305, 388, 441],
|
779
|
+
[0, 49, 80, 93, 161, 204, 327, 433],
|
780
|
+
[0, 70, 99, 197, 230, 362, 403, 435],
|
781
|
+
[0, 121, 141, 193, 293, 331, 335, 382],
|
782
|
+
[0, 191, 285, 295, 321, 371, 390, 392]]
|
783
|
+
sage: is_difference_family(GF(449), df, 449, 8, 1) # needs sage.rings.finite_rings
|
784
|
+
True
|
785
|
+
"""
|
786
|
+
q = K.cardinality()
|
787
|
+
x = K.multiplicative_generator()
|
788
|
+
|
789
|
+
e = k*(k-1)
|
790
|
+
if q % e != 1:
|
791
|
+
raise ValueError("q%e is not 1")
|
792
|
+
|
793
|
+
# We define A by (see the function's documentation):
|
794
|
+
# ΔB = C.A
|
795
|
+
if k % 2 == 1:
|
796
|
+
m = (k-1) // 2
|
797
|
+
r = x ** ((q-1) // k) # k-th root of unity
|
798
|
+
A = [r**i - 1 for i in range(1,m+1)]
|
799
|
+
else:
|
800
|
+
m = k // 2
|
801
|
+
r = x ** ((q-1) // (k-1)) # (k-1)-th root of unity
|
802
|
+
A = [r**i - 1 for i in range(1,m)]
|
803
|
+
A.append(K.one())
|
804
|
+
|
805
|
+
# instead of the complicated multiplicative group K^*/(±C) we use the
|
806
|
+
# discrete logarithm to convert everything into the additive group Z/cZ
|
807
|
+
c = m * (q-1) // e # cardinal of ±C
|
808
|
+
from sage.groups.generic import discrete_log
|
809
|
+
logA = [discrete_log(a,x) % c for a in A]
|
810
|
+
|
811
|
+
# if two elements of A are equal modulo c then no tiling is possible
|
812
|
+
if len(set(logA)) != m:
|
813
|
+
return None
|
814
|
+
|
815
|
+
# brute force
|
816
|
+
tiling = one_cyclic_tiling(logA, c)
|
817
|
+
if tiling is None:
|
818
|
+
return None
|
819
|
+
|
820
|
+
D = K.cyclotomic_cosets(r, [x**i for i in tiling])
|
821
|
+
if k % 2 == 0:
|
822
|
+
for d in D:
|
823
|
+
d.insert(K.zero(),0)
|
824
|
+
return D
|
825
|
+
|
826
|
+
|
827
|
+
def radical_difference_family(K, k, l=1, existence=False, check=True):
|
828
|
+
r"""
|
829
|
+
Return a ``(v,k,l)``-radical difference family.
|
830
|
+
|
831
|
+
Let fix an integer `k` and a prime power `q = t k(k-1) + 1`. Let `K` be a
|
832
|
+
field of cardinality `q`. A `(q,k,1)`-difference family is *radical* if
|
833
|
+
its base blocks are either: a coset of the `k`-th root of unity for `k` odd
|
834
|
+
or a coset of `k-1`-th root of unity and `0` if `k` is even (the number `t`
|
835
|
+
is the number of blocks of that difference family).
|
836
|
+
|
837
|
+
The terminology comes from M. Buratti article [Bu95]_ but the first
|
838
|
+
constructions go back to R. Wilson [Wi72]_.
|
839
|
+
|
840
|
+
INPUT:
|
841
|
+
|
842
|
+
- ``K`` -- a finite field
|
843
|
+
- ``k`` -- positive integer; the size of the blocks
|
844
|
+
- ``l`` -- integer (default: `1`); the `\lambda` parameter
|
845
|
+
- ``existence`` -- if ``True``, then return either ``True`` if Sage knows
|
846
|
+
how to build such design, ``Unknown`` if it does not and ``False`` if it
|
847
|
+
knows that the design does not exist
|
848
|
+
- ``check`` -- boolean (default: ``True``); if ``True`` then the result of
|
849
|
+
the computation is checked before being returned. This should not be
|
850
|
+
needed but ensures that the output is correct
|
851
|
+
|
852
|
+
EXAMPLES::
|
853
|
+
|
854
|
+
sage: from sage.combinat.designs.difference_family import radical_difference_family
|
855
|
+
|
856
|
+
sage: radical_difference_family(GF(73), 9) # needs sage.rings.finite_rings
|
857
|
+
[[1, 2, 4, 8, 16, 32, 37, 55, 64]]
|
858
|
+
|
859
|
+
sage: radical_difference_family(GF(281), 5) # needs sage.rings.finite_rings
|
860
|
+
[[1, 86, 90, 153, 232],
|
861
|
+
[4, 50, 63, 79, 85],
|
862
|
+
[5, 36, 149, 169, 203],
|
863
|
+
[7, 40, 68, 219, 228],
|
864
|
+
[9, 121, 212, 248, 253],
|
865
|
+
[29, 81, 222, 246, 265],
|
866
|
+
[31, 137, 167, 247, 261],
|
867
|
+
[32, 70, 118, 119, 223],
|
868
|
+
[39, 56, 66, 138, 263],
|
869
|
+
[43, 45, 116, 141, 217],
|
870
|
+
[98, 101, 109, 256, 279],
|
871
|
+
[106, 124, 145, 201, 267],
|
872
|
+
[111, 123, 155, 181, 273],
|
873
|
+
[156, 209, 224, 264, 271]]
|
874
|
+
|
875
|
+
sage: for k in range(5,10): # needs sage.rings.finite_rings
|
876
|
+
....: print("k = {}".format(k))
|
877
|
+
....: list_q = []
|
878
|
+
....: for q in range(k*(k-1)+1, 2000, k*(k-1)):
|
879
|
+
....: if is_prime_power(q):
|
880
|
+
....: K = GF(q,'a')
|
881
|
+
....: if radical_difference_family(K, k, existence=True) is True:
|
882
|
+
....: list_q.append(q)
|
883
|
+
....: _ = radical_difference_family(K,k)
|
884
|
+
....: print(" ".join(str(p) for p in list_q))
|
885
|
+
k = 5
|
886
|
+
41 61 81 241 281 401 421 601 641 661 701 761 821 881 1181 1201 1301 1321
|
887
|
+
1361 1381 1481 1601 1681 1801 1901
|
888
|
+
k = 6
|
889
|
+
181 211 241 631 691 1531 1831 1861
|
890
|
+
k = 7
|
891
|
+
337 421 463 883 1723
|
892
|
+
k = 8
|
893
|
+
449 1009
|
894
|
+
k = 9
|
895
|
+
73 1153 1873
|
896
|
+
"""
|
897
|
+
v = K.cardinality()
|
898
|
+
x = K.multiplicative_generator()
|
899
|
+
e = k*(k-1)
|
900
|
+
if (l*(v-1)) % e:
|
901
|
+
raise ValueError("k (k-1) = {} should be a multiple of l (v-1) ={}".format(
|
902
|
+
k*(k-1), l*(v-1)))
|
903
|
+
t = l*(v-1) // e # number of blocks
|
904
|
+
|
905
|
+
if t == 1:
|
906
|
+
return radical_difference_set(K, k, l, existence=existence, check=check)
|
907
|
+
|
908
|
+
elif l == (k-1):
|
909
|
+
if existence:
|
910
|
+
return True
|
911
|
+
else:
|
912
|
+
return K.cyclotomic_cosets(x**((v-1)//k))[1:]
|
913
|
+
|
914
|
+
# all the other cases below concern the case l == 1
|
915
|
+
elif l != 1:
|
916
|
+
if existence:
|
917
|
+
return Unknown
|
918
|
+
raise NotImplementedError("No radical families implemented for l > 2")
|
919
|
+
|
920
|
+
else:
|
921
|
+
D = one_radical_difference_family(K,k)
|
922
|
+
if D is None:
|
923
|
+
if existence:
|
924
|
+
return False
|
925
|
+
raise EmptySetError("No such difference family")
|
926
|
+
elif existence:
|
927
|
+
return True
|
928
|
+
|
929
|
+
if check and not is_difference_family(K, D, v, k, l):
|
930
|
+
raise RuntimeError("radical_difference_family produced a wrong "
|
931
|
+
"difference family with parameters v={}, "
|
932
|
+
"k={}, l={}. Please contact "
|
933
|
+
"sage-devel@googlegroups.com".format(v,k,l))
|
934
|
+
|
935
|
+
return D
|
936
|
+
|
937
|
+
|
938
|
+
def twin_prime_powers_difference_set(p, check=True):
|
939
|
+
r"""
|
940
|
+
Return a difference set on `GF(p) \times GF(p+2)`.
|
941
|
+
|
942
|
+
The difference set is built from the following element of the Cartesian
|
943
|
+
product of finite fields `GF(p) \times GF(p+2)`:
|
944
|
+
|
945
|
+
- `(x,0)` with any `x`
|
946
|
+
- `(x,y)` with `x` and `y` squares
|
947
|
+
- `(x,y)` with `x` and `y` non-squares
|
948
|
+
|
949
|
+
For more information see :wikipedia:`Difference_set`.
|
950
|
+
|
951
|
+
INPUT:
|
952
|
+
|
953
|
+
- ``check`` -- boolean (default: ``True``); if ``True``, then the result of
|
954
|
+
the computation is checked before being returned. This should not be
|
955
|
+
needed but ensures that the output is correct
|
956
|
+
|
957
|
+
EXAMPLES::
|
958
|
+
|
959
|
+
sage: from sage.combinat.designs.difference_family import twin_prime_powers_difference_set
|
960
|
+
sage: G, D = twin_prime_powers_difference_set(3)
|
961
|
+
sage: G
|
962
|
+
The Cartesian product of (Finite Field of size 3, Finite Field of size 5)
|
963
|
+
sage: D
|
964
|
+
[[(1, 1), (1, 4), (2, 2), (2, 3), (0, 0), (1, 0), (2, 0)]]
|
965
|
+
"""
|
966
|
+
from sage.rings.finite_rings.finite_field_constructor import FiniteField
|
967
|
+
from sage.categories.cartesian_product import cartesian_product
|
968
|
+
from itertools import product
|
969
|
+
Fp = FiniteField(p,'x')
|
970
|
+
Fq = FiniteField(p+2,'x')
|
971
|
+
Fpset = set(Fp)
|
972
|
+
Fqset = set(Fq)
|
973
|
+
Fp_squares = set(x**2 for x in Fpset)
|
974
|
+
Fq_squares = set(x**2 for x in Fqset)
|
975
|
+
|
976
|
+
# Pairs of squares, pairs of non-squares
|
977
|
+
d = []
|
978
|
+
d.extend(product(Fp_squares.difference([0]),Fq_squares.difference([0])))
|
979
|
+
d.extend(product(Fpset.difference(Fp_squares),Fqset.difference(Fq_squares)))
|
980
|
+
|
981
|
+
# All (x,0)
|
982
|
+
d.extend((x,0) for x in Fpset)
|
983
|
+
|
984
|
+
G = cartesian_product([Fp,Fq])
|
985
|
+
|
986
|
+
if check and not is_difference_family(G, [d]):
|
987
|
+
raise RuntimeError("twin_prime_powers_difference_set produced a wrong "
|
988
|
+
"difference set with p={}. Please contact "
|
989
|
+
"sage-devel@googlegroups.com".format(p))
|
990
|
+
|
991
|
+
return G, [d]
|
992
|
+
|
993
|
+
|
994
|
+
def are_mcfarland_1973_parameters(v, k, lmbda, return_parameters=False):
|
995
|
+
r"""
|
996
|
+
Test whether ``(v,k,lmbda)`` is a triple that can be obtained from the
|
997
|
+
construction from [McF1973]_.
|
998
|
+
|
999
|
+
See :func:`mcfarland_1973_construction`.
|
1000
|
+
|
1001
|
+
INPUT:
|
1002
|
+
|
1003
|
+
- ``v``, ``k``, ``lmbda`` -- integers; parameters of the difference family
|
1004
|
+
- ``return_parameters`` -- boolean (default: ``False``); if ``True``, return a
|
1005
|
+
pair ``(True, (q, s))`` so that ``(q,s)`` can be used in the function
|
1006
|
+
:func:`mcfarland_1973_construction` to actually build a
|
1007
|
+
``(v,k,lmbda)``-difference family. Or ``(False, None)`` if the
|
1008
|
+
construction is not possible
|
1009
|
+
|
1010
|
+
EXAMPLES::
|
1011
|
+
|
1012
|
+
sage: # needs sage.rings.finite_rings
|
1013
|
+
sage: from sage.combinat.designs.difference_family import are_mcfarland_1973_parameters
|
1014
|
+
sage: are_mcfarland_1973_parameters(64, 28, 12)
|
1015
|
+
True
|
1016
|
+
sage: are_mcfarland_1973_parameters(64, 28, 12, return_parameters=True)
|
1017
|
+
(True, (2, 2))
|
1018
|
+
sage: are_mcfarland_1973_parameters(60, 13, 5)
|
1019
|
+
False
|
1020
|
+
sage: are_mcfarland_1973_parameters(98125, 19500, 3875)
|
1021
|
+
True
|
1022
|
+
sage: are_mcfarland_1973_parameters(98125, 19500, 3875, True)
|
1023
|
+
(True, (5, 3))
|
1024
|
+
|
1025
|
+
sage: from sage.combinat.designs.difference_family import are_mcfarland_1973_parameters
|
1026
|
+
sage: for v in range(1, 100): # needs sage.rings.finite_rings
|
1027
|
+
....: for k in range(1,30):
|
1028
|
+
....: for l in range(1,15):
|
1029
|
+
....: if are_mcfarland_1973_parameters(v,k,l):
|
1030
|
+
....: answer, (q,s) = are_mcfarland_1973_parameters(v,k,l,return_parameters=True)
|
1031
|
+
....: print("{} {} {} {} {}".format(v,k,l,q,s))
|
1032
|
+
....: assert answer is True
|
1033
|
+
....: assert designs.difference_family(v,k,l,existence=True) is True
|
1034
|
+
....: G,D = designs.difference_family(v,k,l)
|
1035
|
+
16 6 2 2 1
|
1036
|
+
45 12 3 3 1
|
1037
|
+
64 28 12 2 2
|
1038
|
+
96 20 4 4 1
|
1039
|
+
"""
|
1040
|
+
if v <= k or k <= lmbda:
|
1041
|
+
return (False, None) if return_parameters else False
|
1042
|
+
k = ZZ(k)
|
1043
|
+
lmbda = ZZ(lmbda)
|
1044
|
+
qs, r = (k - lmbda).sqrtrem() # sqrt(k-l) should be q^s
|
1045
|
+
if r or (qs*(qs-1)) % lmbda:
|
1046
|
+
return (False, None) if return_parameters else False
|
1047
|
+
|
1048
|
+
q = qs*(qs-1) // lmbda + 1
|
1049
|
+
if (q <= 1 or
|
1050
|
+
v * (q-1) != qs*q * (qs*q+q-2) or
|
1051
|
+
k * (q-1) != qs * (qs*q-1)):
|
1052
|
+
return (False, None) if return_parameters else False
|
1053
|
+
|
1054
|
+
# NOTE: below we compute the value of s so that qs = q^s. If the method
|
1055
|
+
# is_power_of of integers would be able to return the exponent, we could use
|
1056
|
+
# that... but currently this is not the case
|
1057
|
+
# see github issue #19792
|
1058
|
+
p1,a1 = qs.is_prime_power(get_data=True)
|
1059
|
+
p2,a2 = q.is_prime_power(get_data=True)
|
1060
|
+
|
1061
|
+
if a1 == 0 or a2 == 0 or p1 != p2 or a1 % a2:
|
1062
|
+
return (False, None) if return_parameters else False
|
1063
|
+
|
1064
|
+
return (True, (q, a1//a2)) if return_parameters else True
|
1065
|
+
|
1066
|
+
|
1067
|
+
def mcfarland_1973_construction(q, s):
|
1068
|
+
r"""
|
1069
|
+
Return a difference set.
|
1070
|
+
|
1071
|
+
The difference set returned has the following parameters
|
1072
|
+
|
1073
|
+
.. MATH::
|
1074
|
+
|
1075
|
+
v = \frac{q^{s+1}(q^{s+1}+q-2)}{q-1},
|
1076
|
+
k = \frac{q^s (q^{s+1}-1)}{q-1},
|
1077
|
+
\lambda = \frac{q^s(q^s-1)}{q-1}
|
1078
|
+
|
1079
|
+
This construction is due to [McF1973]_.
|
1080
|
+
|
1081
|
+
INPUT:
|
1082
|
+
|
1083
|
+
- ``q``, ``s`` -- integers; parameters for the difference set (see the above
|
1084
|
+
formulas for the expression of ``v``, ``k``, ``l`` in terms of ``q`` and
|
1085
|
+
``s``)
|
1086
|
+
|
1087
|
+
.. SEEALSO::
|
1088
|
+
|
1089
|
+
The function :func:`are_mcfarland_1973_parameters` makes the translation
|
1090
|
+
between the parameters `(q,s)` corresponding to a given triple
|
1091
|
+
`(v,k,\lambda)`.
|
1092
|
+
|
1093
|
+
REFERENCES:
|
1094
|
+
|
1095
|
+
.. [McF1973] Robert L. McFarland
|
1096
|
+
"A family of difference sets in non-cyclic groups"
|
1097
|
+
J. Combinatorial Theory (A) 15 (1973) 1--10.
|
1098
|
+
:doi:`10.1016/0097-3165(73)90031-9`
|
1099
|
+
|
1100
|
+
EXAMPLES::
|
1101
|
+
|
1102
|
+
sage: from sage.combinat.designs.difference_family import (
|
1103
|
+
....: mcfarland_1973_construction, is_difference_family)
|
1104
|
+
|
1105
|
+
sage: G,D = mcfarland_1973_construction(3, 1) # needs sage.modules
|
1106
|
+
sage: assert is_difference_family(G, D, 45, 12, 3) # needs sage.modules
|
1107
|
+
|
1108
|
+
sage: G,D = mcfarland_1973_construction(2, 2) # needs sage.modules
|
1109
|
+
sage: assert is_difference_family(G, D, 64, 28, 12) # needs sage.modules
|
1110
|
+
"""
|
1111
|
+
from sage.rings.finite_rings.finite_field_constructor import GF
|
1112
|
+
from sage.modules.free_module import VectorSpace
|
1113
|
+
from sage.rings.finite_rings.integer_mod_ring import Zmod
|
1114
|
+
from sage.categories.cartesian_product import cartesian_product
|
1115
|
+
|
1116
|
+
r = (q**(s+1)-1) // (q-1)
|
1117
|
+
F = GF(q,'a')
|
1118
|
+
V = VectorSpace(F, s+1)
|
1119
|
+
K = Zmod(r+1)
|
1120
|
+
|
1121
|
+
G = cartesian_product([F]*(s+1) + [K])
|
1122
|
+
|
1123
|
+
D = []
|
1124
|
+
for k, H in zip(K, V.subspaces(s)):
|
1125
|
+
for v in H:
|
1126
|
+
D.append(G(tuple(v) + (k,)))
|
1127
|
+
|
1128
|
+
return G,[D]
|
1129
|
+
|
1130
|
+
|
1131
|
+
def are_hadamard_difference_set_parameters(v, k, lmbda):
|
1132
|
+
r"""
|
1133
|
+
Check whether ``(v,k,lmbda)`` is of the form ``(4N^2, 2N^2 - N, N^2 - N)``.
|
1134
|
+
|
1135
|
+
INPUT:
|
1136
|
+
|
1137
|
+
- ``(v, k, lmbda)`` -- parameters of a difference set
|
1138
|
+
|
1139
|
+
EXAMPLES::
|
1140
|
+
|
1141
|
+
sage: from sage.combinat.designs.difference_family import are_hadamard_difference_set_parameters
|
1142
|
+
sage: are_hadamard_difference_set_parameters(36, 15, 6)
|
1143
|
+
True
|
1144
|
+
sage: are_hadamard_difference_set_parameters(60, 13, 5)
|
1145
|
+
False
|
1146
|
+
"""
|
1147
|
+
N = k - 2*lmbda
|
1148
|
+
N2 = N*N
|
1149
|
+
return v == 4*N2 and k == 2*N2 - N and lmbda == N2 - N
|
1150
|
+
|
1151
|
+
|
1152
|
+
@cached_function
|
1153
|
+
def hadamard_difference_set_product_parameters(N):
|
1154
|
+
r"""
|
1155
|
+
Check whether a product construction is available for Hadamard difference
|
1156
|
+
set with parameter ``N``.
|
1157
|
+
|
1158
|
+
This function looks for two integers `N_1` and `N_2` greater than `1`
|
1159
|
+
and so that `N = 2 N_1 N_2` and there exists Hadamard difference set with
|
1160
|
+
parameters `(4 N_i^2, 2N_i^2 - N_i, N_i^2 - N_i)`. If such pair exists,
|
1161
|
+
the output is the pair ``(N_1, N_2)`` otherwise it is ``None``.
|
1162
|
+
|
1163
|
+
INPUT:
|
1164
|
+
|
1165
|
+
- ``N`` -- positive integer
|
1166
|
+
|
1167
|
+
EXAMPLES::
|
1168
|
+
|
1169
|
+
sage: from sage.combinat.designs.difference_family import hadamard_difference_set_product_parameters
|
1170
|
+
sage: hadamard_difference_set_product_parameters(8) # needs sage.rings.finite_rings
|
1171
|
+
(2, 2)
|
1172
|
+
"""
|
1173
|
+
if N % 2:
|
1174
|
+
return False
|
1175
|
+
|
1176
|
+
for N1 in (N//2).divisors()[1:]:
|
1177
|
+
if 4*N1 > N:
|
1178
|
+
break
|
1179
|
+
v1 = 4*N1*N1
|
1180
|
+
k1 = 2*N1*N1 - N1
|
1181
|
+
l1 = N1*N1 - N1
|
1182
|
+
if not difference_family(v1, k1, l1, existence=True):
|
1183
|
+
continue
|
1184
|
+
N2 = N // (2*N1)
|
1185
|
+
v2 = 4*N2*N2
|
1186
|
+
k2 = 2*N2*N2 - N2
|
1187
|
+
l2 = N2*N2 - N2
|
1188
|
+
if not difference_family(v2, k2, l2, existence=True):
|
1189
|
+
continue
|
1190
|
+
|
1191
|
+
return (N1,N2)
|
1192
|
+
|
1193
|
+
return None
|
1194
|
+
|
1195
|
+
|
1196
|
+
def hadamard_difference_set_product(G1, D1, G2, D2):
|
1197
|
+
r"""
|
1198
|
+
Make a product of two Hadamard difference sets.
|
1199
|
+
|
1200
|
+
This product construction appears in [Tu1984]_.
|
1201
|
+
|
1202
|
+
INPUT:
|
1203
|
+
|
1204
|
+
- ``G1, D1``, ``G2, D2`` -- two Hadamard difference sets
|
1205
|
+
|
1206
|
+
EXAMPLES::
|
1207
|
+
|
1208
|
+
sage: from sage.combinat.designs.difference_family import hadamard_difference_set_product
|
1209
|
+
sage: from sage.combinat.designs.difference_family import is_difference_family
|
1210
|
+
|
1211
|
+
sage: G1,D1 = designs.difference_family(16,6,2) # needs sage.rings.finite_rings
|
1212
|
+
sage: G2,D2 = designs.difference_family(36,15,6) # needs sage.rings.finite_rings
|
1213
|
+
|
1214
|
+
sage: G11,D11 = hadamard_difference_set_product(G1,D1,G1,D1) # needs sage.rings.finite_rings
|
1215
|
+
sage: assert is_difference_family(G11, D11, 256, 120, 56) # needs sage.rings.finite_rings
|
1216
|
+
sage: assert designs.difference_family(256, 120, 56, existence=True) is True # needs sage.rings.finite_rings
|
1217
|
+
|
1218
|
+
sage: G12,D12 = hadamard_difference_set_product(G1,D1,G2,D2) # needs sage.rings.finite_rings
|
1219
|
+
sage: assert is_difference_family(G12, D12, 576, 276, 132) # needs sage.rings.finite_rings
|
1220
|
+
sage: assert designs.difference_family(576, 276, 132, existence=True) is True # needs sage.rings.finite_rings
|
1221
|
+
"""
|
1222
|
+
from sage.categories.cartesian_product import cartesian_product
|
1223
|
+
|
1224
|
+
G = cartesian_product([G1,G2])
|
1225
|
+
D1 = set(D1[0])
|
1226
|
+
D1c = set(s for s in G1 if s not in D1)
|
1227
|
+
D2 = set(D2[0])
|
1228
|
+
D2c = set(s for s in G2 if s not in D2)
|
1229
|
+
|
1230
|
+
D = set().union((G((s1,s2)) for s1 in D1 for s2 in D2),
|
1231
|
+
(G((s1,s2)) for s1 in D1c for s2 in D2c))
|
1232
|
+
|
1233
|
+
return G, [[s for s in G if s not in D]]
|
1234
|
+
|
1235
|
+
|
1236
|
+
def turyn_1965_3x3xK(k=4):
|
1237
|
+
r"""
|
1238
|
+
Return a difference set in either `C_3 \times C_3 \times C_4` or `C_3 \times
|
1239
|
+
C_3 \times C_2 \times C_2` with parameters `v=36`, `k=15`, `\lambda=6`.
|
1240
|
+
|
1241
|
+
This example appears in [Tu1965]_.
|
1242
|
+
|
1243
|
+
INPUT:
|
1244
|
+
|
1245
|
+
- ``k`` -- either ``2`` (to get a difference set in `C_3 \times C_3 \times
|
1246
|
+
C_2 \times C_2`) or ``4`` (to get a difference set in `C_3 \times C_3
|
1247
|
+
\times C_3 \times C_4`)
|
1248
|
+
|
1249
|
+
EXAMPLES::
|
1250
|
+
|
1251
|
+
sage: from sage.combinat.designs.difference_family import turyn_1965_3x3xK
|
1252
|
+
sage: from sage.combinat.designs.difference_family import is_difference_family
|
1253
|
+
sage: G,D = turyn_1965_3x3xK(4)
|
1254
|
+
sage: assert is_difference_family(G, D, 36, 15, 6)
|
1255
|
+
sage: G,D = turyn_1965_3x3xK(2)
|
1256
|
+
sage: assert is_difference_family(G, D, 36, 15, 6)
|
1257
|
+
"""
|
1258
|
+
from sage.categories.cartesian_product import cartesian_product
|
1259
|
+
from sage.rings.finite_rings.integer_mod_ring import Zmod
|
1260
|
+
|
1261
|
+
if k == 2:
|
1262
|
+
G = cartesian_product([Zmod(3), Zmod(3), Zmod(2), Zmod(2)])
|
1263
|
+
K = [(0,0), (0,1), (1,0), (1,1)]
|
1264
|
+
elif k == 4:
|
1265
|
+
G = cartesian_product([Zmod(3), Zmod(3), Zmod(4)])
|
1266
|
+
K = [(0,), (1,), (2,), (3,)]
|
1267
|
+
else:
|
1268
|
+
raise ValueError("k must be 2 or 4")
|
1269
|
+
|
1270
|
+
L = [[(0,1),(1,1),(2,1),(0,2),(1,2),(2,2)], # complement of y=0
|
1271
|
+
[(0,0),(1,1),(2,2)], # x-y=0
|
1272
|
+
[(0,0),(1,2),(2,1)], # x+y=0
|
1273
|
+
[(0,0),(0,1),(0,2)]] # x=0
|
1274
|
+
|
1275
|
+
return G, [[G(v + k) for l, k in zip(L, K) for v in l]]
|
1276
|
+
|
1277
|
+
|
1278
|
+
def _is_periodic_sequence(seq, period):
|
1279
|
+
r"""
|
1280
|
+
Check if the sequence is periodic with correct period.
|
1281
|
+
|
1282
|
+
The sequence should have length at least twice the period, so that
|
1283
|
+
periodicity can be checked.
|
1284
|
+
|
1285
|
+
INPUT:
|
1286
|
+
|
1287
|
+
- ``seq`` -- the sequence to be tested (must have length at least twice the period)
|
1288
|
+
- ``period`` -- integer; the period that the sequence should have
|
1289
|
+
|
1290
|
+
EXAMPLES::
|
1291
|
+
|
1292
|
+
sage: from sage.combinat.designs.difference_family import _is_periodic_sequence
|
1293
|
+
sage: _is_periodic_sequence([0, 1, 2, 3, 0, 1, 2, 3], 4)
|
1294
|
+
True
|
1295
|
+
sage: _is_periodic_sequence([0, 1, 0, 1, 0, 1, 0, 1], 4)
|
1296
|
+
False
|
1297
|
+
sage: _is_periodic_sequence([0, 1, 1, 1, 0, 1, 2, 1], 4)
|
1298
|
+
False
|
1299
|
+
"""
|
1300
|
+
assert len(seq) >= 2*period
|
1301
|
+
|
1302
|
+
for per in range(1, period):
|
1303
|
+
first = seq[:per]
|
1304
|
+
periodic = True
|
1305
|
+
for j in range(1, len(seq)//per):
|
1306
|
+
if seq[j*per : (j+1)*per] != first:
|
1307
|
+
periodic = False
|
1308
|
+
break
|
1309
|
+
if periodic:
|
1310
|
+
return False
|
1311
|
+
if seq[:period] != seq[period : 2*period]:
|
1312
|
+
return False
|
1313
|
+
return True
|
1314
|
+
|
1315
|
+
|
1316
|
+
def _create_m_sequence(q, n, check=True):
|
1317
|
+
r"""
|
1318
|
+
Create an m-sequence over GF(q) with period `q^n - 1`.
|
1319
|
+
|
1320
|
+
Given a prime power `q`, the m-sequence is created as described by [Zie1959]_
|
1321
|
+
from a primitive function over the finite field `GF(q)`.
|
1322
|
+
|
1323
|
+
Given a primitive function `f=c_0+c_1x+...+c_nx^n` over `K = GF(q)` of degree `n`,
|
1324
|
+
the recurrence is given by: `a_i = -c_0^{-1}(c_1a_{i-1} + ... + c_na{i-n})`.
|
1325
|
+
The first `n` elements will be `0, 0, ..., 0, 1` and these will give a maximal length recurrence sequence
|
1326
|
+
as shown in [Mit2008]_.
|
1327
|
+
|
1328
|
+
INPUT:
|
1329
|
+
|
1330
|
+
- ``q`` -- a prime power
|
1331
|
+
- ``n`` -- a nonnegative number
|
1332
|
+
- ``check`` -- boolean (default: ``True``); if ``True``, check that the
|
1333
|
+
result is a sequence with correct period; detting it to ``False`` may
|
1334
|
+
speed up considerably the computation
|
1335
|
+
|
1336
|
+
EXAMPLES::
|
1337
|
+
|
1338
|
+
sage: from sage.combinat.designs.difference_family import _create_m_sequence
|
1339
|
+
sage: _create_m_sequence(3, 2) # random # needs sage.rings.finite_rings
|
1340
|
+
[1, 0, 1, 2, 2, 0, 2, 1]
|
1341
|
+
sage: _create_m_sequence(4, 2, check=False) # random # needs sage.rings.finite_rings
|
1342
|
+
[1, 0, a, a + 1, a, a, 0, a + 1, 1, a + 1, a + 1, 0, 1, a, 1]
|
1343
|
+
sage: _create_m_sequence(6, 2)
|
1344
|
+
Traceback (most recent call last):
|
1345
|
+
...
|
1346
|
+
ValueError: q must be a prime power
|
1347
|
+
"""
|
1348
|
+
from sage.rings.finite_rings.finite_field_constructor import GF
|
1349
|
+
|
1350
|
+
if not is_prime_power(q):
|
1351
|
+
raise ValueError('q must be a prime power')
|
1352
|
+
if n < 0:
|
1353
|
+
raise ValueError('n cannot be negative')
|
1354
|
+
|
1355
|
+
K = GF(q, 'a')
|
1356
|
+
|
1357
|
+
T = PolynomialRing(K, 'x')
|
1358
|
+
primitive = T.irreducible_element(n, algorithm='random')
|
1359
|
+
while not primitive.is_primitive():
|
1360
|
+
primitive = T.irreducible_element(n, algorithm='random')
|
1361
|
+
coeffs = primitive.coefficients()
|
1362
|
+
exps = primitive.exponents()
|
1363
|
+
|
1364
|
+
period = q**n - 1
|
1365
|
+
seq_len = period*2 if check else period
|
1366
|
+
seq = [1] + [0]*(n-1)
|
1367
|
+
|
1368
|
+
while len(seq) < seq_len:
|
1369
|
+
nxt = 0
|
1370
|
+
for i, coeff in zip(exps[1:], coeffs[1:]):
|
1371
|
+
nxt += coeff * seq[-i]
|
1372
|
+
seq.append(-coeffs[0].inverse() * nxt)
|
1373
|
+
|
1374
|
+
if check:
|
1375
|
+
assert _is_periodic_sequence(seq, period)
|
1376
|
+
return seq[:period]
|
1377
|
+
|
1378
|
+
|
1379
|
+
def _get_submodule_of_order(G, order):
|
1380
|
+
r"""
|
1381
|
+
Construct a submodule of the given order from group ``G``.
|
1382
|
+
|
1383
|
+
This method tries to construct submodules from various elements of `G` until
|
1384
|
+
a submodule of the correct order is found.
|
1385
|
+
|
1386
|
+
INPUT:
|
1387
|
+
|
1388
|
+
- ``G`` -- an additive abelian group
|
1389
|
+
- ``order`` -- integer; the order of the desired submodule
|
1390
|
+
|
1391
|
+
TESTS:
|
1392
|
+
|
1393
|
+
sage: # needs sage.modules
|
1394
|
+
sage: from sage.combinat.designs.difference_family import _get_submodule_of_order
|
1395
|
+
sage: G = AdditiveAbelianGroup([48])
|
1396
|
+
sage: _get_submodule_of_order(G, 6).order()
|
1397
|
+
6
|
1398
|
+
sage: G = AdditiveAbelianGroup([13^2 - 1])
|
1399
|
+
sage: _get_submodule_of_order(G, 12).order()
|
1400
|
+
12
|
1401
|
+
"""
|
1402
|
+
for el in G:
|
1403
|
+
H = G.submodule([el])
|
1404
|
+
if H.order() == order:
|
1405
|
+
return H
|
1406
|
+
return None
|
1407
|
+
|
1408
|
+
|
1409
|
+
def relative_difference_set_from_m_sequence(q, N, check=True, return_group=False):
|
1410
|
+
r"""
|
1411
|
+
Construct `R((q^N-1)/(q-1), q-1, q^{N-1}, q^{N-2})` where ``q`` is a prime power and `N\ge 2`.
|
1412
|
+
|
1413
|
+
The relative difference set is constructed over the set of additive integers modulo `q^N-1`,
|
1414
|
+
as described in Theorem 5.1 of [EB1966]_. Given an m-sequence `(a_i)` of period `q^N-1`, the
|
1415
|
+
set is: `R=\{i | 0 \le i \le q^{N-1}, a_i=1\}`.
|
1416
|
+
|
1417
|
+
INPUT:
|
1418
|
+
|
1419
|
+
- ``q`` -- a prime power
|
1420
|
+
- ``N`` -- a nonnegative number
|
1421
|
+
- ``check`` -- boolean (default: ``True``); if ``True``, check that the
|
1422
|
+
result is a relative difference set before returning it
|
1423
|
+
- ``return_group`` -- boolean (default: ``False``); if ``True``, the function
|
1424
|
+
will also return the group from which the set is created
|
1425
|
+
|
1426
|
+
OUTPUT:
|
1427
|
+
|
1428
|
+
If ``return_group=False``, the function return only the relative difference
|
1429
|
+
set. Otherwise, it returns a tuple containing the group and the set.
|
1430
|
+
|
1431
|
+
EXAMPLES::
|
1432
|
+
|
1433
|
+
sage: from sage.combinat.designs.difference_family import relative_difference_set_from_m_sequence
|
1434
|
+
sage: relative_difference_set_from_m_sequence(2, 4, # random # needs sage.modules sage.rings.finite_rings
|
1435
|
+
....: return_group=True)
|
1436
|
+
(Additive abelian group isomorphic to Z/15,
|
1437
|
+
[(0), (4), (5), (6), (7), (9), (11), (12)])
|
1438
|
+
sage: relative_difference_set_from_m_sequence(8, 2, check=False) # random # needs sage.modules sage.rings.finite_rings
|
1439
|
+
[(0), (6), (30), (40), (41), (44), (56), (61)]
|
1440
|
+
sage: relative_difference_set_from_m_sequence(6, 2) # needs sage.modules
|
1441
|
+
Traceback (most recent call last):
|
1442
|
+
...
|
1443
|
+
ValueError: q must be a prime power
|
1444
|
+
|
1445
|
+
TESTS::
|
1446
|
+
|
1447
|
+
sage: from sage.combinat.designs.difference_family import is_relative_difference_set, _get_submodule_of_order
|
1448
|
+
sage: q, N = 5, 3
|
1449
|
+
sage: G, D = relative_difference_set_from_m_sequence(q, N, check=False, # needs sage.modules sage.rings.finite_rings
|
1450
|
+
....: return_group=True)
|
1451
|
+
sage: H = _get_submodule_of_order(G, q-1) # needs sage.modules sage.rings.finite_rings
|
1452
|
+
sage: is_relative_difference_set(D, G, H, # needs sage.modules sage.rings.finite_rings
|
1453
|
+
....: ((q^N-1)//(q-1), q-1, q^(N-1), q^(N-2)))
|
1454
|
+
True
|
1455
|
+
sage: q, N = 13, 2
|
1456
|
+
sage: G, D = relative_difference_set_from_m_sequence(q, N, check=False, # needs sage.modules sage.rings.finite_rings
|
1457
|
+
....: return_group=True)
|
1458
|
+
sage: H = _get_submodule_of_order(G, q-1) # needs sage.modules sage.rings.finite_rings
|
1459
|
+
sage: is_relative_difference_set(D, G, H, # needs sage.modules sage.rings.finite_rings
|
1460
|
+
....: ((q^N-1)//(q-1), q-1, q^(N-1), q^(N-2)))
|
1461
|
+
True
|
1462
|
+
"""
|
1463
|
+
from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup
|
1464
|
+
|
1465
|
+
if not is_prime_power(q):
|
1466
|
+
raise ValueError('q must be a prime power')
|
1467
|
+
if N < 2:
|
1468
|
+
raise ValueError('N must be at least 2')
|
1469
|
+
|
1470
|
+
m_seq = _create_m_sequence(q, N, check=False)
|
1471
|
+
period = q**N - 1
|
1472
|
+
G = AdditiveAbelianGroup([period])
|
1473
|
+
|
1474
|
+
set1 = [i for i in G if m_seq[i[0]] == 1]
|
1475
|
+
|
1476
|
+
if check:
|
1477
|
+
H = _get_submodule_of_order(G, q-1)
|
1478
|
+
assert is_relative_difference_set(set1, G, H, (period // (q-1), q - 1, q**(N-1), q**(N-2)))
|
1479
|
+
|
1480
|
+
if return_group:
|
1481
|
+
return G, set1
|
1482
|
+
return set1
|
1483
|
+
|
1484
|
+
|
1485
|
+
def relative_difference_set_from_homomorphism(q, N, d, check=True, return_group=False):
|
1486
|
+
r"""
|
1487
|
+
Construct `R((q^N-1)/(q-1), n, q^{N-1}, q^{N-2}d)` where `nd = q-1`.
|
1488
|
+
|
1489
|
+
Given a prime power `q`, a number `N \ge 2` and integers `d` such that `d | q-1` we create the
|
1490
|
+
relative difference set using the construction from Corollary 5.1.1 of [EB1966]_.
|
1491
|
+
|
1492
|
+
INPUT:
|
1493
|
+
|
1494
|
+
- ``q`` -- a prime power
|
1495
|
+
- ``N`` -- integer greater than 1
|
1496
|
+
- ``d`` -- integer which divides `q-1`
|
1497
|
+
- ``check`` -- boolean (default: ``True``); if ``True``, check that the
|
1498
|
+
result is a relative difference set before returning it
|
1499
|
+
- ``return_group`` -- boolean (default: ``False``); if ``True``, the function
|
1500
|
+
will also return the group from which the set is created
|
1501
|
+
|
1502
|
+
OUTPUT:
|
1503
|
+
|
1504
|
+
If ``return_group=False``, the function return only the relative difference
|
1505
|
+
set. Otherwise, it returns a tuple containing the group and the set.
|
1506
|
+
|
1507
|
+
EXAMPLES::
|
1508
|
+
|
1509
|
+
sage: from sage.combinat.designs.difference_family import relative_difference_set_from_homomorphism
|
1510
|
+
sage: relative_difference_set_from_homomorphism(7, 2, 3) # random # needs sage.modules sage.rings.finite_rings
|
1511
|
+
[(0), (3), (4), (2), (13), (7), (14)]
|
1512
|
+
sage: relative_difference_set_from_homomorphism(9, 2, 4, # random # needs sage.modules sage.rings.finite_rings
|
1513
|
+
....: check=False, return_group=True)
|
1514
|
+
(Additive abelian group isomorphic to Z/80,
|
1515
|
+
[(0), (4), (6), (13), (7), (12), (15), (8), (9)])
|
1516
|
+
sage: relative_difference_set_from_homomorphism(9, 2, 5) # needs sage.modules sage.rings.finite_rings
|
1517
|
+
Traceback (most recent call last):
|
1518
|
+
...
|
1519
|
+
ValueError: q-1 must be a multiple of d
|
1520
|
+
|
1521
|
+
TESTS::
|
1522
|
+
|
1523
|
+
sage: from sage.combinat.designs.difference_family import is_relative_difference_set, _get_submodule_of_order
|
1524
|
+
sage: q, N, d = 11, 2, 5
|
1525
|
+
sage: G, D = relative_difference_set_from_homomorphism(q, N, d, check=False, # needs sage.modules sage.rings.finite_rings
|
1526
|
+
....: return_group=True)
|
1527
|
+
sage: H = _get_submodule_of_order(G, (q-1)//d) # needs sage.modules sage.rings.finite_rings
|
1528
|
+
sage: is_relative_difference_set(D, G, H, # needs sage.modules sage.rings.finite_rings
|
1529
|
+
....: ((q**N-1)//(q-1), (q-1)//d, q**(N-1), q**(N-2)*d))
|
1530
|
+
True
|
1531
|
+
sage: q, N, d = 9, 2, 4
|
1532
|
+
sage: G, D = relative_difference_set_from_homomorphism(q, N, d, check=False, # needs sage.modules sage.rings.finite_rings
|
1533
|
+
....: return_group=True)
|
1534
|
+
sage: H = _get_submodule_of_order(G, (q-1)//d) # needs sage.modules sage.rings.finite_rings
|
1535
|
+
sage: is_relative_difference_set(D, G, H, # needs sage.modules sage.rings.finite_rings
|
1536
|
+
....: ((q**N-1)//(q-1), (q-1)//d, q**(N-1), q**(N-2)*d))
|
1537
|
+
True
|
1538
|
+
"""
|
1539
|
+
from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup
|
1540
|
+
|
1541
|
+
if not is_prime_power(q):
|
1542
|
+
raise ValueError('q must be a prime power')
|
1543
|
+
if N < 2:
|
1544
|
+
raise ValueError('N must be at least 2')
|
1545
|
+
if (q-1) % d != 0:
|
1546
|
+
raise ValueError('q-1 must be a multiple of d')
|
1547
|
+
|
1548
|
+
G = AdditiveAbelianGroup([q**N - 1])
|
1549
|
+
K = _get_submodule_of_order(G, d)
|
1550
|
+
assert K is not None, 'Could not find kernel'
|
1551
|
+
|
1552
|
+
G2 = G/K
|
1553
|
+
|
1554
|
+
theta = G.hom([G2.gen(0)], G2)
|
1555
|
+
diff_set = relative_difference_set_from_m_sequence(q, N, check=False)
|
1556
|
+
second_diff_set = [theta(x) for x in diff_set]
|
1557
|
+
|
1558
|
+
if check:
|
1559
|
+
H = _get_submodule_of_order(G2, (q-1) // d)
|
1560
|
+
assert is_relative_difference_set(second_diff_set, G2, H, ((q**N-1) // (q-1), (q-1) // d, q**(N-1), q**(N-2) * d))
|
1561
|
+
|
1562
|
+
if return_group:
|
1563
|
+
return G2, second_diff_set
|
1564
|
+
return second_diff_set
|
1565
|
+
|
1566
|
+
|
1567
|
+
def is_relative_difference_set(R, G, H, params, verbose=False):
|
1568
|
+
r"""
|
1569
|
+
Check if ``R`` is a difference set of ``G`` relative to ``H``, with the given parameters.
|
1570
|
+
|
1571
|
+
This function checks that `G`, `H` and `R` have the orders specified in the parameters, and
|
1572
|
+
that `R` satisfies the definition of relative difference set (from [EB1966]_): the collection of
|
1573
|
+
differences `r-s`, `r,s \in R`, `r \neq s` contains only elements of `G` which are not in `H`, and contains
|
1574
|
+
every such element exactly `d` times.
|
1575
|
+
|
1576
|
+
INPUT:
|
1577
|
+
|
1578
|
+
- ``R`` -- list; the relative diffeence set of length `k`
|
1579
|
+
- ``G`` -- an additive abelian group of order `mn`
|
1580
|
+
- ``H`` -- list; a submodule of ``G`` of order `n`
|
1581
|
+
- ``params`` -- tuple in the form `(m, n, k, d)`
|
1582
|
+
- ``verbose`` -- boolean (default: ``False``); if ``True``, the function
|
1583
|
+
will be verbose when the sequences do not satisfy the constraints
|
1584
|
+
|
1585
|
+
EXAMPLES::
|
1586
|
+
|
1587
|
+
sage: from sage.combinat.designs.difference_family import _get_submodule_of_order, relative_difference_set_from_m_sequence, is_relative_difference_set
|
1588
|
+
sage: q, N = 5, 2
|
1589
|
+
sage: params = ((q^N-1) // (q-1), q - 1, q^(N-1), q^(N-2))
|
1590
|
+
sage: G, R = relative_difference_set_from_m_sequence(q, N, return_group=True) # needs sage.libs.pari sage.modules
|
1591
|
+
sage: H = _get_submodule_of_order(G, q - 1) # needs sage.libs.pari sage.modules
|
1592
|
+
sage: is_relative_difference_set(R, G, H, params) # needs sage.libs.pari sage.modules
|
1593
|
+
True
|
1594
|
+
|
1595
|
+
If we pass the ``verbose`` argument, the function will explain why it failed::
|
1596
|
+
|
1597
|
+
sage: R2 = [G[1], G[2], G[3], G[5], G[6]] # needs sage.libs.pari sage.modules
|
1598
|
+
sage: is_relative_difference_set(R2, G, H, params, verbose=True) # needs sage.libs.pari sage.modules
|
1599
|
+
There is a value in the difference set which is not repeated d times
|
1600
|
+
False
|
1601
|
+
"""
|
1602
|
+
m, n, k, d = params
|
1603
|
+
if G.order() != m * n:
|
1604
|
+
if verbose:
|
1605
|
+
print('Incorrect order of G:', G.order())
|
1606
|
+
return False
|
1607
|
+
|
1608
|
+
if H.order() != n:
|
1609
|
+
if verbose:
|
1610
|
+
print('Incorect order of H:', H.order())
|
1611
|
+
|
1612
|
+
if len(R) != k:
|
1613
|
+
if verbose:
|
1614
|
+
print('Length of R not correct:', len(R))
|
1615
|
+
return False
|
1616
|
+
|
1617
|
+
diff_set = {}
|
1618
|
+
for el1 in R:
|
1619
|
+
for el2 in R:
|
1620
|
+
if el1 != el2:
|
1621
|
+
idx = el1 - el2
|
1622
|
+
if idx not in diff_set:
|
1623
|
+
diff_set[idx] = 0
|
1624
|
+
diff_set[idx] += 1
|
1625
|
+
values = [diff_set[x] for x in diff_set]
|
1626
|
+
if max(values) != d or min(values) != d:
|
1627
|
+
if verbose:
|
1628
|
+
print('There is a value in the difference set which is not repeated d times')
|
1629
|
+
return False
|
1630
|
+
|
1631
|
+
for el in G:
|
1632
|
+
if el in H and el in diff_set:
|
1633
|
+
if verbose:
|
1634
|
+
print('An element of G is present in both the difference set and in H')
|
1635
|
+
return False
|
1636
|
+
if el not in H and el not in diff_set:
|
1637
|
+
if verbose:
|
1638
|
+
print('An element of G is not present in either one of H or the difference set')
|
1639
|
+
return False
|
1640
|
+
|
1641
|
+
return True
|
1642
|
+
|
1643
|
+
|
1644
|
+
def is_supplementary_difference_set(Ks, v=None, lmbda=None, G=None, verbose=False):
|
1645
|
+
r"""
|
1646
|
+
Check that the sets in ``Ks`` are `n-\{v; k_1, ..., k_n; \lambda \}` supplementary
|
1647
|
+
difference sets over group ``G`` of order ``v``.
|
1648
|
+
|
1649
|
+
From the definition in [Spe1975]_: let `S_1, S_2, ..., S_n` be `n` subsets of a group `G` of order `v`
|
1650
|
+
such that `|S_i| = k_i`. If, for each `g \in G`, `g \neq 0`, the total number of solutions of `a_i - a'_i = g`, with
|
1651
|
+
`a_i, a'_i \in S_i` is `\lambda`, then `S_1, S_2, ..., S_n` are `n-\{v; k_1, ..., k_n; \lambda\}` supplementary difference sets.
|
1652
|
+
|
1653
|
+
One of the parameters ``v`` or ``G`` must always be specified. If ``G`` is not
|
1654
|
+
given, the function will use an ``AdditiveAbelianGroup`` of order ``v``.
|
1655
|
+
|
1656
|
+
INPUT:
|
1657
|
+
|
1658
|
+
- ``Ks`` -- list of sets to be checked
|
1659
|
+
- ``v`` -- integer; the parameter `v` of the supplementary difference sets
|
1660
|
+
- ``lmbda`` -- integer; the parameter `\lambda` of the supplementary difference sets
|
1661
|
+
- ``G`` -- a group of order `v`
|
1662
|
+
- ``verbose`` -- boolean (default: ``False``); if ``True``, the function will
|
1663
|
+
be verbose when the sets do not satisfy the constraints
|
1664
|
+
|
1665
|
+
EXAMPLES::
|
1666
|
+
|
1667
|
+
sage: from sage.combinat.designs.difference_family import supplementary_difference_set_from_rel_diff_set, is_supplementary_difference_set
|
1668
|
+
sage: G, [S1, S2, S3, S4] = supplementary_difference_set_from_rel_diff_set(17) # needs sage.modules sage.rings.finite_rings
|
1669
|
+
sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=16, G=G) # needs sage.modules sage.rings.finite_rings
|
1670
|
+
True
|
1671
|
+
|
1672
|
+
The parameter ``v`` can be given instead of ``G``::
|
1673
|
+
|
1674
|
+
sage: is_supplementary_difference_set([S1, S2, S3, S4], v=16, lmbda=16) # needs sage.modules sage.rings.finite_rings
|
1675
|
+
True
|
1676
|
+
sage: is_supplementary_difference_set([S1, S2, S3, S4], v=20, lmbda=16) # needs sage.modules sage.rings.finite_rings
|
1677
|
+
False
|
1678
|
+
|
1679
|
+
If ``verbose=True``, the function will be verbose::
|
1680
|
+
|
1681
|
+
sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=14, G=G, # needs sage.modules sage.rings.finite_rings
|
1682
|
+
....: verbose=True)
|
1683
|
+
Number of pairs with difference (1) is 16, but lambda is 14
|
1684
|
+
False
|
1685
|
+
|
1686
|
+
TESTS::
|
1687
|
+
|
1688
|
+
sage: # needs sage.modules sage.rings.finite_rings
|
1689
|
+
sage: is_supplementary_difference_set([[1], [1]], lmbda=0, G=Zmod(3))
|
1690
|
+
True
|
1691
|
+
sage: is_supplementary_difference_set([S1, S2, S3, S4], v=17, lmbda=16, G=G)
|
1692
|
+
False
|
1693
|
+
sage: is_supplementary_difference_set([S1, S2, S3, S4], G=G)
|
1694
|
+
True
|
1695
|
+
sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=16)
|
1696
|
+
Traceback (most recent call last):
|
1697
|
+
...
|
1698
|
+
ValueError: one of G or v must be specified
|
1699
|
+
|
1700
|
+
.. SEEALSO::
|
1701
|
+
|
1702
|
+
:func:`supplementary_difference_set_from_rel_diff_set`
|
1703
|
+
"""
|
1704
|
+
|
1705
|
+
if G is None and v is None:
|
1706
|
+
raise ValueError('one of G or v must be specified')
|
1707
|
+
|
1708
|
+
if G is None:
|
1709
|
+
from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup
|
1710
|
+
G = AdditiveAbelianGroup([v])
|
1711
|
+
|
1712
|
+
if v is not None and G.order() != v:
|
1713
|
+
if verbose:
|
1714
|
+
print(f'G has order {G.order()}, but it should be {v}')
|
1715
|
+
return False
|
1716
|
+
|
1717
|
+
differences_counter = {el: 0 for el in G}
|
1718
|
+
for K in Ks:
|
1719
|
+
for el1 in K:
|
1720
|
+
for el2 in K:
|
1721
|
+
diff = G(el1) - G(el2)
|
1722
|
+
differences_counter[diff] += 1
|
1723
|
+
|
1724
|
+
for key, diff in differences_counter.items():
|
1725
|
+
if key == 0:
|
1726
|
+
continue
|
1727
|
+
if lmbda is None:
|
1728
|
+
lmbda = diff
|
1729
|
+
if diff != lmbda:
|
1730
|
+
if verbose:
|
1731
|
+
print(f'Number of pairs with difference {key} is {diff}, but lambda is {lmbda}')
|
1732
|
+
return False
|
1733
|
+
|
1734
|
+
return True
|
1735
|
+
|
1736
|
+
|
1737
|
+
def supplementary_difference_set_from_rel_diff_set(q, existence=False, check=True):
|
1738
|
+
r"""
|
1739
|
+
Construct `4-\{2v; v, v+1, v, v; 2v\}` supplementary difference sets where `q=2v+1`.
|
1740
|
+
|
1741
|
+
The sets are created from relative difference sets as detailed in Theorem 3.3 of [Spe1975]_. this construction
|
1742
|
+
requires that `q` is an odd prime power and that there exists `s \ge 0` such that `(q-(2^{s+1}+1))/2^{s+1}` is
|
1743
|
+
an odd prime power.
|
1744
|
+
|
1745
|
+
Note that the construction from [Spe1975]_ states that the resulting sets are `4-\{2v; v+1, v, v, v; 2v\}`
|
1746
|
+
supplementary difference sets. However, the implementation of that construction returns
|
1747
|
+
`4-\{2v; v, v+1, v, v; 2v\}` supplementary difference sets. This is not important, since the supplementary
|
1748
|
+
difference sets are not ordered.
|
1749
|
+
|
1750
|
+
INPUT:
|
1751
|
+
|
1752
|
+
- ``q`` -- an odd prime power
|
1753
|
+
- ``existence`` -- boolean (default: ``False``); if ``True``, only check
|
1754
|
+
whether the supplementary difference sets can be constructed
|
1755
|
+
- ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
|
1756
|
+
are supplementary difference sets before returning them
|
1757
|
+
|
1758
|
+
OUTPUT:
|
1759
|
+
|
1760
|
+
If ``existence=False``, the function returns the 4 sets (containing integers),
|
1761
|
+
or raises an error if ``q`` does not satisfy the constraints.
|
1762
|
+
If ``existence=True``, the function returns a boolean representing whether
|
1763
|
+
supplementary difference sets can be constructed.
|
1764
|
+
|
1765
|
+
EXAMPLES::
|
1766
|
+
|
1767
|
+
sage: from sage.combinat.designs.difference_family import supplementary_difference_set_from_rel_diff_set
|
1768
|
+
sage: supplementary_difference_set_from_rel_diff_set(17) #random # needs sage.libs.pari
|
1769
|
+
(Additive abelian group isomorphic to Z/16,
|
1770
|
+
[[(1), (5), (6), (7), (9), (13), (14), (15)],
|
1771
|
+
[(0), (2), (3), (5), (6), (10), (11), (13), (14)],
|
1772
|
+
[(0), (1), (2), (3), (5), (6), (7), (12)],
|
1773
|
+
[(0), (2), (3), (5), (6), (7), (9), (12)]])
|
1774
|
+
|
1775
|
+
If ``existence=True``, the function returns a boolean::
|
1776
|
+
|
1777
|
+
sage: supplementary_difference_set_from_rel_diff_set(7, existence=True)
|
1778
|
+
False
|
1779
|
+
sage: supplementary_difference_set_from_rel_diff_set(17, existence=True)
|
1780
|
+
True
|
1781
|
+
|
1782
|
+
TESTS::
|
1783
|
+
|
1784
|
+
sage: # needs sage.libs.pari
|
1785
|
+
sage: from sage.combinat.designs.difference_family import is_supplementary_difference_set
|
1786
|
+
sage: G, sets = supplementary_difference_set_from_rel_diff_set(17, check=False)
|
1787
|
+
sage: is_supplementary_difference_set(sets, lmbda=16, G=G)
|
1788
|
+
True
|
1789
|
+
sage: G, sets = supplementary_difference_set_from_rel_diff_set(9, check=False)
|
1790
|
+
sage: is_supplementary_difference_set(sets, lmbda=8, G=G)
|
1791
|
+
True
|
1792
|
+
sage: supplementary_difference_set_from_rel_diff_set(7)
|
1793
|
+
Traceback (most recent call last):
|
1794
|
+
...
|
1795
|
+
ValueError: There is no s for which m-1 is an odd prime power
|
1796
|
+
sage: supplementary_difference_set_from_rel_diff_set(8)
|
1797
|
+
Traceback (most recent call last):
|
1798
|
+
...
|
1799
|
+
ValueError: q must be an odd prime power
|
1800
|
+
sage: supplementary_difference_set_from_rel_diff_set(8, existence=True)
|
1801
|
+
False
|
1802
|
+
sage: supplementary_difference_set_from_rel_diff_set(7, existence=True)
|
1803
|
+
False
|
1804
|
+
sage: supplementary_difference_set_from_rel_diff_set(1, existence=True)
|
1805
|
+
False
|
1806
|
+
|
1807
|
+
Check that the function works even when s > 1::
|
1808
|
+
|
1809
|
+
sage: G, sets = supplementary_difference_set_from_rel_diff_set(353, check=False) # long time, needs sage.libs.pari
|
1810
|
+
sage: is_supplementary_difference_set(sets, lmbda=352, G=G) # long time, needs sage.libs.pari
|
1811
|
+
True
|
1812
|
+
|
1813
|
+
.. SEEALSO::
|
1814
|
+
|
1815
|
+
:func:`is_supplementary_difference_set`
|
1816
|
+
"""
|
1817
|
+
s = 0
|
1818
|
+
m = -1
|
1819
|
+
|
1820
|
+
while q > 2**(s+1) and (q-1) % 2**(s+1) == 0:
|
1821
|
+
prime_pow = (q-1)//2**(s+1) - 1
|
1822
|
+
if is_prime_power(prime_pow) and prime_pow % 2 == 1:
|
1823
|
+
m = (q - (2**(s+1) + 1)) // 2**(s+1) + 1
|
1824
|
+
break
|
1825
|
+
s += 1
|
1826
|
+
|
1827
|
+
if existence:
|
1828
|
+
return is_prime_power(q) and q % 2 == 1 and m != -1
|
1829
|
+
|
1830
|
+
if not is_prime_power(q) or q % 2 != 1:
|
1831
|
+
raise ValueError('q must be an odd prime power')
|
1832
|
+
if m == -1:
|
1833
|
+
raise ValueError('There is no s for which m-1 is an odd prime power')
|
1834
|
+
|
1835
|
+
set1 = relative_difference_set_from_homomorphism(m - 1, 2, (m-2) // 2, check=False)
|
1836
|
+
|
1837
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
1838
|
+
P = PolynomialRing(ZZ, 'x')
|
1839
|
+
|
1840
|
+
# Compute psi3, psi4
|
1841
|
+
hall = 0
|
1842
|
+
for d in set1:
|
1843
|
+
hall += P.monomial(d[0])
|
1844
|
+
|
1845
|
+
def get_T(k):
|
1846
|
+
T = P.monomial(0) - 1
|
1847
|
+
for i in range(k):
|
1848
|
+
T += P.monomial(i)
|
1849
|
+
return T
|
1850
|
+
|
1851
|
+
modulo = P.monomial(2*m) - 1
|
1852
|
+
|
1853
|
+
diff = get_T(2*m) - (1+P.monomial(m))*hall
|
1854
|
+
diff = diff.mod(modulo)
|
1855
|
+
exp1, exp2 = diff.exponents()
|
1856
|
+
a = (exp1+exp2-m) // 2
|
1857
|
+
|
1858
|
+
psi3 = (P.monomial(a) + hall).mod(modulo)
|
1859
|
+
psi4 = (P.monomial(a+m) + hall).mod(modulo)
|
1860
|
+
|
1861
|
+
for i in range(s):
|
1862
|
+
m_start = 2**i * m
|
1863
|
+
psi3, psi4 = (psi3(P.monomial(2)) + P.monomial(1)*psi4(P.monomial(2))).mod(P.monomial(4*m_start)-1), \
|
1864
|
+
(psi3(P.monomial(2)) + P.monomial(1)*(get_T(2*m_start)(P.monomial(2)) - psi4(P.monomial(2)))).mod(P.monomial(4*m_start)-1)
|
1865
|
+
|
1866
|
+
# Construction of psi1, psi2
|
1867
|
+
G2, set2 = relative_difference_set_from_m_sequence(q, 2, check=False, return_group=True)
|
1868
|
+
s3 = get_fixed_relative_difference_set(G2, set2)
|
1869
|
+
|
1870
|
+
phi_exps = []
|
1871
|
+
for i in range(len(s3)):
|
1872
|
+
for j in range(i+1, len(s3)):
|
1873
|
+
diff = s3[i] - s3[j]
|
1874
|
+
if diff % (q-1) == 0 and diff % (q**2-1) != 0:
|
1875
|
+
phi_exps.append(s3[i])
|
1876
|
+
|
1877
|
+
exps1 = [(x+1)//2 for x in phi_exps if x % 2 == 1]
|
1878
|
+
exps2 = [x//2 for x in phi_exps if x % 2 == 0]
|
1879
|
+
|
1880
|
+
theta1 = 0
|
1881
|
+
for exp in exps1:
|
1882
|
+
theta1 += P.monomial(exp)
|
1883
|
+
theta1 = theta1.mod(P.monomial(q-1)-1)
|
1884
|
+
|
1885
|
+
theta2 = 0
|
1886
|
+
for exp in exps2:
|
1887
|
+
theta2 += P.monomial(exp)
|
1888
|
+
theta2 = theta2.mod(P.monomial(q-1) - 1)
|
1889
|
+
|
1890
|
+
psi1 = ((1 + P.monomial((q-1)//2)) * theta1).mod(P.monomial(q-1) - 1)
|
1891
|
+
psi2 = (1 + (1 + P.monomial((q-1)//2)) * theta2).mod(P.monomial(q-1) - 1)
|
1892
|
+
|
1893
|
+
from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup
|
1894
|
+
G = AdditiveAbelianGroup([q-1])
|
1895
|
+
K1 = [G[x] for x in psi1.exponents()]
|
1896
|
+
K2 = [G[x] for x in psi2.exponents()]
|
1897
|
+
K3 = [G[x] for x in psi3.exponents()]
|
1898
|
+
K4 = [G[x] for x in psi4.exponents()]
|
1899
|
+
|
1900
|
+
if check:
|
1901
|
+
assert is_supplementary_difference_set([K1, K2, K3, K4], lmbda=q-1, G=G)
|
1902
|
+
|
1903
|
+
return G, [K1, K2, K3, K4]
|
1904
|
+
|
1905
|
+
|
1906
|
+
def supplementary_difference_set(q, existence=False, check=True):
|
1907
|
+
r"""
|
1908
|
+
Construct `4-\{2v; v, v+1, v, v; 2v\}` supplementary difference sets where `q=2v+1`.
|
1909
|
+
|
1910
|
+
This is a deprecated version of :func:`supplementary_difference_set_from_rel_diff_set`,
|
1911
|
+
please use that instead.
|
1912
|
+
"""
|
1913
|
+
from sage.misc.superseded import deprecation
|
1914
|
+
deprecation(35211, 'This function is deprecated, please use supplementary_difference_set_from_rel_diff_set instead.')
|
1915
|
+
|
1916
|
+
if existence:
|
1917
|
+
return supplementary_difference_set_from_rel_diff_set(q, existence=True)
|
1918
|
+
_, s = supplementary_difference_set_from_rel_diff_set(q, check=check)
|
1919
|
+
return s
|
1920
|
+
|
1921
|
+
|
1922
|
+
def get_fixed_relative_difference_set(G, rel_diff_set, as_elements=False):
|
1923
|
+
r"""
|
1924
|
+
Construct an equivalent relative difference set fixed by the size of the set.
|
1925
|
+
|
1926
|
+
Given a relative difference set `R(q+1, q-1, q, 1)`, it is possible to find a translation
|
1927
|
+
of this set fixed by `q` (see Section 3 of [Spe1975]_). We say that a set is fixed by `t` if
|
1928
|
+
`\{td | d\in R\}= R`.
|
1929
|
+
|
1930
|
+
In addition, the set returned by this function will contain the element `0`. This is needed in the
|
1931
|
+
construction of supplementary difference sets (see :func:`supplementary_difference_set_from_rel_diff_set`).
|
1932
|
+
|
1933
|
+
INPUT:
|
1934
|
+
|
1935
|
+
- ``G`` -- a group, of which ``rel_diff_set`` is a subset
|
1936
|
+
- ``rel_diff_set`` -- the relative difference set
|
1937
|
+
- ``as_elements`` -- boolean (default: ``False``); if ``True``, the list
|
1938
|
+
returned will contain elements of the abelian group (this may slow down
|
1939
|
+
the computation considerably)
|
1940
|
+
|
1941
|
+
OUTPUT:
|
1942
|
+
|
1943
|
+
By default, this function returns the set as a list of integers. However, if
|
1944
|
+
``as_elements=True`` it will return the set as a list containing elements of
|
1945
|
+
the abelian group.
|
1946
|
+
If no such set can be found, the function will raise an error.
|
1947
|
+
|
1948
|
+
EXAMPLES::
|
1949
|
+
|
1950
|
+
sage: from sage.combinat.designs.difference_family import relative_difference_set_from_m_sequence, get_fixed_relative_difference_set
|
1951
|
+
sage: G, s1 = relative_difference_set_from_m_sequence(5, 2, return_group=True) # needs sage.libs.pari sage.modules
|
1952
|
+
sage: get_fixed_relative_difference_set(G, s1) # random # needs sage.libs.pari sage.modules
|
1953
|
+
[2, 10, 19, 23, 0]
|
1954
|
+
|
1955
|
+
If ``as_elements=True``, the result will contain elements of the group::
|
1956
|
+
|
1957
|
+
sage: get_fixed_relative_difference_set(G, s1, as_elements=True) # random # needs sage.libs.pari sage.modules
|
1958
|
+
[(2), (10), (19), (23), (0)]
|
1959
|
+
|
1960
|
+
TESTS::
|
1961
|
+
|
1962
|
+
sage: # needs sage.libs.pari sage.modules
|
1963
|
+
sage: from sage.combinat.designs.difference_family import is_fixed_relative_difference_set
|
1964
|
+
sage: G, s1 = relative_difference_set_from_m_sequence(5, 2, return_group=True)
|
1965
|
+
sage: s2 = get_fixed_relative_difference_set(G, s1, as_elements=True)
|
1966
|
+
sage: is_fixed_relative_difference_set(s2, len(s2))
|
1967
|
+
True
|
1968
|
+
sage: G, s1 = relative_difference_set_from_m_sequence(9, 2, return_group=True)
|
1969
|
+
sage: s2 = get_fixed_relative_difference_set(G, s1, as_elements=True)
|
1970
|
+
sage: is_fixed_relative_difference_set(s2, len(s2))
|
1971
|
+
True
|
1972
|
+
sage: type(s2[0])
|
1973
|
+
<class 'sage.groups.additive_abelian.additive_abelian_group.AdditiveAbelianGroup_fixed_gens_with_category.element_class'>
|
1974
|
+
sage: s2 = get_fixed_relative_difference_set(G, s1)
|
1975
|
+
sage: type(s2[0])
|
1976
|
+
<class 'sage.rings.integer.Integer'>
|
1977
|
+
"""
|
1978
|
+
q = len(rel_diff_set)
|
1979
|
+
|
1980
|
+
s2 = None
|
1981
|
+
for el in G:
|
1982
|
+
fixed_set = [el+x for x in rel_diff_set]
|
1983
|
+
if is_fixed_relative_difference_set(fixed_set, q):
|
1984
|
+
s2 = fixed_set
|
1985
|
+
break
|
1986
|
+
assert s2 is not None, 'Cannot find fixed translation of the set'
|
1987
|
+
|
1988
|
+
s3 = None
|
1989
|
+
for i in range(G.order()):
|
1990
|
+
temp = [((q+1)*i+x[0]) % G.order() for x in s2]
|
1991
|
+
if 0 in temp:
|
1992
|
+
s3 = temp
|
1993
|
+
break
|
1994
|
+
assert s3 is not None, 'Cannot find fixed set containing 0'
|
1995
|
+
|
1996
|
+
if as_elements:
|
1997
|
+
return [G[x] for x in s3]
|
1998
|
+
return s3
|
1999
|
+
|
2000
|
+
|
2001
|
+
def is_fixed_relative_difference_set(R, q):
|
2002
|
+
r"""
|
2003
|
+
Check if the relative difference set ``R`` is fixed by ``q``.
|
2004
|
+
|
2005
|
+
A relative difference set `R` is fixed by `q` if `\{qd | d \in R\}= R` (see Section 3 of [Spe1975]_).
|
2006
|
+
|
2007
|
+
INPUT:
|
2008
|
+
|
2009
|
+
- ``R`` -- list containing elements of an abelian group; the relative
|
2010
|
+
difference set
|
2011
|
+
- ``q`` -- integer
|
2012
|
+
|
2013
|
+
EXAMPLES::
|
2014
|
+
|
2015
|
+
sage: # needs sage.modules
|
2016
|
+
sage: from sage.combinat.designs.difference_family import relative_difference_set_from_m_sequence, get_fixed_relative_difference_set, is_fixed_relative_difference_set
|
2017
|
+
sage: G, s1 = relative_difference_set_from_m_sequence(7, 2, return_group=True) # needs sage.libs.pari
|
2018
|
+
sage: s2 = get_fixed_relative_difference_set(G, s1, as_elements=True) # needs sage.libs.pari
|
2019
|
+
sage: is_fixed_relative_difference_set(s2, len(s2)) # needs sage.libs.pari
|
2020
|
+
True
|
2021
|
+
sage: G = AdditiveAbelianGroup([15])
|
2022
|
+
sage: s3 = [G[1], G[2], G[3], G[4]]
|
2023
|
+
sage: is_fixed_relative_difference_set(s3, len(s3))
|
2024
|
+
False
|
2025
|
+
|
2026
|
+
If the relative difference set does not contain elements of the group, the method returns false::
|
2027
|
+
|
2028
|
+
sage: G, s1 = relative_difference_set_from_m_sequence(7, 2, return_group=True) # needs sage.libs.pari sage.modules
|
2029
|
+
sage: s2 = get_fixed_relative_difference_set(G, s1, as_elements=False) # needs sage.libs.pari sage.modules
|
2030
|
+
sage: is_fixed_relative_difference_set(s2, len(s2)) # needs sage.libs.pari sage.modules
|
2031
|
+
False
|
2032
|
+
"""
|
2033
|
+
for el in R:
|
2034
|
+
if q * el not in R:
|
2035
|
+
return False
|
2036
|
+
return True
|
2037
|
+
|
2038
|
+
|
2039
|
+
def skew_supplementary_difference_set_over_polynomial_ring(n, existence=False, check=True):
|
2040
|
+
r"""
|
2041
|
+
Construct skew supplementary difference sets over a polynomial ring of order ``n``.
|
2042
|
+
|
2043
|
+
The skew supplementary difference sets for `n=81, 169` are taken from [Djo1994a]_.
|
2044
|
+
|
2045
|
+
INPUT:
|
2046
|
+
|
2047
|
+
- ``n`` -- integer; the parameter of the supplementary difference sets
|
2048
|
+
- ``existence`` -- boolean (default: ``False``); if ``True``, only check
|
2049
|
+
whether the supplementary difference sets can be constructed
|
2050
|
+
- ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
|
2051
|
+
are supplementary difference sets with `S_1` skew before returning them;
|
2052
|
+
setting this parameter to ``False`` may speed up the computation considerably
|
2053
|
+
|
2054
|
+
OUTPUT:
|
2055
|
+
|
2056
|
+
If ``existence=False``, the function returns a Polynomial Ring of order ``n``
|
2057
|
+
and a list containing 4 sets, or raises an error if data for the given ``n``
|
2058
|
+
is not available.
|
2059
|
+
If ``existence=True``, the function returns a boolean representing whether
|
2060
|
+
skew supplementary difference sets can be constructed.
|
2061
|
+
|
2062
|
+
EXAMPLES::
|
2063
|
+
|
2064
|
+
sage: from sage.combinat.designs.difference_family import skew_supplementary_difference_set_over_polynomial_ring
|
2065
|
+
sage: G, [S1, S2, S3, S4] = skew_supplementary_difference_set_over_polynomial_ring(81) # needs sage.libs.pari
|
2066
|
+
|
2067
|
+
If ``existence=True``, the function returns a boolean::
|
2068
|
+
|
2069
|
+
sage: skew_supplementary_difference_set_over_polynomial_ring(81, existence=True)
|
2070
|
+
True
|
2071
|
+
sage: skew_supplementary_difference_set_over_polynomial_ring(17, existence=True)
|
2072
|
+
False
|
2073
|
+
|
2074
|
+
TESTS::
|
2075
|
+
|
2076
|
+
sage: skew_supplementary_difference_set_over_polynomial_ring(7)
|
2077
|
+
Traceback (most recent call last):
|
2078
|
+
...
|
2079
|
+
NotImplementedError: skew SDS of order 7 not yet implemented
|
2080
|
+
"""
|
2081
|
+
data = {
|
2082
|
+
81: (3, lambda x: x**4 - x**3 - 1, 16, 5,
|
2083
|
+
[1, 2, 4, 6, 8, 10, 12, 14], [1, 2, 3, 4, 10, 11, 13],
|
2084
|
+
[4, 5, 6, 8, 12, 13, 14], [2, 4, 5, 6, 7, 11, 12, 13, 15]),
|
2085
|
+
169: (13, lambda x: x**2 - 4*x + 6, 24, 7,
|
2086
|
+
[0, 2, 5, 7, 9, 10, 12, 15, 16, 18, 21, 22], [0, 1, 2, 7, 8, 9, 13, 14, 18, 20, 23],
|
2087
|
+
[1, 4, 6, 7, 9, 14, 16, 17, 20, 21, 23], [3, 5, 6, 9, 10, 12, 13, 14, 15, 17, 20])
|
2088
|
+
}
|
2089
|
+
|
2090
|
+
if existence:
|
2091
|
+
return n in data
|
2092
|
+
|
2093
|
+
if n not in data:
|
2094
|
+
raise NotImplementedError(f'skew SDS of order {n} not yet implemented')
|
2095
|
+
|
2096
|
+
mod, poly, exp, order, ind1, ind2, ind3, ind4 = data[n]
|
2097
|
+
|
2098
|
+
from sage.rings.finite_rings.integer_mod_ring import Zmod
|
2099
|
+
|
2100
|
+
Z3 = Zmod(mod)
|
2101
|
+
R = ZZ['x']
|
2102
|
+
x = R.gen()
|
2103
|
+
F = Z3.extension(poly(x))
|
2104
|
+
|
2105
|
+
H = [F.gen() ** (exp * i) for i in range(order)]
|
2106
|
+
|
2107
|
+
cosets = []
|
2108
|
+
for i in range((n - 1) // (2 * order)):
|
2109
|
+
cosets.append([F.gen()**i * el for el in H])
|
2110
|
+
cosets.append([-F.gen()**i * el for el in H])
|
2111
|
+
|
2112
|
+
def generate_set(index_set, cosets):
|
2113
|
+
return sum((cosets[idx] for idx in index_set), [])
|
2114
|
+
|
2115
|
+
S1 = generate_set(ind1, cosets)
|
2116
|
+
S2 = generate_set(ind2, cosets)
|
2117
|
+
S3 = generate_set(ind3, cosets)
|
2118
|
+
S4 = generate_set(ind4, cosets)
|
2119
|
+
|
2120
|
+
if check:
|
2121
|
+
lmbda = len(S1) + len(S2) + len(S3) + len(S4) - n
|
2122
|
+
assert is_supplementary_difference_set([S1, S2, S3, S4], lmbda=lmbda, G=F)
|
2123
|
+
assert _is_skew_set(F, S1)
|
2124
|
+
|
2125
|
+
return F, [S1, S2, S3, S4]
|
2126
|
+
|
2127
|
+
|
2128
|
+
def skew_supplementary_difference_set_with_paley_todd(n, existence=False, check=True):
|
2129
|
+
r"""
|
2130
|
+
Construct `4-\{n; n_1, n_2, n_3, n_4; \lambda\}` skew supplementary difference sets where `S_1` is the Paley-Todd difference set.
|
2131
|
+
|
2132
|
+
The skew SDS returned have the property that `n_1 + n_2 + n_3 + n_4 = n + \lambda`.
|
2133
|
+
|
2134
|
+
This construction is described in [DK2016]_. The function contains, for each
|
2135
|
+
value of `n`, a set `H` containing integers modulo `n`, and four sets `J, K, L`.
|
2136
|
+
Then, these are used to construct `(n; k_2, k_3, k_4; \lambda_2)` difference family,
|
2137
|
+
with `\lambda_2 = k_2 + k_3 + k_4 + (3n - 1) / 4`. Finally, these sets together
|
2138
|
+
with the Paley-Todd difference set form a skew supplementary difference set.
|
2139
|
+
|
2140
|
+
INPUT:
|
2141
|
+
|
2142
|
+
- ``n`` -- integer; the parameter of the supplementary difference set
|
2143
|
+
- ``existence`` -- boolean (default: ``False``); if ``True``, only check
|
2144
|
+
whether the supplementary difference sets can be constructed
|
2145
|
+
- ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
|
2146
|
+
are supplementary difference sets with `S_1` skew before returning them;
|
2147
|
+
setting this parameter to ``False`` may speed up the computation considerably
|
2148
|
+
|
2149
|
+
OUTPUT:
|
2150
|
+
|
2151
|
+
If ``existence=False``, the function returns the group G of integers modulo
|
2152
|
+
``n`` and a list containing 4 sets, or raises an error if data for the given
|
2153
|
+
``n`` is not available.
|
2154
|
+
If ``existence=True``, the function returns a boolean representing whether
|
2155
|
+
skew supplementary difference sets can be constructed.
|
2156
|
+
|
2157
|
+
EXAMPLES::
|
2158
|
+
|
2159
|
+
sage: from sage.combinat.designs.difference_family import skew_supplementary_difference_set_with_paley_todd
|
2160
|
+
sage: G, [S1, S2, S3, S4] = skew_supplementary_difference_set_with_paley_todd(239)
|
2161
|
+
|
2162
|
+
If existence is ``True``, the function returns a boolean::
|
2163
|
+
|
2164
|
+
sage: skew_supplementary_difference_set_with_paley_todd(239, existence=True)
|
2165
|
+
True
|
2166
|
+
sage: skew_supplementary_difference_set_with_paley_todd(17, existence=True)
|
2167
|
+
False
|
2168
|
+
|
2169
|
+
TESTS::
|
2170
|
+
|
2171
|
+
sage: skew_supplementary_difference_set_with_paley_todd(7)
|
2172
|
+
Traceback (most recent call last):
|
2173
|
+
...
|
2174
|
+
NotImplementedError: data for skew SDS of order 7 not yet implemented
|
2175
|
+
"""
|
2176
|
+
H_db = {
|
2177
|
+
239: [1, 10, 24, 44, 98, 100, 201],
|
2178
|
+
}
|
2179
|
+
|
2180
|
+
indices = {
|
2181
|
+
239: [[1, 3, 5, 6, 15, 17, 19, 28, 34, 38, 39, 57, 58, 63, 85, 95, 107],
|
2182
|
+
[1, 3, 4, 5, 15, 16, 17, 18, 19, 21, 23, 29, 35, 45, 58, 63],
|
2183
|
+
[0, 1, 4, 6, 7, 8, 13, 16, 18, 34, 35, 45, 47, 58, 63, 95]],
|
2184
|
+
}
|
2185
|
+
|
2186
|
+
if existence:
|
2187
|
+
return n in H_db
|
2188
|
+
|
2189
|
+
if n not in H_db:
|
2190
|
+
raise NotImplementedError(f'data for skew SDS of order {n} not yet implemented')
|
2191
|
+
|
2192
|
+
G = Zmod(n)
|
2193
|
+
H = {G(el) for el in H_db[n]}
|
2194
|
+
|
2195
|
+
def generate_subset(indices, H):
|
2196
|
+
return list({el * idx for el in H for idx in indices})
|
2197
|
+
|
2198
|
+
from sage.arith.misc import quadratic_residues
|
2199
|
+
|
2200
|
+
S1 = [G(el) for el in quadratic_residues(n) if el != 0]
|
2201
|
+
S2 = generate_subset(indices[n][0], H)
|
2202
|
+
S3 = generate_subset(indices[n][1], H)
|
2203
|
+
S4 = generate_subset(indices[n][2], H)
|
2204
|
+
|
2205
|
+
if check:
|
2206
|
+
lmbda = len(S1) + len(S2) + len(S3) + len(S4) - n
|
2207
|
+
assert is_supplementary_difference_set([S1, S2, S3, S4], lmbda=lmbda, G=G)
|
2208
|
+
assert _is_skew_set(G, S1)
|
2209
|
+
|
2210
|
+
return G, [S1, S2, S3, S4]
|
2211
|
+
|
2212
|
+
|
2213
|
+
def _construct_gs_difference_family_from_full(S1, S2, mu):
|
2214
|
+
r"""
|
2215
|
+
Construct a spin type Goethals-Seidel difference family given the first two
|
2216
|
+
sets.
|
2217
|
+
|
2218
|
+
This construction is described in [Djo2024]_. Given the first two sets
|
2219
|
+
`S_1, S_2`, and the multiplier `\mu`, the last two sets are computed as
|
2220
|
+
`S_3 = \mu S_2`, `S_4 =\mu S_3`.
|
2221
|
+
|
2222
|
+
The sets should contain elements of the additive group of integers modulo `n`.
|
2223
|
+
|
2224
|
+
INPUT:
|
2225
|
+
|
2226
|
+
- ``S1`` -- list of integer modulo `n`; the first set
|
2227
|
+
- ``S2`` -- list of integer modulo `n`; the second set
|
2228
|
+
|
2229
|
+
OUTPUT:
|
2230
|
+
|
2231
|
+
The Goethals-Seidel difference family ``(S_1, S_2, S_3, S_4)``.
|
2232
|
+
|
2233
|
+
TESTS::
|
2234
|
+
|
2235
|
+
sage: from sage.combinat.designs.difference_family import _construct_gs_difference_family_from_full
|
2236
|
+
sage: G = Zmod(9)
|
2237
|
+
sage: S1 = list(map(G, [0,3,6]))
|
2238
|
+
sage: S2 = list(map(G, [0, 1, 8]))
|
2239
|
+
sage: _construct_gs_difference_family_from_full(S1, S2, 4)
|
2240
|
+
[[0, 3, 6], [0, 1, 8], [0, 4, 5], [0, 7, 2]]
|
2241
|
+
"""
|
2242
|
+
S3 = [mu * el for el in S2]
|
2243
|
+
S4 = [mu * el for el in S3]
|
2244
|
+
return [S1, S2, S3, S4]
|
2245
|
+
|
2246
|
+
|
2247
|
+
def _construct_gs_difference_family_from_compact(rep1, rep2, H, mu):
|
2248
|
+
r"""
|
2249
|
+
Construct a spin type Goethals-Seidel difference family given a compact
|
2250
|
+
representation of the first two sets.
|
2251
|
+
|
2252
|
+
This construction is described in [Djo2024]_. Given a subgroup `H` of a group
|
2253
|
+
`G`, and two sets of representatives `rep_1, rep_2` we can construct the first
|
2254
|
+
two sets: `S_1 = \bigcup_{x \in rep_1} x H` and `S_2 = \bigcup_{x \in rep_2} x H`.
|
2255
|
+
The list ``H`` should contain elements of the additive group of integers
|
2256
|
+
modulo `n`.
|
2257
|
+
|
2258
|
+
The other two sets are constructed using
|
2259
|
+
:func:`_construct_gs_difference_family_from_full`.
|
2260
|
+
|
2261
|
+
INPUT:
|
2262
|
+
|
2263
|
+
- ``rep1`` -- list of integers; the first set of representatives
|
2264
|
+
- ``rep2`` -- list of integers; the second set of representatives
|
2265
|
+
- ``H`` -- list of integers modulo `n`; the subgroup used to generate the
|
2266
|
+
first two sets
|
2267
|
+
|
2268
|
+
OUTPUT:
|
2269
|
+
|
2270
|
+
The Goethals-Seidel difference family ``(S_1, S_2, S_3, S_4)``.
|
2271
|
+
|
2272
|
+
TESTS::
|
2273
|
+
|
2274
|
+
sage: from sage.combinat.designs.difference_family import _construct_gs_difference_family_from_compact, is_supplementary_difference_set
|
2275
|
+
sage: G = Zmod(61)
|
2276
|
+
sage: H = list(map(G, [1, 9, 20, 34, 58]))
|
2277
|
+
sage: rep1 = [3, 4, 5, 6, 8, 10]
|
2278
|
+
sage: rep2 = [0, 8, 10, 13, 23, 26]
|
2279
|
+
sage: [S1, S2, S3, S4] = _construct_gs_difference_family_from_compact(rep1, rep2, H, 13)
|
2280
|
+
sage: is_supplementary_difference_set([S1, S2, S3, S4], G=G)
|
2281
|
+
True
|
2282
|
+
"""
|
2283
|
+
S1 = set()
|
2284
|
+
S2 = set()
|
2285
|
+
for el in H:
|
2286
|
+
S1 = S1.union({x * el for x in rep1})
|
2287
|
+
S2 = S2.union({x * el for x in rep2})
|
2288
|
+
S1 = list(S1)
|
2289
|
+
S2 = list(S2)
|
2290
|
+
return _construct_gs_difference_family_from_full(S1, S2, mu)
|
2291
|
+
|
2292
|
+
|
2293
|
+
def spin_goethals_seidel_difference_family(n, existence=False, check=True):
|
2294
|
+
r"""
|
2295
|
+
Construct a spin type Goethals-Seidel difference family with parameters
|
2296
|
+
`(n; k_1, k_2, k_3, k_4; \lambda)`.
|
2297
|
+
|
2298
|
+
The construction is described in [Djo2024]_. This function contains, for
|
2299
|
+
each value of `n`, either a full representation of `S_1, S_2` together with
|
2300
|
+
the multiplier `\mu`, or a subgroup `H`, two sets of representatives, and the
|
2301
|
+
multiplier.
|
2302
|
+
This data is used to construct the difference family using the functions
|
2303
|
+
:func:`_construct_gs_difference_family_from_full` and
|
2304
|
+
:func:`_construct_gs_difference_family_from_compact`.
|
2305
|
+
|
2306
|
+
Additionally, this function also checks if a (skew) difference family can be
|
2307
|
+
constructed using :func:`skew_spin_goethals_seidel_difference_family`.
|
2308
|
+
|
2309
|
+
INPUT:
|
2310
|
+
|
2311
|
+
- ``n`` -- integer; the parameter of the GS difference family
|
2312
|
+
- ``existence`` -- boolean (default: ``False``); if ``True``, only check
|
2313
|
+
whether the difference family can be constructed
|
2314
|
+
- ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
|
2315
|
+
are a difference family before returning them;
|
2316
|
+
setting this parameter to ``False`` may speed up the computation considerably
|
2317
|
+
|
2318
|
+
OUTPUT:
|
2319
|
+
|
2320
|
+
If ``existence=False``, the function returns the group G of integers modulo
|
2321
|
+
``n`` and a list containing 4 sets, or raises an error if data for the given
|
2322
|
+
``n`` is not available.
|
2323
|
+
If ``existence=True``, the function returns a boolean representing whether
|
2324
|
+
the difference family can be constructed.
|
2325
|
+
|
2326
|
+
EXAMPLES::
|
2327
|
+
|
2328
|
+
sage: from sage.combinat.designs.difference_family import spin_goethals_seidel_difference_family
|
2329
|
+
sage: G, [S1, S2, S3, S4] = spin_goethals_seidel_difference_family(73)
|
2330
|
+
|
2331
|
+
If existence is ``True``, the function returns a boolean::
|
2332
|
+
|
2333
|
+
sage: spin_goethals_seidel_difference_family(73, existence=True)
|
2334
|
+
True
|
2335
|
+
sage: spin_goethals_seidel_difference_family(5, existence=True)
|
2336
|
+
False
|
2337
|
+
|
2338
|
+
TESTS::
|
2339
|
+
|
2340
|
+
sage: from sage.combinat.designs.difference_family import is_supplementary_difference_set
|
2341
|
+
sage: G, [S1, S2, S3, S4] = spin_goethals_seidel_difference_family(9, check=False)
|
2342
|
+
sage: lmbda = len(S1) + len(S2) + len(S3) + len(S4) - 9
|
2343
|
+
sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=lmbda, G=G)
|
2344
|
+
True
|
2345
|
+
sage: spin_goethals_seidel_difference_family(5)
|
2346
|
+
Traceback (most recent call last):
|
2347
|
+
...
|
2348
|
+
NotImplementedError: Data for spin type Goethals Seidel family of order 5 not yet implemented
|
2349
|
+
"""
|
2350
|
+
full_data = {
|
2351
|
+
7: ([0], [0, 1, 6], 2),
|
2352
|
+
9: ([0, 3, 6], [0, 1, 8], 4),
|
2353
|
+
13: ([0, 1, 4, 6], [0, 4, 6, 7, 9], 3),
|
2354
|
+
19: ([4, 6, 9, 10, 13, 15], [0, 1, 5, 8, 9, 10, 11, 13], 7),
|
2355
|
+
21: ([1, 4, 5, 8, 10, 11, 12, 17, 19], [1, 3, 8, 9, 12, 13, 18, 20], 4),
|
2356
|
+
31: ([2, 4, 6, 12, 14, 16, 17, 19, 25, 26, 28, 29],
|
2357
|
+
[0, 3, 9, 11, 13, 14, 15, 16, 17, 18, 20, 22, 28], 5),
|
2358
|
+
37: ([0, 3, 4, 5, 7, 13, 18, 19, 24, 30, 32, 33, 34],
|
2359
|
+
[0, 1, 2, 3, 4, 6, 12, 13, 18, 19, 24, 25, 31, 33, 34, 35, 36], 10),
|
2360
|
+
39: ([1, 4, 6, 10, 14, 15, 16, 17, 18, 21, 22, 23, 24, 25, 29, 33, 35, 38],
|
2361
|
+
[0, 2, 4, 6, 7, 10, 11, 14, 16, 19, 20, 22, 26, 32, 33, 38], 16),
|
2362
|
+
57: ([0, 4, 11, 12, 18, 19, 20, 25, 26, 27, 28, 29, 30, 31, 32, 37, 38, 39, 45, 46, 53],
|
2363
|
+
[1, 2, 5, 6, 7, 8, 9, 10, 12, 17, 19, 21, 22, 24, 25, 28, 30, 31, 34, 37, 39, 41, 42, 43, 44, 46, 53, 54],
|
2364
|
+
7)
|
2365
|
+
}
|
2366
|
+
compact_data = {
|
2367
|
+
73: ([1, 8, 64],
|
2368
|
+
[0, 9, 13, 18, 25, 26, 27, 35, 36, 43],
|
2369
|
+
[1, 2, 4, 9, 11, 14, 18, 21, 26, 34, 36, 43], 4),
|
2370
|
+
91: ([1, 16, 74],
|
2371
|
+
[0, 3, 4, 5, 8, 11, 19, 25, 27, 43, 45, 50, 55],
|
2372
|
+
[0, 1, 4, 5, 13, 14, 15, 25, 28, 33, 38, 43, 44, 49, 55], 9),
|
2373
|
+
93: ([1, 4, 16, 64, 70],
|
2374
|
+
[3, 10, 11, 14, 21, 23, 33, 34, 46],
|
2375
|
+
[3, 9, 11, 17, 23, 33, 34, 46, 62], 25),
|
2376
|
+
129: ([1, 4, 16, 64, 97, 121, 127],
|
2377
|
+
[1, 9, 10, 14, 19, 21, 23, 26, 27],
|
2378
|
+
[2, 5, 9, 10, 13, 18, 22, 27, 43, 86], 13),
|
2379
|
+
397: ([1, 16, 31, 99, 126, 167, 256, 273, 290, 333, 393],
|
2380
|
+
[3, 5, 9, 10, 11, 12, 18, 20, 21, 23, 29, 33, 36, 40, 44, 47, 61, 72],
|
2381
|
+
[2, 3, 6, 10, 17, 22, 24, 33, 34, 36, 40, 46, 47, 53, 58, 71, 72],
|
2382
|
+
34)
|
2383
|
+
}
|
2384
|
+
|
2385
|
+
exist = n in full_data or n in compact_data or \
|
2386
|
+
skew_spin_goethals_seidel_difference_family(n, existence=True)
|
2387
|
+
if existence:
|
2388
|
+
return exist
|
2389
|
+
|
2390
|
+
if not exist:
|
2391
|
+
raise NotImplementedError(f'Data for spin type Goethals Seidel family of order {n} not yet implemented')
|
2392
|
+
|
2393
|
+
G = Zmod(n)
|
2394
|
+
if skew_spin_goethals_seidel_difference_family(n, existence=True):
|
2395
|
+
G, [S1, S2, S3, S4] = skew_spin_goethals_seidel_difference_family(n, check=False)
|
2396
|
+
elif n in full_data:
|
2397
|
+
S1, S2, mu = full_data[n]
|
2398
|
+
S1 = list(map(G, S1))
|
2399
|
+
S2 = list(map(G, S2))
|
2400
|
+
S1, S2, S3, S4 = _construct_gs_difference_family_from_full(S1, S2, mu)
|
2401
|
+
elif n in compact_data:
|
2402
|
+
H, rep1, rep2, mu = compact_data[n]
|
2403
|
+
H = list(map(G, H))
|
2404
|
+
S1, S2, S3, S4 = _construct_gs_difference_family_from_compact(rep1, rep2, H, mu)
|
2405
|
+
|
2406
|
+
if check:
|
2407
|
+
lmbda = len(S1) + len(S2) + len(S3) + len(S4) - n
|
2408
|
+
assert is_supplementary_difference_set([S1, S2, S3, S4], lmbda=lmbda, G=G)
|
2409
|
+
return G, [S1, S2, S3, S4]
|
2410
|
+
|
2411
|
+
|
2412
|
+
def skew_spin_goethals_seidel_difference_family(n, existence=False, check=True):
|
2413
|
+
r"""
|
2414
|
+
Construct skew spin type Goethals-Seidel difference family with parameters
|
2415
|
+
`(n; k_1, k_2, k_3, k_4; \lambda)`.
|
2416
|
+
|
2417
|
+
The construction is described in [Djo2024]_. This function contains, for
|
2418
|
+
each value of `n`, either a full representation of `S_1, S_2` together with
|
2419
|
+
the multiplier `\mu`, or a subgroup `H`, two sets of representatives, and the
|
2420
|
+
multiplier.
|
2421
|
+
|
2422
|
+
This data is used to construct the difference family using the functions
|
2423
|
+
:func:`_construct_gs_difference_family_from_full` and
|
2424
|
+
:func:`_construct_gs_difference_family_from_compact`.
|
2425
|
+
|
2426
|
+
INPUT:
|
2427
|
+
|
2428
|
+
- ``n`` -- integer; the parameter of the GS difference family
|
2429
|
+
- ``existence`` -- boolean (default: ``False``); if ``True``, only check
|
2430
|
+
whether the skew difference family can be constructed
|
2431
|
+
- ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
|
2432
|
+
are a skew difference family before returning them;
|
2433
|
+
setting this parameter to ``False`` may speed up the computation considerably
|
2434
|
+
|
2435
|
+
OUTPUT:
|
2436
|
+
|
2437
|
+
If ``existence=False``, the function returns the group G of integers modulo
|
2438
|
+
``n`` and a list containing 4 sets, or raises an error if data for the given
|
2439
|
+
``n`` is not available.
|
2440
|
+
If ``existence=True``, the function returns a boolean representing whether
|
2441
|
+
the skew difference family can be constructed.
|
2442
|
+
|
2443
|
+
EXAMPLES::
|
2444
|
+
|
2445
|
+
sage: from sage.combinat.designs.difference_family import skew_spin_goethals_seidel_difference_family
|
2446
|
+
sage: G, [S1, S2, S3, S4] = skew_spin_goethals_seidel_difference_family(61)
|
2447
|
+
|
2448
|
+
If existence is ``True``, the function returns a boolean::
|
2449
|
+
|
2450
|
+
sage: skew_spin_goethals_seidel_difference_family(61, existence=True)
|
2451
|
+
True
|
2452
|
+
sage: skew_spin_goethals_seidel_difference_family(5, existence=True)
|
2453
|
+
False
|
2454
|
+
|
2455
|
+
TESTS::
|
2456
|
+
sage: from sage.combinat.designs.difference_family import is_supplementary_difference_set, _is_skew_set
|
2457
|
+
sage: G, [S1, S2, S3, S4] = skew_spin_goethals_seidel_difference_family(7, check=False)
|
2458
|
+
sage: lmbda = len(S1) + len(S2) + len(S3) + len(S4) - 7
|
2459
|
+
sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=lmbda, G=G)
|
2460
|
+
True
|
2461
|
+
sage: _is_skew_set(G, S1)
|
2462
|
+
True
|
2463
|
+
sage: skew_spin_goethals_seidel_difference_family(5)
|
2464
|
+
Traceback (most recent call last):
|
2465
|
+
...
|
2466
|
+
NotImplementedError: Data for skew spin type Goethals Seidel family of order 5 not yet implemented
|
2467
|
+
"""
|
2468
|
+
full_data = {
|
2469
|
+
7: ([1, 2, 4], [1, 6], 2),
|
2470
|
+
19: ([1, 4, 5, 6, 7, 9, 11, 16, 17], [0, 1, 7, 8, 11, 12, 18], -2),
|
2471
|
+
37: ([2, 3, 4, 6, 8, 11, 15, 18, 20, 21, 23, 24, 25, 27, 28, 30, 32, 36],
|
2472
|
+
[0, 1, 2, 5, 9, 13, 14, 15, 22, 23, 24, 28, 32, 35, 36],
|
2473
|
+
10)
|
2474
|
+
}
|
2475
|
+
compact_data = {
|
2476
|
+
61: ([1, 9, 20, 34, 58], [3, 4, 5, 6, 8, 10], [0, 8, 10, 13, 23, 26], 13),
|
2477
|
+
127: ([1, 2, 4, 8, 16, 32, 64],
|
2478
|
+
[1, 3, 7, 9, 11, 19, 21, 23, 47],
|
2479
|
+
[0, 3, 7, 9, 11, 15, 29, 31, 55], 19),
|
2480
|
+
271: ([1, 28, 106, 125, 169, 178, 242, 248, 258],
|
2481
|
+
[1, 4, 5, 7, 8, 11, 14, 16, 19, 21, 22, 25, 31, 43, 44],
|
2482
|
+
[1, 2, 3, 5, 7, 8, 12, 19, 22, 27, 38, 42, 44, 51], 5),
|
2483
|
+
331: ([1, 74, 80, 85, 111, 120, 167, 180, 270, 274, 293],
|
2484
|
+
[5, 10, 11, 13, 16, 19, 20, 22, 32, 38, 53, 56, 64, 76, 101],
|
2485
|
+
[0, 4, 11, 16, 20, 28, 31, 37, 41, 49, 53, 56, 73, 88, 101], 31),
|
2486
|
+
397: ([1, 16, 31, 99, 126, 167, 256, 273, 290, 333, 393],
|
2487
|
+
[1, 6, 7, 8, 9, 10, 11, 12, 17, 18, 20, 21, 29, 34, 46, 47, 53, 106],
|
2488
|
+
[2, 11, 12, 17, 18, 20, 24, 27, 33, 34, 36, 40, 46, 47, 53, 58, 71],
|
2489
|
+
34),
|
2490
|
+
547: ([1, 46, 237, 261, 293, 350, 353, 375, 440, 475, 509, 517, 519],
|
2491
|
+
[1, 4, 5, 6, 10, 11, 13, 14, 17, 25, 29, 34, 35, 40, 49, 52, 55, 64, 69, 110, 123],
|
2492
|
+
[1, 4, 5, 11, 16, 17, 20, 26, 32, 33, 34, 41, 49, 52, 55, 64, 70, 80, 123, 207],
|
2493
|
+
40),
|
2494
|
+
631: ([1, 8, 43, 64, 79, 188, 228, 242, 279, 310, 339, 344, 512, 562, 587],
|
2495
|
+
[1, 2, 3, 4, 6, 7, 12, 13, 14, 17, 19, 21, 26, 27, 31, 38, 42, 52, 62, 76, 124],
|
2496
|
+
[0, 11, 13, 14, 18, 19, 21, 22, 29, 35, 39, 46, 62, 63, 65, 66, 67, 92, 117, 124, 187],
|
2497
|
+
2)
|
2498
|
+
}
|
2499
|
+
|
2500
|
+
if existence:
|
2501
|
+
return n in full_data or n in compact_data
|
2502
|
+
|
2503
|
+
if n not in full_data and n not in compact_data:
|
2504
|
+
raise NotImplementedError(f'Data for skew spin type Goethals Seidel family of order {n} not yet implemented')
|
2505
|
+
|
2506
|
+
G = Zmod(n)
|
2507
|
+
if n in full_data:
|
2508
|
+
S1, S2, mu = full_data[n]
|
2509
|
+
S1 = list(map(G, S1))
|
2510
|
+
S2 = list(map(G, S2))
|
2511
|
+
S1, S2, S3, S4 = _construct_gs_difference_family_from_full(S1, S2, mu)
|
2512
|
+
if n in compact_data:
|
2513
|
+
H, rep1, rep2, mu = compact_data[n]
|
2514
|
+
H = list(map(G, H))
|
2515
|
+
S1, S2, S3, S4 = _construct_gs_difference_family_from_compact(rep1, rep2, H, mu)
|
2516
|
+
|
2517
|
+
if check:
|
2518
|
+
lmbda = len(S1) + len(S2) + len(S3) + len(S4) - n
|
2519
|
+
assert is_supplementary_difference_set([S1, S2, S3, S4], lmbda=lmbda, G=G)
|
2520
|
+
assert _is_skew_set(G, S1)
|
2521
|
+
return G, [S1, S2, S3, S4]
|
2522
|
+
|
2523
|
+
|
2524
|
+
def skew_supplementary_difference_set(n, existence=False, check=True, return_group=False):
|
2525
|
+
r"""
|
2526
|
+
Construct `4-\{n; n_1, n_2, n_3, n_4; \lambda\}` supplementary difference sets,
|
2527
|
+
where `S_1` is skew and `n_1 + n_2 + n_3 + n_4 = n+\lambda`.
|
2528
|
+
|
2529
|
+
These sets are constructed from available data, as described in [Djo1994a]_. The set `S_1 \subset G` is
|
2530
|
+
always skew, i.e. `S_1 \cap (-S_1) = \emptyset` and `S_1 \cup (-S_1) = G \setminus \{0\}`.
|
2531
|
+
|
2532
|
+
The data is taken from:
|
2533
|
+
|
2534
|
+
* `n = 103, 151`: [Djo1994a]_
|
2535
|
+
* `n = 67, 113, 127, 157, 163, 181, 241`: [Djo1992a]_
|
2536
|
+
* `n = 37, 43`: [Djo1992b]_
|
2537
|
+
* `n = 39, 49, 65, 93, 121, 129, 133, 217, 219, 267`: [Djo1992c]_
|
2538
|
+
* `n = 97`: [Djo2008a]_
|
2539
|
+
* `n = 109, 145, 247`: [Djo2008b]_
|
2540
|
+
* `n = 73`: [Djo2023b]_
|
2541
|
+
* `n = 213, 631`: [DGK2014]_
|
2542
|
+
* `n = 331`: [DK2016]_
|
2543
|
+
|
2544
|
+
Additional skew Supplementary difference sets are built using the function
|
2545
|
+
:func:`skew_supplementary_difference_set_over_polynomial_ring`, and
|
2546
|
+
:func:`skew_supplementary_difference_set_with_paley_todd`.
|
2547
|
+
|
2548
|
+
INPUT:
|
2549
|
+
|
2550
|
+
- ``n`` -- integer; the parameter of the supplementary difference set
|
2551
|
+
- ``existence`` -- boolean (default: ``False``); if ``True``, only check
|
2552
|
+
whether the supplementary difference sets can be constructed
|
2553
|
+
- ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
|
2554
|
+
are supplementary difference sets with `S_1` skew before returning them;
|
2555
|
+
setting this parameter to ``False`` may speed up the computation considerably
|
2556
|
+
- ``return_group`` -- boolean (default: ``False``); if ``True``, the function
|
2557
|
+
will also return the group from which the sets are created
|
2558
|
+
|
2559
|
+
OUTPUT:
|
2560
|
+
|
2561
|
+
If ``existence=False``, the function returns a list containing 4 sets,
|
2562
|
+
or raises an error if data for the given ``n`` is not available. If
|
2563
|
+
``return_group=True`` the function will additionally return the group from
|
2564
|
+
which the sets are created.
|
2565
|
+
If ``existence=True``, the function returns a boolean representing whether
|
2566
|
+
skew supplementary difference sets can be constructed.
|
2567
|
+
|
2568
|
+
EXAMPLES::
|
2569
|
+
|
2570
|
+
sage: from sage.combinat.designs.difference_family import skew_supplementary_difference_set
|
2571
|
+
sage: [S1, S2, S3, S4] = skew_supplementary_difference_set(39)
|
2572
|
+
|
2573
|
+
If ``return_group=True``, the function will also return the group::
|
2574
|
+
|
2575
|
+
sage: G, [S1, S2, S3, S4] = skew_supplementary_difference_set(103, return_group=True)
|
2576
|
+
|
2577
|
+
If ``existence=True``, the function returns a boolean::
|
2578
|
+
|
2579
|
+
sage: skew_supplementary_difference_set(103, existence=True)
|
2580
|
+
True
|
2581
|
+
sage: skew_supplementary_difference_set(17, existence=True)
|
2582
|
+
False
|
2583
|
+
|
2584
|
+
TESTS::
|
2585
|
+
|
2586
|
+
sage: from sage.combinat.designs.difference_family import is_supplementary_difference_set, _is_skew_set
|
2587
|
+
sage: G, [S1, S2, S3, S4] = skew_supplementary_difference_set(113, check=False, return_group=True)
|
2588
|
+
sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=len(S1)+len(S2)+len(S3)+len(S4)-113, G=G)
|
2589
|
+
True
|
2590
|
+
sage: _is_skew_set(G, S1)
|
2591
|
+
True
|
2592
|
+
sage: G, [S1, S2, S3, S4] = skew_supplementary_difference_set(67, check=False, return_group=True)
|
2593
|
+
sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=len(S1)+len(S2)+len(S3)+len(S4)-67, G=G)
|
2594
|
+
True
|
2595
|
+
sage: _is_skew_set(G, S1)
|
2596
|
+
True
|
2597
|
+
sage: skew_supplementary_difference_set(17)
|
2598
|
+
Traceback (most recent call last):
|
2599
|
+
...
|
2600
|
+
ValueError: Skew SDS of order 17 not yet implemented.
|
2601
|
+
sage: skew_supplementary_difference_set(17, existence=True)
|
2602
|
+
False
|
2603
|
+
sage: skew_supplementary_difference_set(127, existence=True)
|
2604
|
+
True
|
2605
|
+
sage: skew_supplementary_difference_set(81, existence=True)
|
2606
|
+
True
|
2607
|
+
|
2608
|
+
.. NOTE::
|
2609
|
+
|
2610
|
+
The data for `n=247` in [Djo2008b]_ contains a typo: the set `\alpha_2` should contain `223` instead of `233`.
|
2611
|
+
This can be verified by checking the resulting sets, which are given explicitly in the paper.
|
2612
|
+
"""
|
2613
|
+
|
2614
|
+
# If -1 is present in an index set, it means that {0} should be added to that set
|
2615
|
+
indices = {
|
2616
|
+
37: [[0, 3, 5, 7, 9, 10], [0, 5, 6, 7, 8],
|
2617
|
+
[1, 2, 6, 7, 9], [2, 6, 8, 9, 10]],
|
2618
|
+
39: [[1, 3, 5, 6, 8, 10, 12], [0, 1, 5, 8, 12, 13],
|
2619
|
+
[1, 3, 4, 7, 9, 12, 13], [0, 1, 2, 3, 7, 8]],
|
2620
|
+
43: [[1, 2, 4], [1, 2, 4], [0, 2, 3], [3, 4, -1]],
|
2621
|
+
49: [[1, 2, 5, 7, 8, 10, 13, 14], [4, 5, 6, 7, 10, 11],
|
2622
|
+
[0, 1, 2, 4, 6, 7, 12, 14], [1, 2, 3, 5, 6, 10, 12, 13, 14]],
|
2623
|
+
65: [[1, 3, 5, 6, 8, 10, 13, 14, 17, 18, 20, 22],
|
2624
|
+
[0, 3, 7, 10, 16, 17, 18, 20, 21],
|
2625
|
+
[2, 4, 6, 8, 9, 10, 14, 15, 16, 17, 18, 20],
|
2626
|
+
[5, 7, 8, 9, 11, 12, 13, 14, 16, 18, 19, 20, 21]],
|
2627
|
+
67: [[0, 3, 5, 6, 9, 10, 13, 14, 17, 18, 20],
|
2628
|
+
[0, 2, 4, 9, 11, 12, 13, 16, 19, 21],
|
2629
|
+
[1, 3, 6, 10, 11, 13, 14, 16, 20, 21],
|
2630
|
+
[2, 4, 6, 8, 9, 11, 14, 17, 19]],
|
2631
|
+
73: [[4, 6, 8, 14], [8, 10, 12, 14], [4, 6, 10, 12], [-1, 0, 2, 10]],
|
2632
|
+
93: [[0, 3, 4, 6, 9, 10, 12, 14, 17, 18],
|
2633
|
+
[2, 3, 4, 5, 9, 13, 15, 18, 19],
|
2634
|
+
[1, 2, 3, 4, 5, 6, 7, 8, 16],
|
2635
|
+
[1, 4, 6, 11, 12, 13, 15, 16, 17, 18]],
|
2636
|
+
97: [[1, 2, 4, 6, 9, 11, 13, 14, 17, 18, 21, 23, 25, 27, 29, 30],
|
2637
|
+
[1, 2, 6, 7, 8, 9, 10, 11, 12, 13, 23, 27, 29],
|
2638
|
+
[0, 1, 2, 5, 6, 12, 13, 15, 16, 20, 24, 25, 26, 29, 30, 31],
|
2639
|
+
[0, 2, 3, 4, 7, 8, 9, 11, 12, 13, 15, 16, 17, 18, 23, 28, 29]],
|
2640
|
+
103: [[1, 3, 4, 6, 8, 11, 12, 14, 17, 18, 20, 22, 25, 27, 28, 30, 32],
|
2641
|
+
[2, 9, 10, 12, 13, 14, 15, 16, 20, 21, 22, 23, 24, 26, 28, 29, 30],
|
2642
|
+
[0, 1, 2, 3, 4, 11, 12, 13, 16, 17, 19, 20, 21, 24, 25, 26, 28, 30, 31],
|
2643
|
+
[0, 1, 2, 3, 4, 5, 6, 13, 15, 18, 19, 20, 23, 24, 25, 26, 27, 28, 29, 31]],
|
2644
|
+
109: [[0, 2, 5, 7, 8, 10, 12, 15, 16, 19, 20, 23, 24, 26, 29, 30, 33, 34],
|
2645
|
+
[4, 5, 6, 7, 11, 15, 18, 19, 20, 22, 25, 30, 32, 33, 35],
|
2646
|
+
[0, 1, 5, 6, 9, 10, 11, 14, 17, 20, 24, 26, 27, 28, 29, 31, 32],
|
2647
|
+
[0, 3, 4, 6, 7, 9, 10, 12, 13, 22, 24, 25, 26, 27, 28, 29, 31, 33, 35]],
|
2648
|
+
113: [[0, 3, 4, 6, 8, 10, 13, 14],
|
2649
|
+
[1, 3, 8, 9, 10, 11, 12, 13],
|
2650
|
+
[0, 2, 3, 5, 6, 7, 12],
|
2651
|
+
[1, 2, 3, 5, 8, 9, 15]],
|
2652
|
+
121: [[0, 2, 4, 7, 8, 11, 13, 14, 16, 19, 20, 22],
|
2653
|
+
[0, 1, 4, 5, 8, 9, 10, 15, 17, 20, 23],
|
2654
|
+
[1, 2, 3, 7, 9, 16, 18, 19, 20, 21, 22, 23],
|
2655
|
+
[0, 2, 9, 10, 11, 12, 13, 14, 15, 17, 18, 21, 22, 23]],
|
2656
|
+
127: [[0, 3, 5, 7, 8, 10, 12, 14, 16],
|
2657
|
+
[0, 1, 3, 6, 7, 9, 10, 12, 14, 15],
|
2658
|
+
[0, 1, 3, 4, 5, 7, 8, 9, 15, 16],
|
2659
|
+
[1, 4, 5, 6, 9, 10, 13, 14, 15, 16]],
|
2660
|
+
129: [[1, 2, 4, 7, 9, 11, 12, 14, 16, 18],
|
2661
|
+
[0, 1, 2, 3, 9, 11, 14, 15, 19],
|
2662
|
+
[0, 1, 3, 6, 8, 10, 12, 16, 18, 19],
|
2663
|
+
[0, 3, 7, 8, 9, 10, 12, 14, 15, 17]],
|
2664
|
+
133: [[1, 2, 5, 6, 9, 11, 12, 14], [1, 4, 7, 9, 10, 12, 13, 15],
|
2665
|
+
[0, 5, 6, 8, 11, 12, 13, 15], [0, 1, 2, 5, 7, 8, 9, 13, 14, 15]],
|
2666
|
+
145: [[1, 2, 4, 7, 9, 10, 13, 14, 16, 19, 20, 22], [0, 2, 4, 7, 10, 11, 14, 18, 19, 20, 21, 22],
|
2667
|
+
[1, 3, 6, 9, 12, 13, 14, 17, 19, 20, 21, 22, 23], [2, 3, 5, 6, 7, 9, 12, 13, 15, 16, 19, 20, 21, 22, 23]],
|
2668
|
+
151: [[0, 3, 5, 6, 8, 11, 13, 14, 16, 19, 21, 23, 25, 27, 28],
|
2669
|
+
[2, 3, 6, 13, 16, 17, 20, 23, 25, 26, 27, 28, 29],
|
2670
|
+
[0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 23, 24, 27, 28],
|
2671
|
+
[1, 4, 5, 10, 11, 12, 13, 14, 16, 18, 19, 22, 25, 26, 27, 28]],
|
2672
|
+
157: [[0, 2, 5, 7, 8, 11],
|
2673
|
+
[0, 4, 5, 6, 9, 11],
|
2674
|
+
[6, 7, 8, 9, 10, 11],
|
2675
|
+
[0, 5, 6, 7, 8, 10, 11]],
|
2676
|
+
163: [[0, 2, 5, 6, 9, 10, 13, 14, 17],
|
2677
|
+
[0, 1, 7, 10, 12, 15, 16, 17],
|
2678
|
+
[0, 1, 3, 5, 8, 13, 15, 16, 17],
|
2679
|
+
[3, 6, 7, 8, 11, 12, 13, 14, 16, 17]],
|
2680
|
+
181: [[0, 3, 5, 6, 8, 10, 13, 15, 16, 19],
|
2681
|
+
[4, 5, 7, 8, 11, 14, 15, 16, 18, 19],
|
2682
|
+
[0, 4, 10, 11, 13, 15, 16, 18, 19],
|
2683
|
+
[2, 4, 5, 7, 11, 13, 15, 17, 19]],
|
2684
|
+
213: [[1, 2, 5, 6, 9, 11, 12, 14, 16, 19, 20, 23, 24, 26, 29, 30],
|
2685
|
+
[3, 6, 8, 12, 13, 14, 15, 17, 20, 22, 23, 25, 26, 27, 28, 31],
|
2686
|
+
[2, 3, 5, 7, 9, 13, 16, 17, 19, 21, 23, 24, 27, 28, 29],
|
2687
|
+
[0, 5, 6, 9, 11, 13, 14, 17, 20, 22, 23, 26, 29, 31]],
|
2688
|
+
217: [[0, 3, 5, 7, 8, 11, 12, 14], [1, 3, 4, 7, 9, 11, 12, 15],
|
2689
|
+
[3, 4, 5, 6, 7, 9, 10, 14, 15], [1, 3, 4, 5, 7, 8, 11, 13, 14]],
|
2690
|
+
219: [[1, 3, 5, 6, 8, 11, 12, 15, 17, 18, 21, 22, 24],
|
2691
|
+
[2, 6, 8, 10, 11, 12, 13, 16, 19, 22, 23, 24],
|
2692
|
+
[0, 1, 5, 6, 10, 11, 13, 14, 17, 20, 21, 24, 25],
|
2693
|
+
[0, 2, 3, 4, 5, 6, 7, 11, 12, 13, 16, 20, 23]],
|
2694
|
+
241: [[0, 2, 4, 6, 8, 11, 12, 14],
|
2695
|
+
[1, 3, 4, 6, 7, 13, 14, 15],
|
2696
|
+
[6, 8, 9, 10, 12, 13, 14, 15],
|
2697
|
+
[3, 4, 5, 9, 10, 13, 14]],
|
2698
|
+
247: [[0, 2, 4, 7, 8, 10, 12, 15, 16, 18, 20, 23, 25, 27, 29],
|
2699
|
+
[0, 2, 7, 9, 11, 12, 14, 15, 16, 18, 20, 22, 26],
|
2700
|
+
[2, 3, 4, 12, 13, 14, 15, 16, 18, 20, 23, 24, 26, 27, 29],
|
2701
|
+
[0, 3, 4, 6, 10, 11, 12, 14, 18, 19, 20, 22, 25, 29]],
|
2702
|
+
267: [[0, 3, 4, 7, 8, 11, 13, 15, 16, 19, 21, 22, 25],
|
2703
|
+
[0, 1, 4, 5, 6, 8, 14, 15, 18, 21, 23],
|
2704
|
+
[0, 2, 4, 5, 7, 9, 10, 11, 14, 15, 16, 17, 25],
|
2705
|
+
[0, 1, 3, 4, 6, 14, 15, 16, 17, 18, 20, 22, 23, 25]],
|
2706
|
+
331: [[1, 2, 4, 7, 9, 10, 12, 15, 16, 18, 21, 22, 24, 26, 28],
|
2707
|
+
[-1, 0, 2, 6, 9, 11, 12, 14, 15, 17, 20, 21, 24, 25, 28],
|
2708
|
+
[-1, 0, 1, 5, 6, 7, 8, 9, 10, 12, 15, 18, 23, 28, 29],
|
2709
|
+
[-1, 0, 3, 7, 8, 10, 11, 12, 14, 16, 19, 20, 21, 26, 29]],
|
2710
|
+
631: [[0, 2, 4, 6, 9, 10, 12, 15, 16, 18, 20, 23, 24, 26, 29, 30, 32, 35, 36, 38, 40],
|
2711
|
+
[0, 1, 2, 4, 6, 8, 9, 10, 11, 12, 13, 14, 16, 20, 23, 28, 29, 30, 32, 36, 38, 41],
|
2712
|
+
[0, 2, 3, 4, 6, 9, 10, 12, 14, 15, 16, 17, 18, 19, 20, 22, 24, 25, 26, 29, 34, 40],
|
2713
|
+
[0, 2, 4, 5, 6, 7, 8, 10, 15, 16, 18, 22, 23, 24, 26, 30, 31, 33, 35, 36, 37, 38]],
|
2714
|
+
}
|
2715
|
+
|
2716
|
+
# If the element is a list, that is the coset.
|
2717
|
+
cosets_gens = {
|
2718
|
+
37: [1, 2, 3, 5, 6, 9],
|
2719
|
+
39: [1, 2, 3, 4, 6, 8, [13]],
|
2720
|
+
43: [1, 3, 7],
|
2721
|
+
49: [1, 2, 3, 4, 6, 7, 9, 12],
|
2722
|
+
65: [1, 2, 3, 5, 6, 7, 9, 10, 11, [13], 22, [26]],
|
2723
|
+
67: [1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17],
|
2724
|
+
73: [1, 3, 5, 9, 11, 13, 17, 25],
|
2725
|
+
93: [1, 2, 3, 5, 7, 9, 10, 14, 15, [31]],
|
2726
|
+
97: [1, 2, 3, 4, 5, 6, 7, 9, 10, 12, 13, 15, 18, 20, 23, 26],
|
2727
|
+
103: [1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 15, 17, 19, 21, 23, 30],
|
2728
|
+
109: [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 13, 15, 16, 18, 20, 23, 25, 30],
|
2729
|
+
113: [1, 2, 3, 5, 6, 9, 10, 13],
|
2730
|
+
121: [1, 2, 4, 5, 7, 8, 10, 11, 16, 17, 19, 20],
|
2731
|
+
127: [1, 3, 5, 7, 9, 11, 13, 19, 21],
|
2732
|
+
129: [1, 3, 5, 7, 9, 11, 13, 19, 21, [43]],
|
2733
|
+
133: [1, 2, 3, 6, 7, 9, 18, [19, 38, 76]],
|
2734
|
+
145: [1, [2, 17, 32, 72, 77, 127, 137], [3, 43, 48, 98, 108, 118, 133], [6, 51, 71, 86, 91, 96, 121],
|
2735
|
+
[7, 52, 82, 107, 112, 117, 132], [11, 21, 31, 46, 61, 101, 106], [14, 19, 69, 79, 89, 104, 119],
|
2736
|
+
[22, 42, 57, 62, 67, 92, 122], [5, 35, 80, 100, 115, 120, 125], [10, 15, 55, 70, 85, 95, 105],
|
2737
|
+
[29], [58]],
|
2738
|
+
151: [1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 15, 22, 27, 29, 30],
|
2739
|
+
157: [1, 2, 3, 5, 9, 15],
|
2740
|
+
163: [1, 2, 3, 5, 6, 9, 10, 15, 18],
|
2741
|
+
181: [1, 2, 3, 4, 6, 7, 8, 12, 13, 24],
|
2742
|
+
213: [1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 15, 20, 22, 30, 43, 71],
|
2743
|
+
217: [1, 2, 4, 5, 7, 10, 19, [31, 62, 124]],
|
2744
|
+
219: [1, 2, 3, 5, 7, 9, 11, 15, 19, 22, 23, 33, [73]],
|
2745
|
+
241: [1, 2, 4, 5, 7, 13, 19, 35],
|
2746
|
+
247: [1, [2, 18, 31, 32, 41, 110, 122, 162, 223], [3, 27, 48, 165, 170, 183, 185, 211, 243],
|
2747
|
+
[5, 28, 45, 58, 80, 158, 187, 201, 226], [6, 54, 83, 93, 96, 119, 123, 175, 239], [7, 20, 63, 73, 112, 138, 163, 180, 232],
|
2748
|
+
[10, 56, 69, 90, 116, 127, 155, 160, 205], [11, 47, 99, 102, 111, 115, 150, 176, 177], [13, 52, 65, 78, 91, 117, 143, 208, 221],
|
2749
|
+
[14, 29, 40, 79, 113, 126, 146, 217, 224], [17, 25, 43, 49, 140, 142, 153, 194, 225], [19, 57, 171],
|
2750
|
+
[33, 34, 37, 50, 59, 86, 98, 141, 203], [35, 66, 68, 74, 100, 118, 159, 172, 196], [38, 95, 114]],
|
2751
|
+
267: [1, 2, 3, 5, 7, 9, 10, 13, 14, 15, 19, 39, [89]],
|
2752
|
+
331: [1, 2, 4, 5, 7, 8, 10, 13, 14, 16, 19, 20, 28, 32, 56],
|
2753
|
+
631: [1, 2, 3, 4, 5, 6, 7, 9, 12, 14, 17, 18, 19, 21, 23, 27, 31, 35, 38, 42, 62],
|
2754
|
+
}
|
2755
|
+
|
2756
|
+
H_db = {
|
2757
|
+
37: [1, 10, -11],
|
2758
|
+
39: [1, 16, 22],
|
2759
|
+
43: [1, 4, 11, 16, 21, -2, -8],
|
2760
|
+
49: [1, 18, 30],
|
2761
|
+
65: [1, 16, 61],
|
2762
|
+
67: [1, 29, 37],
|
2763
|
+
73: [1, 2, 4, 8, 16, 32, 64, 55, 37],
|
2764
|
+
93: [1, 4, 16, 64, 70],
|
2765
|
+
97: [1, 35, 61],
|
2766
|
+
103: [1, 46, 56],
|
2767
|
+
109: [1, 45, 63],
|
2768
|
+
113: [1, 16, 28, 30, 49, 106, 109],
|
2769
|
+
121: [1, 3, 9, 27, 81],
|
2770
|
+
127: [1, 2, 4, 8, 16, 32, 64],
|
2771
|
+
129: [1, 4, 16, 64, 97, 121, 127],
|
2772
|
+
133: [1, 4, 16, 25, 64, 93, 100, 106, 123],
|
2773
|
+
145: [1, 16, 36, 81, 111, 136, 141],
|
2774
|
+
151: [1, 8, 19, 59, 64],
|
2775
|
+
157: [1, 14, 16, 39, 46, 67, 75, 93, 99, 101, 108, 130, 153],
|
2776
|
+
163: [1, 38, 40, 53, 58, 85, 104, 133, 140],
|
2777
|
+
181: [1, 39, 43, 48, 62, 65, 73, 80, 132],
|
2778
|
+
213: [1, 37, 91, 103, 172, 187, 190],
|
2779
|
+
217: [1, 8, 9, 25, 51, 64, 72, 78, 81, 142, 190, 191, 193, 200, 214],
|
2780
|
+
219: [1, 4, 16, 37, 55, 64, 148, 154, 178],
|
2781
|
+
241: [1, 15, 24, 54, 87, 91, 94, 98, 100, 119, 160, 183, 205, 225, 231],
|
2782
|
+
247: [1, 9, 16, 55, 61, 81, 139, 144, 235],
|
2783
|
+
267: [1, 4, 16, 64, 67, 91, 97, 121, 217, 223, 256],
|
2784
|
+
331: [1, 74, 80, 85, 111, 120, 167, 180, 270, 274, 293],
|
2785
|
+
631: [1, 8, 43, 64, 79, 188, 228, 242, 279, 310, 339, 344, 512, 562, 587],
|
2786
|
+
}
|
2787
|
+
|
2788
|
+
G, S1, S2, S3, S4 = None, [], [], [], []
|
2789
|
+
|
2790
|
+
if n in indices:
|
2791
|
+
if existence:
|
2792
|
+
return True
|
2793
|
+
G, [S1, S2, S3, S4] = _construction_supplementary_difference_set(n, H_db[n], indices[n], cosets_gens[n], check=False)
|
2794
|
+
elif skew_supplementary_difference_set_over_polynomial_ring(n, existence=True):
|
2795
|
+
if existence:
|
2796
|
+
return True
|
2797
|
+
G, [S1, S2, S3, S4] = skew_supplementary_difference_set_over_polynomial_ring(n, check=False)
|
2798
|
+
elif skew_supplementary_difference_set_with_paley_todd(n, existence=True):
|
2799
|
+
if existence:
|
2800
|
+
return True
|
2801
|
+
G, [S1, S2, S3, S4] = skew_supplementary_difference_set_with_paley_todd(n, check=False)
|
2802
|
+
elif skew_spin_goethals_seidel_difference_family(n, existence=True):
|
2803
|
+
if existence:
|
2804
|
+
return True
|
2805
|
+
G, [S1, S2, S3, S4] = skew_spin_goethals_seidel_difference_family(n, check=False)
|
2806
|
+
|
2807
|
+
if existence:
|
2808
|
+
return False
|
2809
|
+
|
2810
|
+
if G is None:
|
2811
|
+
raise ValueError(f'Skew SDS of order {n} not yet implemented.')
|
2812
|
+
|
2813
|
+
if check:
|
2814
|
+
lmbda = len(S1) + len(S2) + len(S3) + len(S4) - n
|
2815
|
+
assert is_supplementary_difference_set([S1, S2, S3, S4], lmbda=lmbda, G=G)
|
2816
|
+
assert _is_skew_set(G, S1)
|
2817
|
+
|
2818
|
+
if return_group:
|
2819
|
+
return G, [S1, S2, S3, S4]
|
2820
|
+
return [S1, S2, S3, S4]
|
2821
|
+
|
2822
|
+
|
2823
|
+
def _construction_supplementary_difference_set(n, H, indices, cosets_gen, check=True):
|
2824
|
+
r"""
|
2825
|
+
Construct `4-\{n; n_1, n_2, n_3, n_4; \lambda\}` supplementary difference sets,
|
2826
|
+
where `n_1 + n_2 + n_3 + n_4 = n+\lambda`.
|
2827
|
+
|
2828
|
+
This construction is described in [Djo1994a]_.
|
2829
|
+
|
2830
|
+
Let H be a subgroup of Zmod(n) of order `t`. We construct the `2s = n/t` cosets
|
2831
|
+
`\alpha_0, .., \alpha_{2s-1}` by using the elements contained in a sequence `A`:
|
2832
|
+
`\alpha_{2i} = a_iH` and `\alpha_{2i+1} = -\alpha_{2i}`.
|
2833
|
+
|
2834
|
+
Then, we use four indices sets `J_1, J_2, J_3, J_4` to construct the four
|
2835
|
+
supplementary difference sets: `S_i = \bigcup_{j\in J__i} \alpha_i`. Note that
|
2836
|
+
if `J_i` contains the value `-1`, this function will add `0` to the set `S_i`.
|
2837
|
+
|
2838
|
+
To construct a coset `\alpha_{2i}` by listing it directly, replace the `2i`-th
|
2839
|
+
element of the list `A` with the desired set.
|
2840
|
+
|
2841
|
+
INPUT:
|
2842
|
+
|
2843
|
+
- ``n`` -- integer; the parameter of the supplementary difference set
|
2844
|
+
- ``H`` -- list of integers; the set `H` used to construct the cosets
|
2845
|
+
- ``indices`` -- list containing four lists of integers; the sets
|
2846
|
+
`J_1, J_2, J_3, J_4` described above
|
2847
|
+
- ``cosets_gen`` -- list containing integers or list of integers; the set `A`
|
2848
|
+
described above
|
2849
|
+
- ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
|
2850
|
+
are supplementary difference sets; setting this parameter to ``False`` may
|
2851
|
+
speed up the computation considerably
|
2852
|
+
|
2853
|
+
OUTPUT:
|
2854
|
+
|
2855
|
+
The function returns the ring of integers modulo ``n`` and a list containing
|
2856
|
+
the 4 sets.
|
2857
|
+
|
2858
|
+
TESTS::
|
2859
|
+
|
2860
|
+
sage: from sage.combinat.designs.difference_family import is_supplementary_difference_set, _construction_supplementary_difference_set
|
2861
|
+
sage: H = [1, 10, -11]
|
2862
|
+
sage: cosets_gen = [1, 2, 3, 5, 6, 9]
|
2863
|
+
sage: indices = [[0, 3, 5, 7, 9, 10], [0, 5, 6, 7, 8], [1, 2, 6, 7, 9], [2, 6, 8, 9, 10]]
|
2864
|
+
sage: _construction_supplementary_difference_set(37, H, indices, cosets_gen)
|
2865
|
+
(Ring of integers modulo 37,
|
2866
|
+
[[32, 1, 33, 35, 34, 7, 9, 10, 12, 14, 16, 17, 18, 22, 24, 26, 29, 31],
|
2867
|
+
[32, 1, 33, 34, 5, 6, 7, 8, 10, 13, 18, 19, 23, 24, 26],
|
2868
|
+
[32, 2, 36, 5, 11, 13, 14, 15, 18, 19, 20, 24, 27, 29, 31],
|
2869
|
+
[2, 5, 6, 8, 9, 12, 13, 14, 15, 16, 19, 20, 23, 29, 31]])
|
2870
|
+
sage: H = [1, 16, 22]
|
2871
|
+
sage: cosets_gen = [1, 2, 3, 4, 6, 8, [13]]
|
2872
|
+
sage: indices = [[1, 3, 5, 6, 8, 10, 12], [0, 1, 5, 8, 12, 13], [1, 3, 4, 7, 9, 12, 13], [0, 1, 2, 3, 7, 8]]
|
2873
|
+
sage: _construction_supplementary_difference_set(39, H, indices, cosets_gen)
|
2874
|
+
(Ring of integers modulo 39,
|
2875
|
+
[[4, 6, 7, 8, 10, 11, 12, 13, 15, 17, 18, 20, 23, 25, 30, 34, 36, 37, 38],
|
2876
|
+
[1, 36, 38, 6, 12, 13, 15, 16, 17, 18, 22, 23, 26, 30],
|
2877
|
+
[3, 7, 9, 13, 14, 17, 21, 23, 24, 26, 27, 29, 33, 34, 35, 37, 38],
|
2878
|
+
[32, 1, 2, 34, 35, 5, 38, 37, 7, 6, 14, 15, 16, 17, 18, 22, 23, 29]])
|
2879
|
+
sage: H = [1, 4, 11, 16, 21, -2, -8]
|
2880
|
+
sage: cosets_gen = [1, 3, 7]
|
2881
|
+
sage: indices = [[1, 2, 4], [1, 2, 4], [0, 2, 3], [3, 4, -1]]
|
2882
|
+
sage: G, sets = _construction_supplementary_difference_set(43, H, indices, cosets_gen, check=False)
|
2883
|
+
sage: is_supplementary_difference_set(sets, lmbda=35, G=G)
|
2884
|
+
True
|
2885
|
+
|
2886
|
+
.. SEEALSO::
|
2887
|
+
|
2888
|
+
:func:`skew_supplementary_difference_set`
|
2889
|
+
"""
|
2890
|
+
def generate_set(index_set, cosets):
|
2891
|
+
S = set()
|
2892
|
+
for idx in index_set:
|
2893
|
+
if idx == -1:
|
2894
|
+
S.add(Z(0))
|
2895
|
+
else:
|
2896
|
+
S = S.union(cosets[idx])
|
2897
|
+
return list(S)
|
2898
|
+
|
2899
|
+
Z = Zmod(n)
|
2900
|
+
H = set(map(Z, H))
|
2901
|
+
|
2902
|
+
cosets = []
|
2903
|
+
for el in cosets_gen:
|
2904
|
+
if isinstance(el, list):
|
2905
|
+
even_coset = {Z(x) for x in el}
|
2906
|
+
else:
|
2907
|
+
even_coset = {x*el for x in H}
|
2908
|
+
odd_coset = {-x for x in even_coset}
|
2909
|
+
cosets.append(even_coset)
|
2910
|
+
cosets.append(odd_coset)
|
2911
|
+
|
2912
|
+
S1 = generate_set(indices[0], cosets)
|
2913
|
+
S2 = generate_set(indices[1], cosets)
|
2914
|
+
S3 = generate_set(indices[2], cosets)
|
2915
|
+
S4 = generate_set(indices[3], cosets)
|
2916
|
+
|
2917
|
+
if check:
|
2918
|
+
lmbda = len(S1) + len(S2) + len(S3) + len(S4) - n
|
2919
|
+
assert is_supplementary_difference_set([S1, S2, S3, S4], lmbda=lmbda, G=Z)
|
2920
|
+
|
2921
|
+
return Z, [S1, S2, S3, S4]
|
2922
|
+
|
2923
|
+
|
2924
|
+
def supplementary_difference_set_hadamard(n, existence=False, check=True):
|
2925
|
+
r"""
|
2926
|
+
Construct `4-\{n; n_1, n_2, n_3, n_4; \lambda\}` supplementary difference sets,
|
2927
|
+
where `n_1 + n_2 + n_3 + n_4 = n+\lambda`.
|
2928
|
+
|
2929
|
+
These sets are constructed from available data, as described in [Djo1994a]_.
|
2930
|
+
The data is taken from:
|
2931
|
+
|
2932
|
+
* `n = 191`: [Djo2008c]_
|
2933
|
+
* `n = 239`: [Djo1994b]_
|
2934
|
+
* `n = 251`: [DGK2014]_
|
2935
|
+
|
2936
|
+
Additional SDS are constructed using :func:`skew_supplementary_difference_set`.
|
2937
|
+
|
2938
|
+
INPUT:
|
2939
|
+
|
2940
|
+
- ``n`` -- integer; the parameter of the supplementary difference set
|
2941
|
+
- ``existence`` -- boolean (default: ``False``); if ``True``, only check
|
2942
|
+
whether the supplementary difference sets can be constructed
|
2943
|
+
- ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
|
2944
|
+
are supplementary difference sets before returning them; Setting this
|
2945
|
+
parameter to ``False`` may speed up the computation considerably
|
2946
|
+
|
2947
|
+
OUTPUT:
|
2948
|
+
|
2949
|
+
If ``existence=False``, the function returns the ring of integers modulo
|
2950
|
+
``n`` and a list containing the 4 sets, or raises an error if data for the
|
2951
|
+
given ``n`` is not available.
|
2952
|
+
If ``existence=True``, the function returns a boolean representing whether
|
2953
|
+
skew supplementary difference sets can be constructed.
|
2954
|
+
|
2955
|
+
EXAMPLES::
|
2956
|
+
|
2957
|
+
sage: from sage.combinat.designs.difference_family import supplementary_difference_set_hadamard
|
2958
|
+
sage: G, [S1, S2, S3, S4] = supplementary_difference_set_hadamard(191)
|
2959
|
+
|
2960
|
+
If ``existence=True``, the function returns a boolean::
|
2961
|
+
|
2962
|
+
sage: supplementary_difference_set_hadamard(191, existence=True)
|
2963
|
+
True
|
2964
|
+
sage: supplementary_difference_set_hadamard(17, existence=True)
|
2965
|
+
False
|
2966
|
+
|
2967
|
+
TESTS::
|
2968
|
+
|
2969
|
+
sage: from sage.combinat.designs.difference_family import is_supplementary_difference_set
|
2970
|
+
sage: G, [S1, S2, S3, S4] = supplementary_difference_set_hadamard(191, check=False)
|
2971
|
+
sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=len(S1)+len(S2)+len(S3)+len(S4)-191, G=G)
|
2972
|
+
True
|
2973
|
+
sage: G, [S1, S2, S3, S4] = supplementary_difference_set_hadamard(37, check=False)
|
2974
|
+
sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=len(S1)+len(S2)+len(S3)+len(S4)-37, G=G)
|
2975
|
+
True
|
2976
|
+
sage: supplementary_difference_set_hadamard(11)
|
2977
|
+
Traceback (most recent call last):
|
2978
|
+
...
|
2979
|
+
ValueError: SDS of order 11 not yet implemented.
|
2980
|
+
sage: supplementary_difference_set_hadamard(11, existence=True)
|
2981
|
+
False
|
2982
|
+
sage: supplementary_difference_set_hadamard(127, existence=True)
|
2983
|
+
True
|
2984
|
+
"""
|
2985
|
+
|
2986
|
+
indices = {
|
2987
|
+
191: [[1, 7, 9, 10, 11, 13, 17, 18, 25, 26, 30, 31, 33, 34, 35, 36, 37],
|
2988
|
+
[1, 4, 7, 9, 11, 12, 13, 14, 19, 21, 22, 23, 24, 25, 26, 29, 36, 37],
|
2989
|
+
[0, 3, 4, 5, 7, 8, 9, 16, 17, 19, 24, 25, 29, 30, 31, 33, 35, 37],
|
2990
|
+
[1, 3, 4, 5, 8, 11, 14, 18, 19, 20, 21, 23, 24, 25, 28, 29, 30, 32, 34, 35]],
|
2991
|
+
239: [[0, 1, 2, 3, 4, 5, 6, 7, 14, 18, 19, 21, 24, 25, 29, 30],
|
2992
|
+
[0, 1, 3, 7, 9, 12, 15, 18, 20, 22, 26, 28, 29, 30, 31, 32, 33],
|
2993
|
+
[2, 3, 4, 5, 8, 9, 10, 11, 13, 17, 19, 21, 22, 24, 27, 31, 32],
|
2994
|
+
[0, 1, 2, 3, 6, 7, 8, 11, 13, 15, 17, 18, 19, 22, 25, 26, 27, 32, 33]],
|
2995
|
+
251: [[2, 6, 8, 10, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23, 27, 28, 35, 36, 39, 41, 43, 44, 47, 48],
|
2996
|
+
[2, 5, 10, 11, 17, 18, 21, 23, 24, 25, 26, 28, 29, 30, 34, 35, 38, 39, 40, 41, 42, 43, 44, 49],
|
2997
|
+
[0, 2, 6, 7, 10, 11, 14, 15, 16, 18, 21, 22, 24, 26, 30, 35, 37, 38, 45, 46, 47, 48, 49],
|
2998
|
+
[1, 2, 3, 4, 8, 9, 12, 17, 21, 22, 27, 28, 29, 30, 33, 34, 39, 41, 42, 43, 46, 47, 48]],
|
2999
|
+
}
|
3000
|
+
|
3001
|
+
cosets_gens = {
|
3002
|
+
191: [1, 2, 3, 4, 6, 8, 9, 11, 12, 13, 16, 17, 18, 19, 22, 32, 36, 38, 41],
|
3003
|
+
239: [1, 2, 3, 4, 5, 6, 7, 8, 9, 13, 14, 15, 18, 21, 28, 35, 42],
|
3004
|
+
251: [1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 14, 15, 17, 18, 19, 21, 28, 30, 33, 34, 35, 41, 43, 45, 68],
|
3005
|
+
}
|
3006
|
+
|
3007
|
+
H_db = {
|
3008
|
+
191: [1, 39, 184, 109, 49],
|
3009
|
+
239: [1, 10, 24, 44, 98, 100, 201],
|
3010
|
+
251: [1, 20, 113, 149, 219],
|
3011
|
+
}
|
3012
|
+
|
3013
|
+
if existence:
|
3014
|
+
return n in indices or skew_supplementary_difference_set(n, existence=True)
|
3015
|
+
|
3016
|
+
sets = None
|
3017
|
+
G = None
|
3018
|
+
if n in indices:
|
3019
|
+
G, sets = _construction_supplementary_difference_set(n, H_db[n], indices[n], cosets_gens[n], check=False)
|
3020
|
+
elif skew_supplementary_difference_set(n, existence=True):
|
3021
|
+
G, sets = skew_supplementary_difference_set(n, check=False, return_group=True)
|
3022
|
+
elif spin_goethals_seidel_difference_family(n, existence=True):
|
3023
|
+
if existence:
|
3024
|
+
return True
|
3025
|
+
G, [S1, S2, S3, S4] = spin_goethals_seidel_difference_family(n, check=False)
|
3026
|
+
|
3027
|
+
if sets is None:
|
3028
|
+
raise ValueError(f'SDS of order {n} not yet implemented.')
|
3029
|
+
|
3030
|
+
S1, S2, S3, S4 = sets
|
3031
|
+
if check:
|
3032
|
+
lmbda = len(S1) + len(S2) + len(S3) + len(S4) - n
|
3033
|
+
assert is_supplementary_difference_set([S1, S2, S3, S4], lmbda=lmbda, G=G)
|
3034
|
+
|
3035
|
+
return G, [S1, S2, S3, S4]
|
3036
|
+
|
3037
|
+
|
3038
|
+
def _is_skew_set(G, S):
|
3039
|
+
r"""
|
3040
|
+
Check if ``S`` is a skew set over the group ``G``.
|
3041
|
+
|
3042
|
+
From [Djo1994a]_, a set `S \subset G` (where `G` is a finite abelian group of order `n`) is of skew
|
3043
|
+
type if `S_1 \cap (-S_1) = \emptyset` and `S_1 \cup (-S_1) = G\setminus \{0\}`.
|
3044
|
+
|
3045
|
+
INPUT:
|
3046
|
+
|
3047
|
+
- ``G`` -- a group
|
3048
|
+
- ``S`` -- list containing elements of ``G``; the set to be checked
|
3049
|
+
|
3050
|
+
EXAMPLES::
|
3051
|
+
|
3052
|
+
sage: from sage.combinat.designs.difference_family import _is_skew_set
|
3053
|
+
sage: Z5 = Zmod(5)
|
3054
|
+
sage: _is_skew_set(Z5, [Z5(1), Z5(2)])
|
3055
|
+
True
|
3056
|
+
sage: _is_skew_set(Z5, [Z5(1), Z5(2), Z5(3)])
|
3057
|
+
False
|
3058
|
+
sage: _is_skew_set(Z5, [Z5(1)])
|
3059
|
+
False
|
3060
|
+
"""
|
3061
|
+
for el in S:
|
3062
|
+
if -el in S:
|
3063
|
+
return False
|
3064
|
+
for el in G:
|
3065
|
+
if el == 0:
|
3066
|
+
continue
|
3067
|
+
if el not in S and -el not in S:
|
3068
|
+
return False
|
3069
|
+
return True
|
3070
|
+
|
3071
|
+
|
3072
|
+
def are_complementary_difference_sets(G, A, B, verbose=False):
|
3073
|
+
r"""
|
3074
|
+
Check if ``A`` and ``B`` are complementary difference sets over the group ``G``.
|
3075
|
+
|
3076
|
+
According to [Sze1971]_, two sets `A`, `B` of size `m` are complementary
|
3077
|
+
difference sets over a group `G` of size `2m+1` if:
|
3078
|
+
|
3079
|
+
1. they are `2-\{2m+1; m, m; m-1\}` supplementary difference sets
|
3080
|
+
2. `A` is skew, i.e. `a \in A` implies `-a \not \in A`
|
3081
|
+
|
3082
|
+
INPUT:
|
3083
|
+
|
3084
|
+
- ``G`` -- a group of odd order
|
3085
|
+
- ``A`` -- set of elements of ``G``
|
3086
|
+
- ``B`` -- set of elements of ``G``
|
3087
|
+
- ``verbose`` -- boolean (default: ``False``); if ``True`` the function will
|
3088
|
+
be verbose when the sets do not satisfy the constraints
|
3089
|
+
|
3090
|
+
EXAMPLES::
|
3091
|
+
|
3092
|
+
sage: from sage.combinat.designs.difference_family import are_complementary_difference_sets
|
3093
|
+
sage: are_complementary_difference_sets(Zmod(7), [1, 2, 4], [1, 2, 4])
|
3094
|
+
True
|
3095
|
+
|
3096
|
+
If ``verbose=True``, the function will be verbose::
|
3097
|
+
|
3098
|
+
sage: are_complementary_difference_sets(Zmod(7), [1, 2, 5], [1, 2, 4], verbose=True)
|
3099
|
+
The sets are not supplementary difference sets with lambda = 2
|
3100
|
+
False
|
3101
|
+
|
3102
|
+
TESTS::
|
3103
|
+
|
3104
|
+
sage: are_complementary_difference_sets(Zmod(16), [1, 2, 4], [1, 2, 4])
|
3105
|
+
False
|
3106
|
+
sage: are_complementary_difference_sets(Zmod(7), [1, 2, 4], [1, 2, 3, 4])
|
3107
|
+
False
|
3108
|
+
sage: are_complementary_difference_sets(Zmod(19), [1, 4, 5, 6, 7, 9, 11, 16, 17], [1, 4, 5, 6, 7, 9, 11, 16, 17])
|
3109
|
+
True
|
3110
|
+
|
3111
|
+
.. SEEALSO::
|
3112
|
+
|
3113
|
+
:func:`is_supplementary_difference_set`
|
3114
|
+
"""
|
3115
|
+
n = G.order()
|
3116
|
+
|
3117
|
+
if n % 2 != 1:
|
3118
|
+
if verbose:
|
3119
|
+
print('G must have odd order')
|
3120
|
+
return False
|
3121
|
+
|
3122
|
+
m = (n - 1) // 2
|
3123
|
+
|
3124
|
+
if len(A) != m or len(B) != m:
|
3125
|
+
if verbose:
|
3126
|
+
print(f'A and B must have size {m}')
|
3127
|
+
return False
|
3128
|
+
|
3129
|
+
if not is_supplementary_difference_set([A, B], lmbda=m-1, G=G):
|
3130
|
+
if verbose:
|
3131
|
+
print(f'The sets are not supplementary difference sets with lambda = {m-1}')
|
3132
|
+
return False
|
3133
|
+
|
3134
|
+
if not _is_skew_set(G, A):
|
3135
|
+
if verbose:
|
3136
|
+
print('The set A is not skew')
|
3137
|
+
return False
|
3138
|
+
|
3139
|
+
return True
|
3140
|
+
|
3141
|
+
|
3142
|
+
def complementary_difference_setsI(n, check=True):
|
3143
|
+
r"""
|
3144
|
+
Construct complementary difference sets in a group of order `n \cong 3 \mod 4`, `n` a prime power.
|
3145
|
+
|
3146
|
+
Let `G` be a Galois Field of order `n`, where `n` satisfies the requirements
|
3147
|
+
above. Let `A` be the set of nonzero quadratic elements in `G`, and `B = A`.
|
3148
|
+
Then `A` and `B` are complementary difference sets over a group of order `n`.
|
3149
|
+
This construction is described in [Sze1971]_.
|
3150
|
+
|
3151
|
+
INPUT:
|
3152
|
+
|
3153
|
+
- ``n`` -- integer; the order of the group `G`
|
3154
|
+
- ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
|
3155
|
+
are complementary difference sets before returning them
|
3156
|
+
|
3157
|
+
OUTPUT:
|
3158
|
+
|
3159
|
+
The function returns the Galois field of order ``n`` and the two sets, or raises
|
3160
|
+
an error if ``n`` does not satisfy the requirements of this construction.
|
3161
|
+
|
3162
|
+
EXAMPLES::
|
3163
|
+
|
3164
|
+
sage: from sage.combinat.designs.difference_family import complementary_difference_setsI
|
3165
|
+
sage: complementary_difference_setsI(19)
|
3166
|
+
(Finite Field of size 19,
|
3167
|
+
[1, 4, 5, 6, 7, 9, 11, 16, 17],
|
3168
|
+
[1, 4, 5, 6, 7, 9, 11, 16, 17])
|
3169
|
+
|
3170
|
+
TESTS::
|
3171
|
+
|
3172
|
+
sage: from sage.combinat.designs.difference_family import are_complementary_difference_sets
|
3173
|
+
sage: G, A, B = complementary_difference_setsI(23, check=False)
|
3174
|
+
sage: are_complementary_difference_sets(G, A, B)
|
3175
|
+
True
|
3176
|
+
sage: complementary_difference_setsI(17)
|
3177
|
+
Traceback (most recent call last):
|
3178
|
+
...
|
3179
|
+
ValueError: the parameter 17 is not valid
|
3180
|
+
sage: complementary_difference_setsI(15) # needs sage.libs.pari
|
3181
|
+
Traceback (most recent call last):
|
3182
|
+
...
|
3183
|
+
ValueError: the parameter 15 is not valid
|
3184
|
+
|
3185
|
+
.. SEEALSO::
|
3186
|
+
|
3187
|
+
:func:`are_complementary_difference_sets`
|
3188
|
+
:func:`complementary_difference_sets`
|
3189
|
+
"""
|
3190
|
+
if not (n % 4 == 3 and is_prime_power(n)):
|
3191
|
+
raise ValueError(f'the parameter {n} is not valid')
|
3192
|
+
|
3193
|
+
from sage.rings.finite_rings.finite_field_constructor import GF
|
3194
|
+
|
3195
|
+
G = GF(n, 'a')
|
3196
|
+
A = list({x**2 for x in G if x**2 != 0})
|
3197
|
+
B = A
|
3198
|
+
if check:
|
3199
|
+
assert are_complementary_difference_sets(G, A, B)
|
3200
|
+
|
3201
|
+
return G, A, B
|
3202
|
+
|
3203
|
+
|
3204
|
+
def complementary_difference_setsII(n, check=True):
|
3205
|
+
r"""
|
3206
|
+
Construct complementary difference sets in a group of order `n = p^t`, where `p \cong 5 \mod 8` and `t \cong 1, 2, 3 \mod 4`.
|
3207
|
+
|
3208
|
+
Consider a finite field `G` of order `n` and let `\rho` be the generator of
|
3209
|
+
the corresponding multiplicative group. Then, there are two different constructions,
|
3210
|
+
depending on whether `t` is even or odd.
|
3211
|
+
|
3212
|
+
If `t \cong 2 \mod 4`, let `C_0` be the set of nonzero octic residues in `G`,
|
3213
|
+
and let `C_i = \rho^i C_0` for `1 \le i \le 7`.
|
3214
|
+
Then, `A = C_0 \cup C_1 \cup C_2 \cup C_3` and `B = C_0 \cup C_1 \cup C_6 \cup C_7`.
|
3215
|
+
|
3216
|
+
If `t` is odd, let `C_0` be the set of nonzero fourth powers in `G`, and let
|
3217
|
+
`C_i = \rho^i C_0` for `1 \le i \le 3`.
|
3218
|
+
Then, `A = C_0 \cup C_1` and `B = C_0 \cup C_3`.
|
3219
|
+
|
3220
|
+
For more details on this construction, see [Sze1971]_.
|
3221
|
+
|
3222
|
+
INPUT:
|
3223
|
+
|
3224
|
+
- ``n`` -- integer; the order of the group `G`
|
3225
|
+
- ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
|
3226
|
+
are complementary difference sets before returning them; setting this to
|
3227
|
+
``False`` might speed up the computation for large values of ``n``
|
3228
|
+
|
3229
|
+
OUTPUT:
|
3230
|
+
|
3231
|
+
The function returns the Galois field of order ``n`` and the two sets, or raises
|
3232
|
+
an error if ``n`` does not satisfy the requirements of this construction.
|
3233
|
+
|
3234
|
+
EXAMPLES::
|
3235
|
+
|
3236
|
+
sage: from sage.combinat.designs.difference_family import complementary_difference_setsII
|
3237
|
+
sage: complementary_difference_setsII(5) # needs sage.libs.pari
|
3238
|
+
(Finite Field of size 5, [1, 2], [1, 3])
|
3239
|
+
|
3240
|
+
TESTS::
|
3241
|
+
|
3242
|
+
sage: # needs sage.libs.pari
|
3243
|
+
sage: from sage.combinat.designs.difference_family import are_complementary_difference_sets
|
3244
|
+
sage: G, A, B = complementary_difference_setsII(25, check=False)
|
3245
|
+
sage: are_complementary_difference_sets(G, A, B)
|
3246
|
+
True
|
3247
|
+
sage: G, A, B = complementary_difference_setsII(13, check=False)
|
3248
|
+
sage: are_complementary_difference_sets(G, A, B)
|
3249
|
+
True
|
3250
|
+
sage: complementary_difference_setsII(49)
|
3251
|
+
Traceback (most recent call last):
|
3252
|
+
...
|
3253
|
+
ValueError: the parameter 49 is not valid
|
3254
|
+
sage: complementary_difference_setsII(15)
|
3255
|
+
Traceback (most recent call last):
|
3256
|
+
...
|
3257
|
+
ValueError: the parameter 15 is not valid
|
3258
|
+
|
3259
|
+
.. SEEALSO::
|
3260
|
+
|
3261
|
+
:func:`are_complementary_difference_sets`
|
3262
|
+
:func:`complementary_difference_sets`
|
3263
|
+
"""
|
3264
|
+
p, t = is_prime_power(n, get_data=True)
|
3265
|
+
if not (p % 8 == 5 and t > 0 and t % 4 in [1, 2, 3]):
|
3266
|
+
raise ValueError(f'the parameter {n} is not valid')
|
3267
|
+
|
3268
|
+
from sage.rings.finite_rings.finite_field_constructor import GF
|
3269
|
+
G = GF(n, 'a')
|
3270
|
+
A, B = None, None
|
3271
|
+
|
3272
|
+
if t % 2 == 0:
|
3273
|
+
rho = G.multiplicative_generator()
|
3274
|
+
C0 = list({el**8 for el in G if el != 0})
|
3275
|
+
C1, C2, C3, C6, C7 = ([rho**i * el for el in C0] for i in [1, 2, 3, 6, 7])
|
3276
|
+
A = C0 + C1 + C2 + C3
|
3277
|
+
B = C0 + C1 + C6 + C7
|
3278
|
+
else:
|
3279
|
+
rho = G.multiplicative_generator()
|
3280
|
+
C0 = list({el**4 for el in G if el**4 != 0})
|
3281
|
+
C1 = [rho * el for el in C0]
|
3282
|
+
C3 = [rho**3 * el for el in C0]
|
3283
|
+
A = C0 + C1
|
3284
|
+
B = C0 + C3
|
3285
|
+
|
3286
|
+
if check:
|
3287
|
+
assert are_complementary_difference_sets(G, A, B)
|
3288
|
+
|
3289
|
+
return G, A, B
|
3290
|
+
|
3291
|
+
|
3292
|
+
def complementary_difference_setsIII(n, check=True):
|
3293
|
+
r"""
|
3294
|
+
Construct complementary difference sets in a group of order `n = 2m + 1`, where `4m + 3` is a prime power.
|
3295
|
+
|
3296
|
+
Consider a finite field `G` of order `n` and let `\rho` be a primite element
|
3297
|
+
of this group. Now let `Q` be the set of nonzero quadratic residues in `G`,
|
3298
|
+
and let `A = \{ a | \rho^{2a} - 1 \in Q\}`, `B' = \{ b | -(\rho^{2b} + 1) \in Q\}`.
|
3299
|
+
Then `A` and `B = Q \setminus B'` are complementary difference sets over the ring
|
3300
|
+
of integers modulo `n`. For more details, see [Sz1969]_.
|
3301
|
+
|
3302
|
+
INPUT:
|
3303
|
+
|
3304
|
+
- ``n`` -- integer; the order of the group over which the sets are constructed
|
3305
|
+
- ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
|
3306
|
+
are complementary difference sets before returning them; setting this to
|
3307
|
+
``False`` might speed up the computation for large values of ``n``
|
3308
|
+
|
3309
|
+
OUTPUT:
|
3310
|
+
|
3311
|
+
The function returns the Galois field of order ``n`` and the two sets, or raises
|
3312
|
+
an error if ``n`` does not satisfy the requirements of this construction.
|
3313
|
+
|
3314
|
+
EXAMPLES::
|
3315
|
+
|
3316
|
+
sage: from sage.combinat.designs.difference_family import complementary_difference_setsIII
|
3317
|
+
sage: complementary_difference_setsIII(11) # needs sage.libs.pari
|
3318
|
+
(Ring of integers modulo 11, [1, 2, 5, 7, 8], [0, 1, 3, 8, 10])
|
3319
|
+
|
3320
|
+
TESTS::
|
3321
|
+
|
3322
|
+
sage: from sage.combinat.designs.difference_family import are_complementary_difference_sets
|
3323
|
+
sage: G, A, B = complementary_difference_setsIII(21, check=False) # needs sage.libs.pari
|
3324
|
+
sage: are_complementary_difference_sets(G, A, B) # needs sage.libs.pari
|
3325
|
+
True
|
3326
|
+
sage: G, A, B = complementary_difference_setsIII(65, check=False) # needs sage.libs.pari
|
3327
|
+
sage: are_complementary_difference_sets(G, A, B) # needs sage.libs.pari
|
3328
|
+
True
|
3329
|
+
sage: complementary_difference_setsIII(10)
|
3330
|
+
Traceback (most recent call last):
|
3331
|
+
...
|
3332
|
+
ValueError: the parameter 10 is not valid
|
3333
|
+
sage: complementary_difference_setsIII(17) # needs sage.libs.pari
|
3334
|
+
Traceback (most recent call last):
|
3335
|
+
...
|
3336
|
+
ValueError: the parameter 17 is not valid
|
3337
|
+
|
3338
|
+
.. SEEALSO::
|
3339
|
+
|
3340
|
+
:func:`are_complementary_difference_sets`
|
3341
|
+
:func:`complementary_difference_sets`
|
3342
|
+
"""
|
3343
|
+
m = (n - 1) // 2
|
3344
|
+
q = 4*m + 3
|
3345
|
+
if n % 2 != 1 or not is_prime_power(q):
|
3346
|
+
raise ValueError(f'the parameter {n} is not valid')
|
3347
|
+
|
3348
|
+
from sage.rings.finite_rings.finite_field_constructor import GF
|
3349
|
+
G = Zmod(n)
|
3350
|
+
G2 = GF(q)
|
3351
|
+
rho = G2.primitive_element()
|
3352
|
+
|
3353
|
+
Q = [rho ** (2*b) for b in range(1, n+1)]
|
3354
|
+
|
3355
|
+
A = [G(a) for a in range(n) if rho**(2*a) - 1 in Q]
|
3356
|
+
B = [G(b) for b in range(n) if -rho**(2*b) - 1 not in Q]
|
3357
|
+
|
3358
|
+
if check:
|
3359
|
+
assert are_complementary_difference_sets(G, A, B)
|
3360
|
+
|
3361
|
+
return G, A, B
|
3362
|
+
|
3363
|
+
|
3364
|
+
def complementary_difference_sets(n, existence=False, check=True):
|
3365
|
+
r"""
|
3366
|
+
Compute complementary difference sets over a group of order `n = 2m + 1`.
|
3367
|
+
|
3368
|
+
According to [Sze1971]_, two sets `A`, `B` of size `m` are complementary
|
3369
|
+
difference sets over a group `G` of size `n = 2m + 1` if:
|
3370
|
+
|
3371
|
+
1. they are `2-\{2m+1; m, m; m-1\}` supplementary difference sets
|
3372
|
+
2. `A` is skew, i.e. `a \in A` implies `-a \not \in A`
|
3373
|
+
|
3374
|
+
This method tries to call :func:`complementary_difference_setsI`,
|
3375
|
+
:func:`complementary_difference_setsII` or :func:`complementary_difference_setsIII`
|
3376
|
+
if the parameter `n` satisfies the requirements of one of these functions.
|
3377
|
+
|
3378
|
+
INPUT:
|
3379
|
+
|
3380
|
+
- ``n`` -- integer; the order of the group over which the sets are constructed
|
3381
|
+
- ``existence`` -- boolean (default: ``False``); if ``True``, only check
|
3382
|
+
whether the supplementary difference sets can be constructed
|
3383
|
+
- ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
|
3384
|
+
are complementary difference sets before returning them; setting this to
|
3385
|
+
``False`` might speed up the computation for large values of ``n``
|
3386
|
+
|
3387
|
+
OUTPUT:
|
3388
|
+
|
3389
|
+
If ``existence=False``, the function returns group ``G`` and two complementary
|
3390
|
+
difference sets, or raises an error if data for the given ``n`` is not available.
|
3391
|
+
If ``existence=True``, the function returns a boolean representing whether
|
3392
|
+
complementary difference sets can be constructed for the given ``n``.
|
3393
|
+
|
3394
|
+
EXAMPLES::
|
3395
|
+
|
3396
|
+
sage: from sage.combinat.designs.difference_family import complementary_difference_sets
|
3397
|
+
sage: complementary_difference_sets(15) # needs sage.libs.pari
|
3398
|
+
(Ring of integers modulo 15, [1, 2, 4, 6, 7, 10, 12], [0, 1, 2, 6, 9, 13, 14])
|
3399
|
+
|
3400
|
+
If ``existence=True``, the function returns a boolean::
|
3401
|
+
|
3402
|
+
sage: complementary_difference_sets(15, existence=True) # needs sage.libs.pari
|
3403
|
+
True
|
3404
|
+
sage: complementary_difference_sets(16, existence=True)
|
3405
|
+
False
|
3406
|
+
|
3407
|
+
TESTS::
|
3408
|
+
|
3409
|
+
sage: from sage.combinat.designs.difference_family import are_complementary_difference_sets
|
3410
|
+
sage: G, A, B = complementary_difference_sets(29) # needs sage.libs.pari
|
3411
|
+
sage: are_complementary_difference_sets(G, A, B) # needs sage.libs.pari
|
3412
|
+
True
|
3413
|
+
sage: G, A, B = complementary_difference_sets(65) # needs sage.libs.pari
|
3414
|
+
sage: are_complementary_difference_sets(G, A, B) # needs sage.libs.pari
|
3415
|
+
True
|
3416
|
+
sage: complementary_difference_sets(10)
|
3417
|
+
Traceback (most recent call last):
|
3418
|
+
...
|
3419
|
+
ValueError: the parameter n must be odd
|
3420
|
+
sage: complementary_difference_sets(17) # needs sage.libs.pari
|
3421
|
+
Traceback (most recent call last):
|
3422
|
+
...
|
3423
|
+
NotImplementedError: complementary difference sets of order 17 are not implemented yet
|
3424
|
+
|
3425
|
+
.. SEEALSO::
|
3426
|
+
|
3427
|
+
:func:`are_complementary_difference_sets`
|
3428
|
+
"""
|
3429
|
+
if n % 2 == 0:
|
3430
|
+
if existence:
|
3431
|
+
return False
|
3432
|
+
raise ValueError('the parameter n must be odd')
|
3433
|
+
|
3434
|
+
p, t = is_prime_power(n, get_data=True)
|
3435
|
+
G, A, B = None, None, None
|
3436
|
+
|
3437
|
+
if n % 4 == 3 and t > 0:
|
3438
|
+
if existence:
|
3439
|
+
return True
|
3440
|
+
G, A, B = complementary_difference_setsI(n, check=False)
|
3441
|
+
elif p % 8 == 5 and t > 0 and t % 4 in [1, 2, 3]:
|
3442
|
+
if existence:
|
3443
|
+
return True
|
3444
|
+
G, A, B = complementary_difference_setsII(n, check=False)
|
3445
|
+
elif is_prime_power(2*n + 1):
|
3446
|
+
if existence:
|
3447
|
+
return True
|
3448
|
+
G, A, B = complementary_difference_setsIII(n, check=False)
|
3449
|
+
|
3450
|
+
if existence:
|
3451
|
+
return False
|
3452
|
+
|
3453
|
+
if G is None:
|
3454
|
+
raise NotImplementedError(f'complementary difference sets of order {n} are not implemented yet')
|
3455
|
+
|
3456
|
+
if check:
|
3457
|
+
assert are_complementary_difference_sets(G, A, B)
|
3458
|
+
return G, A, B
|
3459
|
+
|
3460
|
+
|
3461
|
+
def difference_family(v, k, l=1, existence=False, explain_construction=False, check=True):
|
3462
|
+
r"""
|
3463
|
+
Return a (``k``, ``l``)-difference family on an Abelian group of cardinality ``v``.
|
3464
|
+
|
3465
|
+
Let `G` be a finite Abelian group. For a given subset `D` of `G`, we define
|
3466
|
+
`\Delta D` to be the multi-set of differences `\Delta D = \{x - y; x \in D,
|
3467
|
+
y \in D, x \not= y\}`. A `(G,k,\lambda)`-*difference family* is a collection
|
3468
|
+
of `k`-subsets of `G`, `D = \{D_1, D_2, \ldots, D_b\}` such that the union
|
3469
|
+
of the difference sets `\Delta D_i` for `i=1,...b`, seen as a multi-set,
|
3470
|
+
contains each element of `G \backslash \{0\}` exactly `\lambda`-times.
|
3471
|
+
|
3472
|
+
When there is only one block, i.e. `\lambda(v - 1) = k(k-1)`, then a
|
3473
|
+
`(G,k,\lambda)`-difference family is also called a *difference set*.
|
3474
|
+
|
3475
|
+
See also :wikipedia:`Difference_set`.
|
3476
|
+
|
3477
|
+
If there is no such difference family, an :exc:`EmptySetError` is raised and
|
3478
|
+
if there is no construction at the moment :exc:`NotImplementedError`
|
3479
|
+
is raised.
|
3480
|
+
|
3481
|
+
INPUT:
|
3482
|
+
|
3483
|
+
- ``v``, ``k``, ``l`` -- parameters of the difference family. If ``l`` is
|
3484
|
+
not provided it is assumed to be ``1``
|
3485
|
+
- ``existence`` -- if ``True``, then return either ``True`` if Sage knows
|
3486
|
+
how to build such design, ``Unknown`` if it does not and ``False`` if it
|
3487
|
+
knows that the design does not exist
|
3488
|
+
- ``explain_construction`` -- instead of returning a difference family,
|
3489
|
+
returns a string that explains the construction used
|
3490
|
+
- ``check`` -- boolean (default: ``True``); if ``True``, then the result of
|
3491
|
+
the computation is checked before being returned. This should not be
|
3492
|
+
needed but ensures that the output is correct
|
3493
|
+
|
3494
|
+
OUTPUT:
|
3495
|
+
|
3496
|
+
A pair ``(G,D)`` made of a group `G` and a difference family `D` on that
|
3497
|
+
group. Or, if ``existence=True`` a troolean or if
|
3498
|
+
``explain_construction=True`` a string.
|
3499
|
+
|
3500
|
+
EXAMPLES::
|
3501
|
+
|
3502
|
+
sage: G,D = designs.difference_family(73,4) # needs sage.libs.pari
|
3503
|
+
sage: G # needs sage.libs.pari
|
3504
|
+
Finite Field of size 73
|
3505
|
+
sage: D # needs sage.libs.pari
|
3506
|
+
[[0, 1, 5, 18],
|
3507
|
+
[0, 3, 15, 54],
|
3508
|
+
[0, 9, 45, 16],
|
3509
|
+
[0, 27, 62, 48],
|
3510
|
+
[0, 8, 40, 71],
|
3511
|
+
[0, 24, 47, 67]]
|
3512
|
+
|
3513
|
+
sage: print(designs.difference_family(73, 4, explain_construction=True))
|
3514
|
+
The database contains a (73,4)-evenly distributed set
|
3515
|
+
|
3516
|
+
sage: # needs sage.libs.pari
|
3517
|
+
sage: G,D = designs.difference_family(15,7,3)
|
3518
|
+
sage: G
|
3519
|
+
Ring of integers modulo 15
|
3520
|
+
sage: D
|
3521
|
+
[[0, 1, 2, 4, 5, 8, 10]]
|
3522
|
+
sage: print(designs.difference_family(15,7,3,explain_construction=True))
|
3523
|
+
Singer difference set
|
3524
|
+
|
3525
|
+
sage: # needs sage.libs.pari
|
3526
|
+
sage: print(designs.difference_family(91,10,1,explain_construction=True))
|
3527
|
+
Singer difference set
|
3528
|
+
sage: print(designs.difference_family(64,28,12, explain_construction=True))
|
3529
|
+
McFarland 1973 construction
|
3530
|
+
sage: print(designs.difference_family(576, 276, 132, explain_construction=True))
|
3531
|
+
Hadamard difference set product from N1=2 and N2=3
|
3532
|
+
|
3533
|
+
For `k=6,7` we look at the set of small prime powers for which a
|
3534
|
+
construction is available::
|
3535
|
+
|
3536
|
+
sage: def prime_power_mod(r, m):
|
3537
|
+
....: k = m+r
|
3538
|
+
....: while True:
|
3539
|
+
....: if is_prime_power(k):
|
3540
|
+
....: yield k
|
3541
|
+
....: k += m
|
3542
|
+
|
3543
|
+
sage: # needs sage.libs.pari
|
3544
|
+
sage: from itertools import islice
|
3545
|
+
sage: l6 = {True: [], False: [], Unknown: []}
|
3546
|
+
sage: for q in islice(prime_power_mod(1,30), int(60)):
|
3547
|
+
....: l6[designs.difference_family(q,6,existence=True)].append(q)
|
3548
|
+
sage: l6[True]
|
3549
|
+
[31, 121, 151, 181, 211, ..., 3061, 3121, 3181]
|
3550
|
+
sage: l6[Unknown]
|
3551
|
+
[61]
|
3552
|
+
sage: l6[False]
|
3553
|
+
[]
|
3554
|
+
|
3555
|
+
sage: # needs sage.libs.pari
|
3556
|
+
sage: l7 = {True: [], False: [], Unknown: []}
|
3557
|
+
sage: for q in islice(prime_power_mod(1,42), int(60)):
|
3558
|
+
....: l7[designs.difference_family(q,7,existence=True)].append(q)
|
3559
|
+
sage: l7[True]
|
3560
|
+
[169, 337, 379, 421, 463, 547, 631, 673, 757, 841, 883, 967, ..., 4621, 4957, 5167]
|
3561
|
+
sage: l7[Unknown]
|
3562
|
+
[43, 127, 211, 2017, 2143, 2269, 2311, 2437, 2521, 2647, ..., 4999, 5041, 5209]
|
3563
|
+
sage: l7[False]
|
3564
|
+
[]
|
3565
|
+
|
3566
|
+
List available constructions::
|
3567
|
+
|
3568
|
+
sage: for v in range(2,100): # needs sage.libs.pari
|
3569
|
+
....: constructions = []
|
3570
|
+
....: for k in range(2,10):
|
3571
|
+
....: for l in range(1,10):
|
3572
|
+
....: ret = designs.difference_family(v,k,l,existence=True)
|
3573
|
+
....: if ret is True:
|
3574
|
+
....: constructions.append((k,l))
|
3575
|
+
....: _ = designs.difference_family(v,k,l)
|
3576
|
+
....: if constructions:
|
3577
|
+
....: print("%2d: %s"%(v, ', '.join('(%d,%d)'%(k,l) for k,l in constructions)))
|
3578
|
+
3: (2,1)
|
3579
|
+
4: (3,2)
|
3580
|
+
5: (2,1), (4,3)
|
3581
|
+
6: (5,4)
|
3582
|
+
7: (2,1), (3,1), (3,2), (4,2), (6,5)
|
3583
|
+
8: (7,6)
|
3584
|
+
9: (2,1), (4,3), (8,7)
|
3585
|
+
10: (9,8)
|
3586
|
+
11: (2,1), (4,6), (5,2), (5,4), (6,3)
|
3587
|
+
13: (2,1), (3,1), (3,2), (4,1), (4,3), (5,5), (6,5)
|
3588
|
+
15: (3,1), (4,6), (5,6), (7,3), (7,6)
|
3589
|
+
16: (3,2), (5,4), (6,2)
|
3590
|
+
17: (2,1), (4,3), (5,5), (8,7)
|
3591
|
+
19: (2,1), (3,1), (3,2), (4,2), (6,5), (9,4), (9,8)
|
3592
|
+
21: (3,1), (4,3), (5,1), (6,3), (6,5)
|
3593
|
+
22: (4,2), (6,5), (7,4), (8,8)
|
3594
|
+
23: (2,1)
|
3595
|
+
25: (2,1), (3,1), (3,2), (4,1), (4,3), (6,5), (7,7), (8,7)
|
3596
|
+
27: (2,1), (3,1)
|
3597
|
+
28: (3,2), (6,5)
|
3598
|
+
29: (2,1), (4,3), (7,3), (7,6), (8,4), (8,6)
|
3599
|
+
31: (2,1), (3,1), (3,2), (4,2), (5,2), (5,4), (6,1), (6,5)
|
3600
|
+
33: (3,1), (5,5), (6,5)
|
3601
|
+
34: (4,2)
|
3602
|
+
35: (5,2)
|
3603
|
+
37: (2,1), (3,1), (3,2), (4,1), (4,3), (6,5), (9,2), (9,8)
|
3604
|
+
39: (3,1), (6,5)
|
3605
|
+
40: (3,2), (4,1)
|
3606
|
+
41: (2,1), (4,3), (5,1), (5,4), (6,3), (8,7)
|
3607
|
+
43: (2,1), (3,1), (3,2), (4,2), (6,5), (7,2), (7,3), (7,6), (8,4)
|
3608
|
+
45: (3,1), (5,1)
|
3609
|
+
46: (4,2), (6,2)
|
3610
|
+
47: (2,1)
|
3611
|
+
49: (2,1), (3,1), (3,2), (4,1), (4,3), (6,5), (8,7), (9,3)
|
3612
|
+
51: (3,1), (5,2), (6,3)
|
3613
|
+
52: (4,1)
|
3614
|
+
53: (2,1), (4,3)
|
3615
|
+
55: (3,1), (9,4)
|
3616
|
+
57: (3,1), (7,3), (8,1)
|
3617
|
+
59: (2,1)
|
3618
|
+
61: (2,1), (3,1), (3,2), (4,1), (4,3), (5,1), (5,4), (6,2), (6,3), (6,5)
|
3619
|
+
63: (3,1)
|
3620
|
+
64: (3,2), (4,1), (7,2), (7,6), (9,8)
|
3621
|
+
65: (5,1)
|
3622
|
+
67: (2,1), (3,1), (3,2), (6,5)
|
3623
|
+
69: (3,1)
|
3624
|
+
71: (2,1), (5,2), (5,4), (7,3), (7,6), (8,4)
|
3625
|
+
73: (2,1), (3,1), (3,2), (4,1), (4,3), (6,5), (8,7), (9,1), (9,8)
|
3626
|
+
75: (3,1), (5,2)
|
3627
|
+
76: (4,1)
|
3628
|
+
79: (2,1), (3,1), (3,2), (6,5)
|
3629
|
+
81: (2,1), (3,1), (4,3), (5,1), (5,4), (8,7)
|
3630
|
+
83: (2,1)
|
3631
|
+
85: (4,1), (7,2), (7,3), (8,2)
|
3632
|
+
89: (2,1), (4,3), (8,7)
|
3633
|
+
91: (6,1), (7,1)
|
3634
|
+
97: (2,1), (3,1), (3,2), (4,1), (4,3), (6,5), (8,7), (9,3)
|
3635
|
+
|
3636
|
+
TESTS:
|
3637
|
+
|
3638
|
+
Check more of the Wilson constructions from [Wi72]_::
|
3639
|
+
|
3640
|
+
sage: Q5 = [241, 281,421,601,641, 661, 701, 821,881]
|
3641
|
+
sage: Q9 = [73, 1153, 1873, 2017]
|
3642
|
+
sage: Q15 = [76231]
|
3643
|
+
sage: Q4 = [13, 73, 97, 109, 181, 229, 241, 277, 337, 409, 421, 457]
|
3644
|
+
sage: Q8 = [1009, 3137, 3697]
|
3645
|
+
sage: for Q,k in [(Q4,4),(Q5,5),(Q8,8),(Q9,9),(Q15,15)]: # needs sage.libs.pari
|
3646
|
+
....: for q in Q:
|
3647
|
+
....: assert designs.difference_family(q,k,1,existence=True) is True
|
3648
|
+
....: _ = designs.difference_family(q,k,1)
|
3649
|
+
|
3650
|
+
Check Singer difference sets::
|
3651
|
+
|
3652
|
+
sage: sgp = lambda q,d: ((q**(d+1)-1)//(q-1), (q**d-1)//(q-1), (q**(d-1)-1)//(q-1))
|
3653
|
+
|
3654
|
+
sage: for q in range(2,10): # needs sage.libs.pari
|
3655
|
+
....: if is_prime_power(q):
|
3656
|
+
....: for d in [2,3,4]:
|
3657
|
+
....: v,k,l = sgp(q,d)
|
3658
|
+
....: assert designs.difference_family(v,k,l,existence=True) is True
|
3659
|
+
....: _ = designs.difference_family(v,k,l)
|
3660
|
+
|
3661
|
+
Check twin primes difference sets::
|
3662
|
+
|
3663
|
+
sage: for p in [3,5,7,9,11]: # needs sage.libs.pari
|
3664
|
+
....: v = p*(p+2); k = (v-1)/2; lmbda = (k-1)/2
|
3665
|
+
....: G,D = designs.difference_family(v,k,lmbda)
|
3666
|
+
|
3667
|
+
Check Complementary difference sets::
|
3668
|
+
|
3669
|
+
sage: for v in [15, 33, 35, 39, 51]: # needs sage.libs.pari
|
3670
|
+
....: G, D = designs.difference_family(v, (v-1)//2, (v-1)//2-1)
|
3671
|
+
|
3672
|
+
Check the database::
|
3673
|
+
|
3674
|
+
sage: from sage.combinat.designs.database import DF,EDS
|
3675
|
+
sage: for v,k,l in DF:
|
3676
|
+
....: assert designs.difference_family(v,k,l,existence=True) is True
|
3677
|
+
....: df = designs.difference_family(v,k,l,check=True)
|
3678
|
+
|
3679
|
+
sage: for k in EDS: # needs sage.libs.pari
|
3680
|
+
....: for v in EDS[k]:
|
3681
|
+
....: assert designs.difference_family(v,k,1,existence=True) is True
|
3682
|
+
....: df = designs.difference_family(v,k,1,check=True)
|
3683
|
+
|
3684
|
+
Check the known Hadamard parameters::
|
3685
|
+
|
3686
|
+
sage: for N in range(2,21): # needs sage.libs.pari
|
3687
|
+
....: v = 4*N^2; k = 2*N^2-N; l = N^2-N
|
3688
|
+
....: status = designs.difference_family(v,k,l,existence=True)
|
3689
|
+
....: print("{:2} {}".format(N,designs.difference_family(v,k,l,explain_construction=True) if status is True else status))
|
3690
|
+
2 McFarland 1973 construction
|
3691
|
+
3 Turyn 1965 construction
|
3692
|
+
4 McFarland 1973 construction
|
3693
|
+
5 False
|
3694
|
+
6 The database contains a (144,66,30)-difference family
|
3695
|
+
7 False
|
3696
|
+
8 McFarland 1973 construction
|
3697
|
+
9 Unknown
|
3698
|
+
10 Unknown
|
3699
|
+
11 False
|
3700
|
+
12 Hadamard difference set product from N1=2 and N2=3
|
3701
|
+
13 False
|
3702
|
+
14 Unknown
|
3703
|
+
15 Unknown
|
3704
|
+
16 McFarland 1973 construction
|
3705
|
+
17 False
|
3706
|
+
18 Hadamard difference set product from N1=3 and N2=3
|
3707
|
+
19 False
|
3708
|
+
20 Unknown
|
3709
|
+
|
3710
|
+
Check a failing construction (:issue:`17528`)::
|
3711
|
+
|
3712
|
+
sage: designs.difference_family(9,3)
|
3713
|
+
Traceback (most recent call last):
|
3714
|
+
...
|
3715
|
+
NotImplementedError: No construction available for (9,3,1)-difference family
|
3716
|
+
|
3717
|
+
Check that when ``existence=True`` we always obtain ``True``, ``False`` or ``Unknown``,
|
3718
|
+
and when ``explain_construction=True``, it is a string (see :issue:`24513`)::
|
3719
|
+
|
3720
|
+
sage: designs.difference_family(3, 2, 1, existence=True)
|
3721
|
+
True
|
3722
|
+
sage: designs.difference_family(3, 2, 1, explain_construction=True)
|
3723
|
+
'Trivial difference family'
|
3724
|
+
|
3725
|
+
sage: for _ in range(100): # needs sage.libs.pari
|
3726
|
+
....: v = randint(1, 30)
|
3727
|
+
....: k = randint(2, 30)
|
3728
|
+
....: l = randint(1, 30)
|
3729
|
+
....: res = designs.difference_family(v, k, l, existence=True)
|
3730
|
+
....: assert res is True or res is False or res is Unknown
|
3731
|
+
....: if res is True:
|
3732
|
+
....: assert isinstance(designs.difference_family(3, 2, 1, explain_construction=True), str)
|
3733
|
+
|
3734
|
+
.. TODO::
|
3735
|
+
|
3736
|
+
Implement recursive constructions from Buratti "Recursive for difference
|
3737
|
+
matrices and relative difference families" (1998) and Jungnickel
|
3738
|
+
"Composition theorems for difference families and regular planes" (1978)
|
3739
|
+
"""
|
3740
|
+
from .block_design import are_hyperplanes_in_projective_geometry_parameters
|
3741
|
+
|
3742
|
+
from .database import DF, EDS
|
3743
|
+
|
3744
|
+
v = ZZ(v)
|
3745
|
+
k = ZZ(k)
|
3746
|
+
l = ZZ(l)
|
3747
|
+
|
3748
|
+
if v < 0 or k < 0 or l < 0:
|
3749
|
+
if existence:
|
3750
|
+
return False
|
3751
|
+
raise EmptySetError("No difference family eixsts with negative parameters")
|
3752
|
+
|
3753
|
+
if (v,k,l) in DF:
|
3754
|
+
if existence:
|
3755
|
+
return True
|
3756
|
+
elif explain_construction:
|
3757
|
+
return "The database contains a ({},{},{})-difference family".format(v,k,l)
|
3758
|
+
|
3759
|
+
vv, blocks = next(iter(DF[v,k,l].items()))
|
3760
|
+
|
3761
|
+
# Build the group
|
3762
|
+
from sage.rings.finite_rings.integer_mod_ring import Zmod
|
3763
|
+
if len(vv) == 1:
|
3764
|
+
G = Zmod(vv[0])
|
3765
|
+
else:
|
3766
|
+
from sage.categories.cartesian_product import cartesian_product
|
3767
|
+
G = cartesian_product([Zmod(i) for i in vv])
|
3768
|
+
|
3769
|
+
df = [[G(i) for i in b] for b in blocks]
|
3770
|
+
|
3771
|
+
if check and not is_difference_family(G, df, v=v, k=k, l=l):
|
3772
|
+
raise RuntimeError("There is an invalid ({},{},{})-difference "
|
3773
|
+
"family in the database... Please contact "
|
3774
|
+
"sage-devel@googlegroups.com".format(v,k,l))
|
3775
|
+
|
3776
|
+
return G,df
|
3777
|
+
|
3778
|
+
elif l == 1 and k in EDS and v in EDS[k]:
|
3779
|
+
if existence:
|
3780
|
+
return True
|
3781
|
+
elif explain_construction:
|
3782
|
+
return "The database contains a ({},{})-evenly distributed set".format(v,k)
|
3783
|
+
|
3784
|
+
from sage.rings.finite_rings.finite_field_constructor import GF
|
3785
|
+
poly,B = EDS[k][v]
|
3786
|
+
if poly is None: # q is prime
|
3787
|
+
K = G = GF(v)
|
3788
|
+
else:
|
3789
|
+
K = G = GF(v,'a',modulus=poly)
|
3790
|
+
|
3791
|
+
B = [K(b) for b in B]
|
3792
|
+
e = k*(k-1)//2
|
3793
|
+
xe = G.multiplicative_generator()**e
|
3794
|
+
df = [[xe**j*b for b in B] for j in range((v-1)//(2*e))]
|
3795
|
+
if check and not is_difference_family(G, df, v=v, k=k, l=l):
|
3796
|
+
raise RuntimeError("There is an invalid ({},{})-evenly distributed "
|
3797
|
+
"set in the database... Please contact "
|
3798
|
+
"sage-devel@googlegroups.com".format(v, k))
|
3799
|
+
return G, df
|
3800
|
+
|
3801
|
+
if k in [0,1]:
|
3802
|
+
# Then \Delta D_i is empty
|
3803
|
+
# So if G\{0} is empty is good, otherwise not
|
3804
|
+
if v == 1:
|
3805
|
+
if existence:
|
3806
|
+
return True
|
3807
|
+
from sage.rings.finite_rings.integer_mod_ring import Zmod
|
3808
|
+
l = [0] if k == 1 else []
|
3809
|
+
return Zmod(1),[l]
|
3810
|
+
|
3811
|
+
if existence:
|
3812
|
+
return False
|
3813
|
+
raise EmptySetError("No difference family exists with k=1 and v!=1")
|
3814
|
+
|
3815
|
+
e = k*(k-1)
|
3816
|
+
if (l*(v-1)) % e:
|
3817
|
+
if existence:
|
3818
|
+
return Unknown
|
3819
|
+
raise NotImplementedError("No construction available for ({},{},{})-difference family".format(v,k,l))
|
3820
|
+
|
3821
|
+
# trivial construction
|
3822
|
+
if k == (v-1) and l == (v-2):
|
3823
|
+
if existence:
|
3824
|
+
return True
|
3825
|
+
elif explain_construction:
|
3826
|
+
return "Trivial difference family"
|
3827
|
+
|
3828
|
+
from sage.rings.finite_rings.integer_mod_ring import Zmod
|
3829
|
+
G = Zmod(v)
|
3830
|
+
return G, [list(range(1, v))]
|
3831
|
+
|
3832
|
+
factorization = arith.factor(v)
|
3833
|
+
if len(factorization) == 1:
|
3834
|
+
from sage.rings.finite_rings.finite_field_constructor import GF
|
3835
|
+
K = GF(v,'z')
|
3836
|
+
|
3837
|
+
if are_mcfarland_1973_parameters(v,k,l):
|
3838
|
+
if existence:
|
3839
|
+
return True
|
3840
|
+
elif explain_construction:
|
3841
|
+
return "McFarland 1973 construction"
|
3842
|
+
else:
|
3843
|
+
_, (q,s) = are_mcfarland_1973_parameters(v,k,l,True)
|
3844
|
+
G,D = mcfarland_1973_construction(q,s)
|
3845
|
+
|
3846
|
+
elif are_hyperplanes_in_projective_geometry_parameters(v,k,l):
|
3847
|
+
if existence:
|
3848
|
+
return True
|
3849
|
+
elif explain_construction:
|
3850
|
+
return "Singer difference set"
|
3851
|
+
else:
|
3852
|
+
_, (q,d) = are_hyperplanes_in_projective_geometry_parameters(v,k,l,True)
|
3853
|
+
G,D = singer_difference_set(q,d)
|
3854
|
+
|
3855
|
+
elif are_hadamard_difference_set_parameters(v,k,l) and k-2*l == 3:
|
3856
|
+
if existence:
|
3857
|
+
return True
|
3858
|
+
elif explain_construction:
|
3859
|
+
return "Turyn 1965 construction"
|
3860
|
+
else:
|
3861
|
+
G,D = turyn_1965_3x3xK(4)
|
3862
|
+
|
3863
|
+
elif are_hadamard_difference_set_parameters(v,k,l) and hadamard_difference_set_product_parameters(k-2*l):
|
3864
|
+
N1,N2 = hadamard_difference_set_product_parameters(k-2*l)
|
3865
|
+
if existence:
|
3866
|
+
return True
|
3867
|
+
elif explain_construction:
|
3868
|
+
return "Hadamard difference set product from N1={} and N2={}".format(N1,N2)
|
3869
|
+
else:
|
3870
|
+
v1 = 4*N1*N1
|
3871
|
+
v2 = 4*N2*N2
|
3872
|
+
k1 = 2*N1*N1 - N1
|
3873
|
+
k2 = 2*N2*N2 - N2
|
3874
|
+
l1 = N1*N1 - N1
|
3875
|
+
l2 = N2*N2 - N2
|
3876
|
+
G1, D1 = difference_family(v1,k1,l1)
|
3877
|
+
G2, D2 = difference_family(v2,k2,l2)
|
3878
|
+
G, D = hadamard_difference_set_product(G1,D1,G2,D2)
|
3879
|
+
|
3880
|
+
elif are_hadamard_difference_set_parameters(v,k,l) and (k-2*l).is_prime():
|
3881
|
+
if existence:
|
3882
|
+
return False
|
3883
|
+
else:
|
3884
|
+
raise EmptySetError("by McFarland 1989 such difference family does not exist")
|
3885
|
+
|
3886
|
+
elif len(factorization) == 1 and radical_difference_family(K, k, l, existence=True) is True:
|
3887
|
+
if existence:
|
3888
|
+
return True
|
3889
|
+
elif explain_construction:
|
3890
|
+
return "Radical difference family on a finite field"
|
3891
|
+
else:
|
3892
|
+
D = radical_difference_family(K,k,l)
|
3893
|
+
G = K
|
3894
|
+
|
3895
|
+
elif (len(factorization) == 1
|
3896
|
+
and l == 1
|
3897
|
+
and k == 6
|
3898
|
+
and df_q_6_1(K, existence=True) is True):
|
3899
|
+
if existence:
|
3900
|
+
return True
|
3901
|
+
elif explain_construction:
|
3902
|
+
return "Wilson 1972 difference family made from the union of two cyclotomic cosets"
|
3903
|
+
else:
|
3904
|
+
D = df_q_6_1(K)
|
3905
|
+
G = K
|
3906
|
+
|
3907
|
+
elif (k == (v-1)//2 and
|
3908
|
+
l == (k-1)//2 and
|
3909
|
+
len(factorization) == 2 and
|
3910
|
+
abs(pow(*factorization[0]) - pow(*factorization[1])) == 2):
|
3911
|
+
# Twin prime powers construction
|
3912
|
+
# i.e. v = p(p+2) where p and p+2 are prime powers
|
3913
|
+
# k = (v-1)/2
|
3914
|
+
# lambda = (k-1)/2 (ie 2l+1 = k)
|
3915
|
+
if existence:
|
3916
|
+
return True
|
3917
|
+
elif explain_construction:
|
3918
|
+
return "Twin prime powers difference family"
|
3919
|
+
else:
|
3920
|
+
p = pow(*factorization[0])
|
3921
|
+
q = pow(*factorization[1])
|
3922
|
+
if p > q:
|
3923
|
+
p,q = q,p
|
3924
|
+
G,D = twin_prime_powers_difference_set(p,check=False)
|
3925
|
+
|
3926
|
+
elif (v-1)//2 == k and (v-1)//2-1 == l and complementary_difference_sets(v, existence=True):
|
3927
|
+
if existence:
|
3928
|
+
return True
|
3929
|
+
elif explain_construction:
|
3930
|
+
return "Complementary difference sets"
|
3931
|
+
else:
|
3932
|
+
G, A, B = complementary_difference_sets(v)
|
3933
|
+
D = [A, B]
|
3934
|
+
|
3935
|
+
else:
|
3936
|
+
if existence:
|
3937
|
+
return Unknown
|
3938
|
+
raise NotImplementedError("No constructions for these parameters")
|
3939
|
+
|
3940
|
+
if check and not is_difference_family(G,D,v=v,k=k,l=l,verbose=False):
|
3941
|
+
raise RuntimeError("There is a problem. Sage built the following "
|
3942
|
+
"difference family on G='{}' with parameters ({},{},{}):\n "
|
3943
|
+
"{}\nwhich seems to not be a difference family... "
|
3944
|
+
"Please contact sage-devel@googlegroups.com".format(G,v,k,l,D))
|
3945
|
+
|
3946
|
+
return G, D
|
3947
|
+
|
3948
|
+
|
3949
|
+
from sage.misc.rest_index_of_methods import gen_rest_table_index
|
3950
|
+
import sys
|
3951
|
+
__doc__ = __doc__.format(INDEX_OF_FUNCTIONS=gen_rest_table_index(sys.modules[__name__]))
|