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,993 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-graphs
|
2
|
+
r"""
|
3
|
+
Cython functions for combinatorial designs
|
4
|
+
|
5
|
+
This module implements the design methods that need to be somewhat efficient.
|
6
|
+
|
7
|
+
Functions
|
8
|
+
---------
|
9
|
+
"""
|
10
|
+
|
11
|
+
from sage.data_structures.bitset_base cimport *
|
12
|
+
|
13
|
+
from libc.string cimport memset
|
14
|
+
|
15
|
+
from cysignals.memory cimport sig_malloc, sig_realloc, sig_free
|
16
|
+
from memory_allocator cimport MemoryAllocator
|
17
|
+
|
18
|
+
from sage.misc.unknown import Unknown
|
19
|
+
|
20
|
+
|
21
|
+
def is_covering_array(array, strength=None, levels=None, verbose=False, parameters=False):
|
22
|
+
r"""
|
23
|
+
Check if the input is a covering array with given strength.
|
24
|
+
|
25
|
+
See :mod:`sage.combinat.designs.covering_array` for a definition.
|
26
|
+
|
27
|
+
INPUT:
|
28
|
+
|
29
|
+
- ``array`` -- the Covering Array to be tested
|
30
|
+
|
31
|
+
- ``strength`` -- integer; the parameter `t` of the covering array,
|
32
|
+
such that in any selection of `t` columns of the array, every `t`
|
33
|
+
-tuple appears at least once. If set to None then all t > 0 are
|
34
|
+
tested to and the maximal strength is used.
|
35
|
+
|
36
|
+
- ``levels`` -- the number of symbols that appear in ``array``
|
37
|
+
If set to None, then each unique entry in ``array`` is counted
|
38
|
+
|
39
|
+
- ``verbose`` -- boolean; whether to display some information about
|
40
|
+
the covering array
|
41
|
+
|
42
|
+
- ``parameters`` -- boolean; whether to return the parameters of
|
43
|
+
the Covering Array. If set to ``True``, the function returns a
|
44
|
+
pair ``(boolean_answer,(N,t,k,v))``.
|
45
|
+
|
46
|
+
EXAMPLES::
|
47
|
+
|
48
|
+
sage: from sage.combinat.designs.designs_pyx import is_covering_array
|
49
|
+
sage: C = [[1, 1, 1, 0],
|
50
|
+
....: [1, 1, 0, 0],
|
51
|
+
....: [0, 0, 0]]
|
52
|
+
sage: is_covering_array(C)
|
53
|
+
Traceback (most recent call last):
|
54
|
+
...
|
55
|
+
ValueError: Not all rows are the same length, row 2 is not the same length as row 0
|
56
|
+
|
57
|
+
sage: C = [[0, 1, 1],
|
58
|
+
....: [1, 1, 0],
|
59
|
+
....: [1, 0, 1],
|
60
|
+
....: [0, 0, 0,]]
|
61
|
+
sage: is_covering_array(C,strength=4)
|
62
|
+
Traceback (most recent call last):
|
63
|
+
...
|
64
|
+
ValueError: Strength must be equal or less than number of columns
|
65
|
+
|
66
|
+
sage: C = [[0, 1, 1],
|
67
|
+
....: [1, 1, 1],
|
68
|
+
....: [1, 0, 1]]
|
69
|
+
sage: is_covering_array(C,verbose=True)
|
70
|
+
A 3 by 3 Covering Array with strength 0 with entries from a symbol set of size 2
|
71
|
+
True
|
72
|
+
|
73
|
+
sage: C = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
74
|
+
....: [0, 0, 0, 0, 1, 1, 1, 1, 1, 1],
|
75
|
+
....: [0, 1, 1, 1, 0, 0, 0, 1, 1, 1],
|
76
|
+
....: [1, 0, 1, 1, 0, 1, 1, 0, 0, 1],
|
77
|
+
....: [1, 1, 0, 1, 1, 0, 1, 0, 1, 0],
|
78
|
+
....: [1, 1, 1, 0, 1, 1, 0, 1, 2, 0]]
|
79
|
+
sage: is_covering_array(C,levels=2)
|
80
|
+
Traceback (most recent call last):
|
81
|
+
...
|
82
|
+
ValueError: Array should contain integer symbols from 0 to 1
|
83
|
+
|
84
|
+
sage: C = [[1, 0, 0, 2, 0, 2, 1, 2, 2, 1, 0, 2, 2],
|
85
|
+
....: [1, 1, 0, 0, 2, 0, 2, 1, 2, 2, 1, 0, 2],
|
86
|
+
....: [1, 1, 1, 0, 0, 2, 0, 2, 1, 2, 2, 1, 0],
|
87
|
+
....: [0, 1, 1, 1, 0, 0, 2, 0, 2, 1, 2, 2, 1],
|
88
|
+
....: [2, 0, 1, 1, 1, 0, 0, 2, 0, 2, 1, 2, 2],
|
89
|
+
....: [1, 2, 0, 1, 1, 1, 0, 0, 2, 0, 2, 1, 2],
|
90
|
+
....: [1, 1, 2, 0, 1, 1, 1, 0, 0, 2, 0, 2, 1],
|
91
|
+
....: [2, 1, 1, 2, 0, 1, 1, 1, 0, 0, 2, 0, 2],
|
92
|
+
....: [1, 2, 1, 1, 2, 0, 1, 1, 1, 0, 0, 2, 0],
|
93
|
+
....: [0, 1, 2, 1, 1, 2, 0, 1, 1, 1, 0, 0, 2],
|
94
|
+
....: [1, 0, 1, 2, 1, 1, 2, 0, 1, 1, 1, 0, 0],
|
95
|
+
....: [0, 1, 0, 1, 2, 1, 1, 2, 0, 1, 1, 1, 0],
|
96
|
+
....: [0, 0, 1, 0, 1, 2, 1, 1, 2, 0, 1, 1, 1],
|
97
|
+
....: [2, 0, 0, 1, 0, 1, 2, 1, 1, 2, 0, 1, 1],
|
98
|
+
....: [2, 2, 0, 0, 1, 0, 1, 2, 1, 1, 2, 0, 1],
|
99
|
+
....: [2, 2, 2, 0, 0, 1, 0, 1, 2, 1, 1, 2, 0],
|
100
|
+
....: [0, 2, 2, 2, 0, 0, 1, 0, 1, 2, 1, 1, 2],
|
101
|
+
....: [1, 0, 2, 2, 2, 0, 0, 1, 0, 1, 2, 1, 1],
|
102
|
+
....: [2, 1, 0, 2, 2, 2, 0, 0, 1, 0, 1, 2, 1],
|
103
|
+
....: [2, 2, 1, 0, 2, 2, 2, 0, 0, 1, 0, 1, 2],
|
104
|
+
....: [1, 2, 2, 1, 0, 2, 2, 2, 0, 0, 1, 0, 1],
|
105
|
+
....: [2, 1, 2, 2, 1, 0, 2, 2, 2, 0, 0, 1, 0],
|
106
|
+
....: [0, 2, 1, 2, 2, 1, 0, 2, 2, 2, 0, 0, 1],
|
107
|
+
....: [2, 0, 2, 1, 2, 2, 1, 0, 2, 2, 2, 0, 0],
|
108
|
+
....: [0, 2, 0, 2, 1, 2, 2, 1, 0, 2, 2, 2, 0],
|
109
|
+
....: [0, 0, 2, 0, 2, 1, 2, 2, 1, 0, 2, 2, 2],
|
110
|
+
....: [1, 1, 0, 2, 1, 1, 2, 1, 0, 1, 0, 0, 2],
|
111
|
+
....: [1, 1, 1, 0, 2, 1, 1, 2, 1, 0, 1, 0, 0],
|
112
|
+
....: [0, 1, 1, 1, 0, 2, 1, 1, 2, 1, 0, 1, 0],
|
113
|
+
....: [0, 0, 1, 1, 1, 0, 2, 1, 1, 2, 1, 0, 1],
|
114
|
+
....: [2, 0, 0, 1, 1, 1, 0, 2, 1, 1, 2, 1, 0],
|
115
|
+
....: [0, 2, 0, 0, 1, 1, 1, 0, 2, 1, 1, 2, 1],
|
116
|
+
....: [2, 0, 2, 0, 0, 1, 1, 1, 0, 2, 1, 1, 2],
|
117
|
+
....: [1, 2, 0, 2, 0, 0, 1, 1, 1, 0, 2, 1, 1],
|
118
|
+
....: [2, 1, 2, 0, 2, 0, 0, 1, 1, 1, 0, 2, 1],
|
119
|
+
....: [2, 2, 1, 2, 0, 2, 0, 0, 1, 1, 1, 0, 2],
|
120
|
+
....: [1, 2, 2, 1, 2, 0, 2, 0, 0, 1, 1, 1, 0],
|
121
|
+
....: [0, 1, 2, 2, 1, 2, 0, 2, 0, 0, 1, 1, 1],
|
122
|
+
....: [2, 0, 1, 2, 2, 1, 2, 0, 2, 0, 0, 1, 1],
|
123
|
+
....: [2, 2, 0, 1, 2, 2, 1, 2, 0, 2, 0, 0, 1],
|
124
|
+
....: [2, 2, 2, 0, 1, 2, 2, 1, 2, 0, 2, 0, 0],
|
125
|
+
....: [0, 2, 2, 2, 0, 1, 2, 2, 1, 2, 0, 2, 0],
|
126
|
+
....: [0, 0, 2, 2, 2, 0, 1, 2, 2, 1, 2, 0, 2],
|
127
|
+
....: [1, 0, 0, 2, 2, 2, 0, 1, 2, 2, 1, 2, 0],
|
128
|
+
....: [0, 1, 0, 0, 2, 2, 2, 0, 1, 2, 2, 1, 2],
|
129
|
+
....: [1, 0, 1, 0, 0, 2, 2, 2, 0, 1, 2, 2, 1],
|
130
|
+
....: [2, 1, 0, 1, 0, 0, 2, 2, 2, 0, 1, 2, 2],
|
131
|
+
....: [1, 2, 1, 0, 1, 0, 0, 2, 2, 2, 0, 1, 2],
|
132
|
+
....: [1, 1, 2, 1, 0, 1, 0, 0, 2, 2, 2, 0, 1],
|
133
|
+
....: [2, 1, 1, 2, 1, 0, 1, 0, 0, 2, 2, 2, 0],
|
134
|
+
....: [0, 2, 1, 1, 2, 1, 0, 1, 0, 0, 2, 2, 2],
|
135
|
+
....: [1, 0, 2, 1, 1, 2, 1, 0, 1, 0, 0, 2, 2],
|
136
|
+
....: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
|
137
|
+
sage: is_covering_array(C,parameters=True)
|
138
|
+
(True, (53, 3, 13, 3))
|
139
|
+
|
140
|
+
sage: C = [[1, 0, 1, 1, 2, 0, 2, 2],
|
141
|
+
....: [2, 1, 0, 1, 1, 2, 0, 2],
|
142
|
+
....: [2, 2, 1, 0, 1, 1, 2, 0],
|
143
|
+
....: [0, 2, 2, 1, 0, 1, 1, 2],
|
144
|
+
....: [2, 0, 2, 2, 1, 0, 1, 1],
|
145
|
+
....: [1, 2, 0, 2, 2, 1, 0, 1],
|
146
|
+
....: [1, 1, 2, 0, 2, 2, 1, 0],
|
147
|
+
....: [0, 1, 1, 2, 0, 2, 2, 1]]
|
148
|
+
sage: is_covering_array(C,strength=2,parameters=True)
|
149
|
+
(False, (8, 0, 8, 3))
|
150
|
+
"""
|
151
|
+
from itertools import product, combinations
|
152
|
+
|
153
|
+
if levels is None:
|
154
|
+
symbol_list = list({x for l in array for x in l})
|
155
|
+
levels = len(symbol_list)
|
156
|
+
else:
|
157
|
+
symbol_list = range(levels)
|
158
|
+
|
159
|
+
number_rows = len(array)
|
160
|
+
number_columns = len(array[0])
|
161
|
+
|
162
|
+
for row in array:
|
163
|
+
if len(row) != number_columns:
|
164
|
+
raise ValueError("Not all rows are the same length, row {} is not the same length as row 0".format(array.index(row)))
|
165
|
+
else:
|
166
|
+
for entry in row:
|
167
|
+
if int(entry) != entry or entry < -1 or entry >= levels:
|
168
|
+
raise ValueError("Array should contain integer symbols from 0 to {}".format(levels-1))
|
169
|
+
|
170
|
+
result = True
|
171
|
+
|
172
|
+
# If strength t is inputted, check that for every selection of t
|
173
|
+
# columns, each v^t t-tuple is found in some row.
|
174
|
+
if strength:
|
175
|
+
if strength > number_columns:
|
176
|
+
raise ValueError("Strength must be equal or less than number of columns")
|
177
|
+
wstrength = strength
|
178
|
+
for comb in combinations(range(number_columns), wstrength):
|
179
|
+
existing_ttuples = set(tuple([row[ti] for ti in comb]) for row in array)
|
180
|
+
if len(existing_ttuples) != levels ** wstrength:
|
181
|
+
wstrength = 0
|
182
|
+
result = False
|
183
|
+
break
|
184
|
+
|
185
|
+
# If no strength t is inputted, starting at t=1 check all t until
|
186
|
+
# one of the v^t t-tuples does not appear.
|
187
|
+
else:
|
188
|
+
wstrength = 1
|
189
|
+
finished = False
|
190
|
+
do_iterate = True
|
191
|
+
while finished is False:
|
192
|
+
for comb in combinations(range(number_columns), wstrength):
|
193
|
+
tuple_dictionary = {item: 0 for item in product(symbol_list, repeat=wstrength)}
|
194
|
+
for row in array:
|
195
|
+
tuple_dictionary[tuple([row[ti] for ti in comb])] += 1
|
196
|
+
if 0 in tuple_dictionary.values():
|
197
|
+
wstrength -= 1
|
198
|
+
finished = True
|
199
|
+
break
|
200
|
+
elif do_iterate and any(value < levels for value in tuple_dictionary.values()):
|
201
|
+
do_iterate = False
|
202
|
+
finished = True
|
203
|
+
if finished is False and wstrength < number_columns and do_iterate:
|
204
|
+
wstrength += 1
|
205
|
+
|
206
|
+
if verbose:
|
207
|
+
print('A {} by {} Covering Array with strength {} with entries from a symbol set of size {}'.format(number_rows, number_columns, wstrength, levels))
|
208
|
+
|
209
|
+
if parameters:
|
210
|
+
return (result, (number_rows, wstrength, number_columns, levels))
|
211
|
+
else:
|
212
|
+
return result
|
213
|
+
|
214
|
+
|
215
|
+
def is_orthogonal_array(OA, int k, int n, int t=2, verbose=False, terminology='OA'):
|
216
|
+
r"""
|
217
|
+
Check that the integer matrix `OA` is an `OA(k,n,t)`.
|
218
|
+
|
219
|
+
See :func:`~sage.combinat.designs.orthogonal_arrays.orthogonal_array`
|
220
|
+
for a definition.
|
221
|
+
|
222
|
+
INPUT:
|
223
|
+
|
224
|
+
- ``OA`` -- the Orthogonal Array to be tested
|
225
|
+
|
226
|
+
- ``k``, ``n``, ``t`` -- integers; only implemented for `t=2`
|
227
|
+
|
228
|
+
- ``verbose`` -- boolean; whether to display some information when ``OA``
|
229
|
+
is not an orthogonal array `OA(k,n)`
|
230
|
+
|
231
|
+
- ``terminology`` -- string; how to phrase the information when ``verbose =
|
232
|
+
True``. Possible values are `"OA"`, `"MOLS"`
|
233
|
+
|
234
|
+
EXAMPLES::
|
235
|
+
|
236
|
+
sage: # needs sage.schemes
|
237
|
+
sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array
|
238
|
+
sage: OA = designs.orthogonal_arrays.build(8,9)
|
239
|
+
sage: is_orthogonal_array(OA,8,9)
|
240
|
+
True
|
241
|
+
sage: is_orthogonal_array(OA,8,10)
|
242
|
+
False
|
243
|
+
sage: OA[4][3] = 1
|
244
|
+
sage: is_orthogonal_array(OA,8,9)
|
245
|
+
False
|
246
|
+
sage: is_orthogonal_array(OA,8,9,verbose=True)
|
247
|
+
Columns 0 and 3 are not orthogonal
|
248
|
+
False
|
249
|
+
sage: is_orthogonal_array(OA,8,9, verbose=True, terminology='MOLS')
|
250
|
+
Squares 0 and 3 are not orthogonal
|
251
|
+
False
|
252
|
+
|
253
|
+
TESTS::
|
254
|
+
|
255
|
+
sage: # needs sage.schemes
|
256
|
+
sage: is_orthogonal_array(OA,8,9, t=3)
|
257
|
+
Traceback (most recent call last):
|
258
|
+
...
|
259
|
+
NotImplementedError: only implemented for t=2
|
260
|
+
sage: is_orthogonal_array([[3]*8],8,9, verbose=True)
|
261
|
+
The number of rows is 1 instead of 9^2=81
|
262
|
+
False
|
263
|
+
sage: is_orthogonal_array([[3]*8],8,9, verbose=True, terminology='MOLS')
|
264
|
+
All squares do not have dimension n^2=9^2
|
265
|
+
False
|
266
|
+
sage: is_orthogonal_array([[3]*7],8,9, verbose=True)
|
267
|
+
Some row does not have length 8
|
268
|
+
False
|
269
|
+
sage: is_orthogonal_array([[3]*7],8,9, verbose=True, terminology='MOLS')
|
270
|
+
The number of squares is not 6
|
271
|
+
False
|
272
|
+
|
273
|
+
Up to relabelling, there is a unique `OA(3,2)`. So their number is just the
|
274
|
+
cardinality of the relabeling group which is `S_2^3 \times S_3` and has
|
275
|
+
cardinality `48`::
|
276
|
+
|
277
|
+
sage: from itertools import product
|
278
|
+
sage: n = 0
|
279
|
+
sage: for a in product(product((0,1), repeat=3), repeat=4): # needs sage.schemes
|
280
|
+
....: if is_orthogonal_array(a,3,2):
|
281
|
+
....: n += 1
|
282
|
+
sage: n # needs sage.schemes
|
283
|
+
48
|
284
|
+
"""
|
285
|
+
cdef int n2 = n*n
|
286
|
+
cdef int x
|
287
|
+
|
288
|
+
if t != 2:
|
289
|
+
raise NotImplementedError("only implemented for t=2")
|
290
|
+
|
291
|
+
for R in OA:
|
292
|
+
if len(R) != k:
|
293
|
+
if verbose:
|
294
|
+
print({"OA": "Some row does not have length "+str(k),
|
295
|
+
"MOLS": "The number of squares is not "+str(k-2)}[terminology])
|
296
|
+
return False
|
297
|
+
|
298
|
+
if len(OA) != n2:
|
299
|
+
if verbose:
|
300
|
+
print({"OA": "The number of rows is {} instead of {}^2={}".format(len(OA),n,n2),
|
301
|
+
"MOLS": "All squares do not have dimension n^2={}^2".format(n)}[terminology])
|
302
|
+
return False
|
303
|
+
|
304
|
+
if n == 0:
|
305
|
+
return True
|
306
|
+
|
307
|
+
cdef int i,j,l
|
308
|
+
|
309
|
+
# A copy of OA
|
310
|
+
cdef MemoryAllocator mem = MemoryAllocator()
|
311
|
+
cdef unsigned short * OAc = <unsigned short *> mem.malloc(k*n2*sizeof(unsigned short))
|
312
|
+
|
313
|
+
cdef unsigned short * C1
|
314
|
+
cdef unsigned short * C2
|
315
|
+
|
316
|
+
# failed malloc ?
|
317
|
+
if OAc is NULL:
|
318
|
+
raise MemoryError
|
319
|
+
|
320
|
+
# Filling OAc
|
321
|
+
for i,R in enumerate(OA):
|
322
|
+
for j,x in enumerate(R):
|
323
|
+
if x < 0 or x >= n:
|
324
|
+
if verbose:
|
325
|
+
print({"OA": "{} is not in the interval [0..{}]".format(x,n-1),
|
326
|
+
"MOLS": "Entry {} was expected to be in the interval [0..{}]".format(x,n-1)}[terminology])
|
327
|
+
return False
|
328
|
+
OAc[j*n2+i] = x
|
329
|
+
|
330
|
+
# A bitset to keep track of pairs of values
|
331
|
+
cdef bitset_t seen
|
332
|
+
bitset_init(seen, n2)
|
333
|
+
|
334
|
+
for i in range(k): # For any column C1
|
335
|
+
C1 = OAc+i*n2
|
336
|
+
for j in range(i+1,k): # For any column C2 > C1
|
337
|
+
C2 = OAc+j*n2
|
338
|
+
bitset_set_first_n(seen, 0) # No pair has been seen yet
|
339
|
+
for l in range(n2):
|
340
|
+
bitset_add(seen,n*C1[l]+C2[l])
|
341
|
+
|
342
|
+
if bitset_len(seen) != n2: # Have we seen all pairs ?
|
343
|
+
bitset_free(seen)
|
344
|
+
if verbose:
|
345
|
+
print({"OA": "Columns {} and {} are not orthogonal".format(i,j),
|
346
|
+
"MOLS": "Squares {} and {} are not orthogonal".format(i,j)}[terminology])
|
347
|
+
return False
|
348
|
+
|
349
|
+
bitset_free(seen)
|
350
|
+
return True
|
351
|
+
|
352
|
+
|
353
|
+
def is_group_divisible_design(groups, blocks, v, G=None, K=None, lambd=1, verbose=False):
|
354
|
+
r"""
|
355
|
+
Check that input is a Group Divisible Design on `\{0, \ldots, v-1\}`.
|
356
|
+
|
357
|
+
For more information on Group Divisible Designs, see
|
358
|
+
:class:`~sage.combinat.designs.group_divisible_designs.GroupDivisibleDesign`.
|
359
|
+
|
360
|
+
INPUT:
|
361
|
+
|
362
|
+
- ``groups`` -- a partition of `X`. If set to ``None`` the groups are
|
363
|
+
guessed automatically, and the function returns ``(True, guessed_groups)``
|
364
|
+
instead of ``True``
|
365
|
+
|
366
|
+
- ``blocks`` -- collection of blocks
|
367
|
+
|
368
|
+
- ``v`` -- integers; size of the ground set assumed to be `X=\{0,...,v-1\}`
|
369
|
+
|
370
|
+
- ``G`` -- list of integers of which the sizes of the groups must be
|
371
|
+
elements. Set to ``None`` (automatic guess) by default
|
372
|
+
|
373
|
+
- ``K`` -- list of integers of which the sizes of the blocks must be
|
374
|
+
elements. Set to ``None`` (automatic guess) by default
|
375
|
+
|
376
|
+
- ``lambd`` -- value of `\lambda`. Set to `1` by default
|
377
|
+
|
378
|
+
- ``verbose`` -- boolean; whether to display some information when the
|
379
|
+
design is not a GDD
|
380
|
+
|
381
|
+
EXAMPLES::
|
382
|
+
|
383
|
+
sage: from sage.combinat.designs.designs_pyx import is_group_divisible_design
|
384
|
+
sage: TD = designs.transversal_design(4,10) # needs sage.modules
|
385
|
+
sage: groups = [list(range(i*10,(i+1)*10)) for i in range(4)]
|
386
|
+
sage: is_group_divisible_design(groups,TD,40,lambd=1) # needs sage.modules
|
387
|
+
True
|
388
|
+
|
389
|
+
TESTS::
|
390
|
+
|
391
|
+
sage: TD = designs.transversal_design(4,10) # needs sage.modules
|
392
|
+
sage: groups = [list(range(i*10,(i+1)*10)) for i in range(4)]
|
393
|
+
sage: is_group_divisible_design(groups, TD, 40, lambd=2, verbose=True) # needs sage.modules
|
394
|
+
the pair (0,10) has been seen 1 times but lambda=2
|
395
|
+
False
|
396
|
+
sage: is_group_divisible_design([[1,2],[3,4]],[[1,2]],40,lambd=1,verbose=True)
|
397
|
+
groups is not a partition of [0,...,39]
|
398
|
+
False
|
399
|
+
sage: is_group_divisible_design([list(range(40))],[[1,2]],40,lambd=1,verbose=True)
|
400
|
+
the pair (1,2) belongs to a group but appears in some block
|
401
|
+
False
|
402
|
+
sage: is_group_divisible_design([list(range(40))],[[2,2]],40,lambd=1,verbose=True)
|
403
|
+
The following block has repeated elements: [2, 2]
|
404
|
+
False
|
405
|
+
sage: is_group_divisible_design([list(range(40))],[["e",2]],40,lambd=1,verbose=True)
|
406
|
+
e does not belong to [0,...,39]
|
407
|
+
False
|
408
|
+
sage: is_group_divisible_design([list(range(40))],[list(range(40))],40,G=[5],lambd=1,verbose=True)
|
409
|
+
a group has size 40 while G=[5]
|
410
|
+
False
|
411
|
+
sage: is_group_divisible_design([list(range(40))],[["e",2]],40,K=[1],lambd=1,verbose=True)
|
412
|
+
a block has size 2 while K=[1]
|
413
|
+
False
|
414
|
+
|
415
|
+
sage: # needs sage.schemes
|
416
|
+
sage: p = designs.projective_plane(3)
|
417
|
+
sage: is_group_divisible_design(None, p.blocks(), 13)
|
418
|
+
(True, [[0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12]])
|
419
|
+
sage: is_group_divisible_design(None, p.blocks()*2, 13, verbose=True)
|
420
|
+
the pair (0,1) has been seen 2 times but lambda=1
|
421
|
+
False
|
422
|
+
sage: is_group_divisible_design(None, p.blocks()*2, 13, lambd=2)
|
423
|
+
(True, [[0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12]])
|
424
|
+
"""
|
425
|
+
cdef int n = v
|
426
|
+
cdef int i,ii,j,jj,s
|
427
|
+
cdef int l = lambd
|
428
|
+
cdef bint guess_groups = groups is None
|
429
|
+
|
430
|
+
if v < 0 or lambd < 0:
|
431
|
+
if verbose:
|
432
|
+
print("v={} and lambda={} must be nonnegative integers".format(v,l))
|
433
|
+
return False
|
434
|
+
|
435
|
+
# Block sizes are element of K
|
436
|
+
if K is not None:
|
437
|
+
K = set(K)
|
438
|
+
for b in blocks:
|
439
|
+
if not len(b) in K:
|
440
|
+
if verbose:
|
441
|
+
print("a block has size {} while K={}".format(len(b),list(K)))
|
442
|
+
return False
|
443
|
+
|
444
|
+
# Check that "groups" consists of disjoint sets whose union has length n
|
445
|
+
if (groups is not None and
|
446
|
+
(sum(len(g) for g in groups) != n or
|
447
|
+
len(set().union(*groups)) != n)):
|
448
|
+
if verbose:
|
449
|
+
print("groups is not a partition of [0,...,{}]".format(n-1))
|
450
|
+
return False
|
451
|
+
|
452
|
+
# Checks that the blocks are indeed sets and do not repeat elements
|
453
|
+
for b in blocks:
|
454
|
+
if len(b) != len(set(b)):
|
455
|
+
if verbose:
|
456
|
+
print("The following block has repeated elements: {}".format(b))
|
457
|
+
return False
|
458
|
+
|
459
|
+
# Check that the groups/blocks belong to [0,...,n-1]
|
460
|
+
from itertools import chain
|
461
|
+
for b in chain(groups if groups is not None else [],blocks):
|
462
|
+
for x in b:
|
463
|
+
try:
|
464
|
+
i = x
|
465
|
+
except TypeError:
|
466
|
+
i = -1
|
467
|
+
if i < 0 or i >= n:
|
468
|
+
if verbose:
|
469
|
+
print("{} does not belong to [0,...,{}]".format(x, n-1))
|
470
|
+
return False
|
471
|
+
|
472
|
+
cdef MemoryAllocator mem = MemoryAllocator()
|
473
|
+
cdef unsigned short * matrix = <unsigned short *> mem.calloc(n*n, sizeof(unsigned short))
|
474
|
+
|
475
|
+
# Counts the number of occurrences of each pair of points
|
476
|
+
for b in blocks:
|
477
|
+
s = len(b)
|
478
|
+
for i in range(s):
|
479
|
+
ii = b[i]
|
480
|
+
for j in range(i+1,s):
|
481
|
+
jj = b[j]
|
482
|
+
matrix[ii*n+jj] += 1
|
483
|
+
matrix[jj*n+ii] += 1
|
484
|
+
|
485
|
+
# Guess the groups (if necessary)
|
486
|
+
if groups is None:
|
487
|
+
from sage.sets.disjoint_set import DisjointSet_of_integers
|
488
|
+
groups = DisjointSet_of_integers(n)
|
489
|
+
for i in range(n):
|
490
|
+
for j in range(i + 1, n):
|
491
|
+
if matrix[i * n + j] == 0:
|
492
|
+
groups.union(i, j)
|
493
|
+
groups = list(groups.root_to_elements_dict().values())
|
494
|
+
|
495
|
+
# Group sizes are element of G
|
496
|
+
if G is not None:
|
497
|
+
G = set(G)
|
498
|
+
for g in groups:
|
499
|
+
if not len(g) in G:
|
500
|
+
if verbose:
|
501
|
+
print("a group has size {} while G={}".format(len(g),list(G)))
|
502
|
+
return False
|
503
|
+
|
504
|
+
# Checks that two points of the same group were never covered
|
505
|
+
for g in groups:
|
506
|
+
s = len(g)
|
507
|
+
for i in range(s):
|
508
|
+
ii = g[i]
|
509
|
+
for j in range(i+1,s):
|
510
|
+
jj = g[j]
|
511
|
+
if matrix[ii*n+jj] != 0:
|
512
|
+
if verbose:
|
513
|
+
print("the pair ({},{}) belongs to a group but appears in some block".format(ii, jj))
|
514
|
+
return False
|
515
|
+
|
516
|
+
# We fill the entries with what is expected by the next loop
|
517
|
+
matrix[ii*n+jj] = l
|
518
|
+
matrix[jj*n+ii] = l
|
519
|
+
|
520
|
+
# Checking that what should be equal to lambda IS equal to lambda
|
521
|
+
for i in range(n):
|
522
|
+
for j in range(i+1,n):
|
523
|
+
if matrix[i*n+j] != l:
|
524
|
+
if verbose:
|
525
|
+
print("the pair ({},{}) has been seen {} times but lambda={}".format(i,j,matrix[i*n+j],l))
|
526
|
+
return False
|
527
|
+
|
528
|
+
return True if not guess_groups else (True, groups)
|
529
|
+
|
530
|
+
|
531
|
+
def is_pairwise_balanced_design(blocks, v, K=None, lambd=1, verbose=False):
|
532
|
+
r"""
|
533
|
+
Check that input is a Pairwise Balanced Design (PBD) on `\{0, \ldots, v-1\}`.
|
534
|
+
|
535
|
+
For more information on Pairwise Balanced Designs (PBD), see
|
536
|
+
:class:`~sage.combinat.designs.bibd.PairwiseBalancedDesign`.
|
537
|
+
|
538
|
+
INPUT:
|
539
|
+
|
540
|
+
- ``blocks`` -- collection of blocks
|
541
|
+
|
542
|
+
- ``v`` -- integers; size of the ground set assumed to be `X=\{0,...,v-1\}`
|
543
|
+
|
544
|
+
- ``K`` -- list of integers of which the sizes of the blocks must be
|
545
|
+
elements; set to ``None`` (automatic guess) by default
|
546
|
+
|
547
|
+
- ``lambd`` -- value of `\lambda` (default: `1`)
|
548
|
+
|
549
|
+
- ``verbose`` -- boolean; whether to display some information when the
|
550
|
+
design is not a PBD
|
551
|
+
|
552
|
+
EXAMPLES::
|
553
|
+
|
554
|
+
sage: from sage.combinat.designs.designs_pyx import is_pairwise_balanced_design
|
555
|
+
sage: sts = designs.steiner_triple_system(9)
|
556
|
+
sage: is_pairwise_balanced_design(sts,9,[3],1)
|
557
|
+
True
|
558
|
+
sage: TD = designs.transversal_design(4,10).blocks() # needs sage.modules
|
559
|
+
sage: groups = [list(range(i*10,(i+1)*10)) for i in range(4)]
|
560
|
+
sage: is_pairwise_balanced_design(TD + groups, 40, [4,10], 1, verbose=True) # needs sage.modules
|
561
|
+
True
|
562
|
+
|
563
|
+
TESTS::
|
564
|
+
|
565
|
+
sage: from sage.combinat.designs.designs_pyx import is_pairwise_balanced_design
|
566
|
+
sage: is_pairwise_balanced_design(TD + groups, 40, [4,10], 2, verbose=True) # needs sage.modules
|
567
|
+
the pair (0,1) has been seen 1 times but lambda=2
|
568
|
+
False
|
569
|
+
sage: is_pairwise_balanced_design(TD + groups, 40, [10], 1, verbose=True) # needs sage.modules
|
570
|
+
a block has size 4 while K=[10]
|
571
|
+
False
|
572
|
+
sage: is_pairwise_balanced_design([[2,2]], 40, [2], 1, verbose=True)
|
573
|
+
The following block has repeated elements: [2, 2]
|
574
|
+
False
|
575
|
+
sage: is_pairwise_balanced_design([["e",2]], 40, [2], 1, verbose=True)
|
576
|
+
e does not belong to [0,...,39]
|
577
|
+
False
|
578
|
+
"""
|
579
|
+
return is_group_divisible_design([[i] for i in range(v)],
|
580
|
+
blocks,
|
581
|
+
v,
|
582
|
+
K=K,
|
583
|
+
lambd=lambd,
|
584
|
+
verbose=verbose)
|
585
|
+
|
586
|
+
|
587
|
+
def is_projective_plane(blocks, verbose=False):
|
588
|
+
r"""
|
589
|
+
Test whether the blocks form a projective plane on `\{0,...,v-1\}`.
|
590
|
+
|
591
|
+
A *projective plane* is an incidence structure that has the following properties:
|
592
|
+
|
593
|
+
1. Given any two distinct points, there is exactly one line incident with both of them.
|
594
|
+
2. Given any two distinct lines, there is exactly one point incident with both of them.
|
595
|
+
3. There are four points such that no line is incident with more than two of them.
|
596
|
+
|
597
|
+
For more informations, see :wikipedia:`Projective_plane`.
|
598
|
+
|
599
|
+
:meth:`~IncidenceStructure.is_t_design` can also check if an incidence structure is a projective plane
|
600
|
+
with the parameters `v=k^2+k+1`, `t=2` and `l=1`.
|
601
|
+
|
602
|
+
INPUT:
|
603
|
+
|
604
|
+
- ``blocks`` -- collection of blocks
|
605
|
+
|
606
|
+
- ``verbose`` -- whether to print additional information
|
607
|
+
|
608
|
+
EXAMPLES::
|
609
|
+
|
610
|
+
sage: from sage.combinat.designs.designs_pyx import is_projective_plane
|
611
|
+
sage: p = designs.projective_plane(4) # needs sage.schemes
|
612
|
+
sage: b = p.blocks() # needs sage.schemes
|
613
|
+
sage: is_projective_plane(b, verbose=True) # needs sage.schemes
|
614
|
+
True
|
615
|
+
|
616
|
+
sage: # needs sage.schemes
|
617
|
+
sage: p = designs.projective_plane(2)
|
618
|
+
sage: b = p.blocks()
|
619
|
+
sage: is_projective_plane(b)
|
620
|
+
True
|
621
|
+
sage: b[0][2] = 5
|
622
|
+
sage: is_projective_plane(b, verbose=True)
|
623
|
+
the pair (0,5) has been seen 2 times but lambda=1
|
624
|
+
False
|
625
|
+
|
626
|
+
sage: is_projective_plane([[0,1,2],[1,2,4]], verbose=True)
|
627
|
+
the pair (0,3) has been seen 0 times but lambda=1
|
628
|
+
False
|
629
|
+
|
630
|
+
sage: is_projective_plane([[1]], verbose=True)
|
631
|
+
First block has less than 3 points.
|
632
|
+
False
|
633
|
+
|
634
|
+
sage: # needs sage.schemes
|
635
|
+
sage: p = designs.projective_plane(2)
|
636
|
+
sage: b = p.blocks()
|
637
|
+
sage: b[2].append(4)
|
638
|
+
sage: is_projective_plane(b, verbose=True)
|
639
|
+
a block has size 4 while K=[3]
|
640
|
+
False
|
641
|
+
"""
|
642
|
+
if not blocks:
|
643
|
+
if verbose:
|
644
|
+
print('There is no block.')
|
645
|
+
return False
|
646
|
+
k = len(blocks[0])-1
|
647
|
+
if k < 2:
|
648
|
+
if verbose:
|
649
|
+
print('First block has less than 3 points.')
|
650
|
+
return False
|
651
|
+
v = k**2 + k + 1
|
652
|
+
return is_group_divisible_design([[i] for i in range(v)],
|
653
|
+
blocks,
|
654
|
+
v,
|
655
|
+
K=[k+1],
|
656
|
+
lambd=1,
|
657
|
+
verbose=verbose)
|
658
|
+
|
659
|
+
|
660
|
+
def is_difference_matrix(M, G, k, lmbda=1, verbose=False):
|
661
|
+
r"""
|
662
|
+
Test if `M` is a `(G,k,\lambda)`-difference matrix.
|
663
|
+
|
664
|
+
A matrix `M` is a `(G,k,\lambda)`-difference matrix if its entries are
|
665
|
+
element of `G`, and if for any two rows `R,R'` of `M` and `x\in G` there
|
666
|
+
are exactly `\lambda` values `i` such that `R_i-R'_i=x`.
|
667
|
+
|
668
|
+
INPUT:
|
669
|
+
|
670
|
+
- ``M`` -- a matrix with entries from ``G``
|
671
|
+
|
672
|
+
- ``G`` -- a group
|
673
|
+
|
674
|
+
- ``k`` -- integer
|
675
|
+
|
676
|
+
- ``lmbda`` -- integer (default: `1`)
|
677
|
+
|
678
|
+
- ``verbose`` -- boolean; whether to print some information when the answer
|
679
|
+
is ``False``
|
680
|
+
|
681
|
+
EXAMPLES::
|
682
|
+
|
683
|
+
sage: from sage.combinat.designs.designs_pyx import is_difference_matrix
|
684
|
+
sage: q = 3**3
|
685
|
+
sage: F = GF(q,'x') # needs sage.rings.finite_rings
|
686
|
+
sage: M = [[x*y for y in F] for x in F] # needs sage.rings.finite_rings
|
687
|
+
sage: is_difference_matrix(M,F,q,verbose=1) # needs sage.rings.finite_rings
|
688
|
+
True
|
689
|
+
|
690
|
+
sage: B = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
691
|
+
....: [0, 1, 2, 3, 4, 2, 3, 4, 0, 1],
|
692
|
+
....: [0, 2, 4, 1, 3, 3, 0, 2, 4, 1]]
|
693
|
+
sage: G = GF(5)
|
694
|
+
sage: B = [[G(b) for b in R] for R in B]
|
695
|
+
sage: is_difference_matrix(list(zip(*B)),G,3,2)
|
696
|
+
True
|
697
|
+
|
698
|
+
Bad input::
|
699
|
+
|
700
|
+
sage: # needs sage.rings.finite_rings
|
701
|
+
sage: for R in M: R.append(None)
|
702
|
+
sage: is_difference_matrix(M,F,q,verbose=1)
|
703
|
+
The matrix has 28 columns but k=27
|
704
|
+
False
|
705
|
+
sage: for R in M: _=R.pop(-1)
|
706
|
+
sage: M.append([None]*3**3)
|
707
|
+
sage: is_difference_matrix(M,F,q,verbose=1)
|
708
|
+
The matrix has 28 rows instead of lambda(|G|-1+2u)+mu=1(27-1+2.0)+1=27
|
709
|
+
False
|
710
|
+
sage: _= M.pop(-1)
|
711
|
+
sage: for R in M: R[-1] = 0
|
712
|
+
sage: is_difference_matrix(M,F,q,verbose=1)
|
713
|
+
Columns 0 and 26 generate 0 exactly 27 times instead of the expected mu(=1)
|
714
|
+
False
|
715
|
+
sage: for R in M: R[-1] = 1
|
716
|
+
sage: M[-1][-1] = 0
|
717
|
+
sage: is_difference_matrix(M,F,q,verbose=1)
|
718
|
+
Columns 0 and 26 do not generate all elements of G exactly lambda(=1) times.
|
719
|
+
The element x appeared 0 times as a difference.
|
720
|
+
False
|
721
|
+
"""
|
722
|
+
return is_quasi_difference_matrix(M,G,k,lmbda=lmbda,mu=lmbda,u=0,verbose=verbose)
|
723
|
+
|
724
|
+
|
725
|
+
def is_quasi_difference_matrix(M, G, int k, int lmbda, int mu, int u, verbose=False):
|
726
|
+
r"""
|
727
|
+
Test if the matrix is a `(G,k;\lambda,\mu;u)`-quasi-difference matrix.
|
728
|
+
|
729
|
+
Let `G` be an abelian group of order `n`. A
|
730
|
+
`(n,k;\lambda,\mu;u)`-quasi-difference matrix (QDM) is a matrix `Q_{ij}`
|
731
|
+
with `\lambda(n-1+2u)+\mu` rows and `k` columns, with each entry either
|
732
|
+
equal to ``None`` (i.e. the 'missing entries') or to an element of `G`. Each
|
733
|
+
column contains exactly `\lambda u` empty entries, and each row contains at
|
734
|
+
most one ``None``. Furthermore, for each `1\leq i<j\leq k`, the multiset
|
735
|
+
|
736
|
+
.. MATH::
|
737
|
+
|
738
|
+
\{q_{li}-q_{lj}:1\leq l\leq \lambda (n-1+2u)+\mu, \text{ with } q_{li}\text{ and }q_{lj}\text{ not empty}\}
|
739
|
+
|
740
|
+
contains `\lambda` times every nonzero element of `G` and contains `\mu`
|
741
|
+
times `0`.
|
742
|
+
|
743
|
+
INPUT:
|
744
|
+
|
745
|
+
- ``M`` -- a matrix with entries from ``G`` (or equal to ``None`` for
|
746
|
+
missing entries)
|
747
|
+
|
748
|
+
- ``G`` -- a group
|
749
|
+
|
750
|
+
- ``k``, ``lmbda``, ``mu``, ``u`` -- integers
|
751
|
+
|
752
|
+
- ``verbose`` -- boolean; whether to print some information when the answer
|
753
|
+
is ``False``
|
754
|
+
|
755
|
+
EXAMPLES:
|
756
|
+
|
757
|
+
Differences matrices::
|
758
|
+
|
759
|
+
sage: from sage.combinat.designs.designs_pyx import is_quasi_difference_matrix
|
760
|
+
sage: q = 3**3
|
761
|
+
sage: F = GF(q,'x') # needs sage.rings.finite_rings
|
762
|
+
sage: M = [[x*y for y in F] for x in F] # needs sage.rings.finite_rings
|
763
|
+
sage: is_quasi_difference_matrix(M,F,q,1,1,0,verbose=1) # needs sage.rings.finite_rings
|
764
|
+
True
|
765
|
+
|
766
|
+
sage: B = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
767
|
+
....: [0, 1, 2, 3, 4, 2, 3, 4, 0, 1],
|
768
|
+
....: [0, 2, 4, 1, 3, 3, 0, 2, 4, 1]]
|
769
|
+
sage: G = GF(5)
|
770
|
+
sage: B = [[G(b) for b in R] for R in B]
|
771
|
+
sage: is_quasi_difference_matrix(list(zip(*B)),G,3,2,2,0)
|
772
|
+
True
|
773
|
+
|
774
|
+
A quasi-difference matrix from the database::
|
775
|
+
|
776
|
+
sage: from sage.combinat.designs.database import QDM
|
777
|
+
sage: G,M = QDM[38,1][37,1,1,1][1]()
|
778
|
+
sage: is_quasi_difference_matrix(M,G,k=6,lmbda=1,mu=1,u=1)
|
779
|
+
True
|
780
|
+
|
781
|
+
Bad input::
|
782
|
+
|
783
|
+
sage: is_quasi_difference_matrix(M,G,k=6,lmbda=1,mu=1,u=3,verbose=1)
|
784
|
+
The matrix has 39 rows instead of lambda(|G|-1+2u)+mu=1(37-1+2.3)+1=43
|
785
|
+
False
|
786
|
+
sage: is_quasi_difference_matrix(M,G,k=6,lmbda=1,mu=2,u=1,verbose=1)
|
787
|
+
The matrix has 39 rows instead of lambda(|G|-1+2u)+mu=1(37-1+2.1)+2=40
|
788
|
+
False
|
789
|
+
sage: M[3][1] = None
|
790
|
+
sage: is_quasi_difference_matrix(M,G,k=6,lmbda=1,mu=1,u=1,verbose=1)
|
791
|
+
Row 3 contains more than one empty entry
|
792
|
+
False
|
793
|
+
sage: M[3][1] = 1
|
794
|
+
sage: M[6][1] = None
|
795
|
+
sage: is_quasi_difference_matrix(M,G,k=6,lmbda=1,mu=1,u=1,verbose=1)
|
796
|
+
Column 1 contains 2 empty entries instead of the expected lambda.u=1.1=1
|
797
|
+
False
|
798
|
+
"""
|
799
|
+
from sage.combinat.designs.difference_family import group_law
|
800
|
+
|
801
|
+
assert k>=2
|
802
|
+
assert lmbda >=1
|
803
|
+
assert mu>=0
|
804
|
+
assert u>=0
|
805
|
+
|
806
|
+
cdef int n = G.cardinality()
|
807
|
+
cdef int M_nrows = len(M)
|
808
|
+
cdef int i,j,ii
|
809
|
+
cdef bint bit
|
810
|
+
|
811
|
+
# Height of the matrix
|
812
|
+
if lmbda*(n-1+2*u)+mu != M_nrows:
|
813
|
+
if verbose:
|
814
|
+
print("The matrix has {} rows instead of lambda(|G|-1+2u)+mu={}({}-1+2.{})+{}={}".format(M_nrows,lmbda,n,u,mu,lmbda*(n-1+2*u)+mu))
|
815
|
+
return False
|
816
|
+
|
817
|
+
# Width of the matrix
|
818
|
+
for R in M:
|
819
|
+
if len(R) != k:
|
820
|
+
if verbose:
|
821
|
+
print("The matrix has {} columns but k={}".format(len(R), k))
|
822
|
+
return False
|
823
|
+
|
824
|
+
# When |G|=0
|
825
|
+
if M_nrows == 0:
|
826
|
+
return True
|
827
|
+
|
828
|
+
# Map group element with integers
|
829
|
+
cdef list int_to_group = list(G)
|
830
|
+
cdef dict group_to_int = {v:i for i,v in enumerate(int_to_group)}
|
831
|
+
|
832
|
+
# Allocations
|
833
|
+
cdef MemoryAllocator mem = MemoryAllocator()
|
834
|
+
cdef int ** x_minus_y = <int **> mem.malloc((n+1)*sizeof(int *))
|
835
|
+
cdef int * x_minus_y_data = <int *> mem.malloc((n+1)*(n+1)*sizeof(int))
|
836
|
+
cdef int * M_c = <int *> mem.malloc(k*M_nrows*sizeof(int))
|
837
|
+
cdef int * G_seen = <int *> mem.malloc((n+1)*sizeof(int))
|
838
|
+
|
839
|
+
# The "x-y" table. If g_i, g_j \in G, then x_minus_y[i][j] is equal to
|
840
|
+
# group_to_int[g_i-g_j].
|
841
|
+
#
|
842
|
+
# In order to handle empty values represented by n, we have
|
843
|
+
# x_minus_y[?][n]=x_minus_y[n][?]=n
|
844
|
+
zero, op, inv = group_law(G)
|
845
|
+
x_minus_y[0] = x_minus_y_data
|
846
|
+
for i in range(1,n+1):
|
847
|
+
x_minus_y[i] = x_minus_y[i-1] + n+1
|
848
|
+
|
849
|
+
# Elements of G
|
850
|
+
for j,Gj in enumerate(int_to_group):
|
851
|
+
minus_Gj = inv(Gj)
|
852
|
+
assert op(Gj, minus_Gj) == zero
|
853
|
+
for i,Gi in enumerate(int_to_group):
|
854
|
+
x_minus_y[i][j] = group_to_int[op(Gi,minus_Gj)]
|
855
|
+
|
856
|
+
# Empty values
|
857
|
+
for i in range(n+1):
|
858
|
+
x_minus_y[n][i]=n
|
859
|
+
x_minus_y[i][n]=n
|
860
|
+
|
861
|
+
# A copy of the matrix
|
862
|
+
for i,R in enumerate(M):
|
863
|
+
for j,x in enumerate(R):
|
864
|
+
M_c[i*k+j] = group_to_int[G(x)] if x is not None else n
|
865
|
+
|
866
|
+
# Each row contains at most one empty entry
|
867
|
+
if u:
|
868
|
+
for i in range(M_nrows):
|
869
|
+
bit = False
|
870
|
+
for j in range(k):
|
871
|
+
if M_c[i*k+j] == n:
|
872
|
+
if bit:
|
873
|
+
if verbose:
|
874
|
+
print("Row {} contains more than one empty entry".format(i))
|
875
|
+
return False
|
876
|
+
bit = True
|
877
|
+
|
878
|
+
# Each column contains lmbda*u empty entries
|
879
|
+
for j in range(k):
|
880
|
+
ii = 0
|
881
|
+
for i in range(M_nrows):
|
882
|
+
if M_c[i*k+j] == n:
|
883
|
+
ii += 1
|
884
|
+
if ii!=lmbda*u:
|
885
|
+
if verbose:
|
886
|
+
print("Column {} contains {} empty entries instead of the expected "
|
887
|
+
"lambda.u={}.{}={}".format(j, ii, lmbda, u, lmbda*u))
|
888
|
+
return False
|
889
|
+
|
890
|
+
# We are now ready to test every pair of columns
|
891
|
+
for i in range(k):
|
892
|
+
for j in range(i+1,k):
|
893
|
+
memset(G_seen, 0, (n+1)*sizeof(int))
|
894
|
+
for ii in range(M_nrows):
|
895
|
+
G_seen[x_minus_y[M_c[ii*k+i]][M_c[ii*k+j]]] += 1
|
896
|
+
|
897
|
+
if G_seen[0] != mu: # Bad number of 0
|
898
|
+
if verbose:
|
899
|
+
print("Columns {} and {} generate 0 exactly {} times "
|
900
|
+
"instead of the expected mu(={})".format(i,j,G_seen[0],mu))
|
901
|
+
return False
|
902
|
+
|
903
|
+
for ii in range(1,n): # bad number of g_ii\in G
|
904
|
+
if G_seen[ii] != lmbda:
|
905
|
+
if verbose:
|
906
|
+
print("Columns {} and {} do not generate all elements of G "
|
907
|
+
"exactly lambda(={}) times. The element {} appeared {} "
|
908
|
+
"times as a difference.".format(i,j,lmbda,int_to_group[ii],G_seen[ii]))
|
909
|
+
return False
|
910
|
+
|
911
|
+
return True
|
912
|
+
|
913
|
+
|
914
|
+
# Cached information for OA constructions (see .pxd file for more info)
|
915
|
+
|
916
|
+
_OA_cache = <cache_entry *> sig_malloc(2*sizeof(cache_entry))
|
917
|
+
if (_OA_cache == NULL):
|
918
|
+
sig_free(_OA_cache)
|
919
|
+
raise MemoryError
|
920
|
+
_OA_cache[0].max_true = -1
|
921
|
+
_OA_cache[1].max_true = -1
|
922
|
+
_OA_cache_size = 2
|
923
|
+
|
924
|
+
cpdef _OA_cache_set(int k, int n, truth_value):
|
925
|
+
r"""
|
926
|
+
Set a value in the OA cache of existence results.
|
927
|
+
|
928
|
+
INPUT:
|
929
|
+
|
930
|
+
- ``k``, ``n`` -- integers
|
931
|
+
|
932
|
+
- ``truth_value`` -- one of ``True``, ``False``, ``Unknown``
|
933
|
+
"""
|
934
|
+
global _OA_cache, _OA_cache_size
|
935
|
+
cdef int i
|
936
|
+
if _OA_cache_size <= n:
|
937
|
+
new_cache_size = n+100
|
938
|
+
_OA_cache = <cache_entry *> sig_realloc(_OA_cache,new_cache_size*sizeof(cache_entry))
|
939
|
+
if _OA_cache == NULL:
|
940
|
+
sig_free(_OA_cache)
|
941
|
+
raise MemoryError
|
942
|
+
|
943
|
+
for i in range(_OA_cache_size,new_cache_size):
|
944
|
+
_OA_cache[i].max_true = 0
|
945
|
+
_OA_cache[i].min_unknown = -1
|
946
|
+
_OA_cache[i].max_unknown = 0
|
947
|
+
_OA_cache[i].min_false = -1
|
948
|
+
|
949
|
+
_OA_cache_size = new_cache_size
|
950
|
+
|
951
|
+
if truth_value is True:
|
952
|
+
_OA_cache[n].max_true = k if k>_OA_cache[n].max_true else _OA_cache[n].max_true
|
953
|
+
elif truth_value is Unknown:
|
954
|
+
_OA_cache[n].min_unknown = k if k<_OA_cache[n].min_unknown else _OA_cache[n].min_unknown
|
955
|
+
_OA_cache[n].max_unknown = k if k>_OA_cache[n].max_unknown else _OA_cache[n].max_unknown
|
956
|
+
else:
|
957
|
+
_OA_cache[n].min_false = k if k<_OA_cache[n].min_false else _OA_cache[n].min_false
|
958
|
+
|
959
|
+
cpdef _OA_cache_get(int k, int n):
|
960
|
+
r"""
|
961
|
+
Get a value from the OA cache of existence results.
|
962
|
+
|
963
|
+
INPUT:
|
964
|
+
|
965
|
+
- ``k``, ``n`` -- integers
|
966
|
+
"""
|
967
|
+
if n>=_OA_cache_size:
|
968
|
+
return None
|
969
|
+
if k <= _OA_cache[n].max_true:
|
970
|
+
return True
|
971
|
+
elif (k >= _OA_cache[n].min_unknown and k <= _OA_cache[n].max_unknown):
|
972
|
+
return Unknown
|
973
|
+
elif k >= _OA_cache[n].min_false:
|
974
|
+
return False
|
975
|
+
|
976
|
+
return None
|
977
|
+
|
978
|
+
cpdef _OA_cache_construction_available(int k, int n):
|
979
|
+
r"""
|
980
|
+
Test if a construction is implemented using the cache's information.
|
981
|
+
|
982
|
+
INPUT:
|
983
|
+
|
984
|
+
- ``k``, ``n`` -- integers
|
985
|
+
"""
|
986
|
+
if n>=_OA_cache_size:
|
987
|
+
return Unknown
|
988
|
+
if k <= _OA_cache[n].max_true:
|
989
|
+
return True
|
990
|
+
if k >= _OA_cache[n].min_unknown:
|
991
|
+
return False
|
992
|
+
else:
|
993
|
+
return Unknown
|