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,2846 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-graphs
|
2
|
+
r"""
|
3
|
+
Database of distance regular graphs
|
4
|
+
|
5
|
+
In this module we construct several distance regular graphs
|
6
|
+
and group them in a function that maps intersection arrays
|
7
|
+
to graphs.
|
8
|
+
|
9
|
+
For a survey on distance-regular graph see [BCN1989]_ or [VDKT2016]_.
|
10
|
+
|
11
|
+
EXAMPLES::
|
12
|
+
|
13
|
+
sage: # needs cliquer
|
14
|
+
sage: G = graphs.cocliques_HoffmannSingleton()
|
15
|
+
sage: G.is_distance_regular()
|
16
|
+
True
|
17
|
+
sage: H = graphs.distance_regular_graph([15, 14, 10, 3, 1, 5, 12, 15])
|
18
|
+
sage: H == G
|
19
|
+
True
|
20
|
+
sage: G = graphs.distance_regular_graph([27, 10, 1, 1, 10, 27])
|
21
|
+
sage: G.is_distance_regular(True)
|
22
|
+
([27, 10, 1, None], [None, 1, 10, 27])
|
23
|
+
|
24
|
+
AUTHORS:
|
25
|
+
|
26
|
+
- Ivo Maffei (2020-07-28): initial version
|
27
|
+
"""
|
28
|
+
|
29
|
+
# ****************************************************************************
|
30
|
+
# Copyright (C) 2020 Ivo Maffei <ivomaffei@gmail.com>
|
31
|
+
#
|
32
|
+
# This program is free software: you can redistribute it and/or modify
|
33
|
+
# it under the terms of the GNU General Public License as published by
|
34
|
+
# the Free Software Foundation, either version 2 of the License, or
|
35
|
+
# (at your option) any later version.
|
36
|
+
# https://www.gnu.org/licenses/
|
37
|
+
# ****************************************************************************
|
38
|
+
|
39
|
+
import itertools
|
40
|
+
|
41
|
+
from cysignals.signals cimport sig_check
|
42
|
+
|
43
|
+
from sage.graphs.graph import Graph
|
44
|
+
from sage.misc.lazy_import import LazyImport
|
45
|
+
from sage.graphs.generators.smallgraphs import (FosterGraph, BiggsSmithGraph,
|
46
|
+
CoxeterGraph, LivingstoneGraph,
|
47
|
+
WellsGraph, GossetGraph,
|
48
|
+
HoffmanSingletonGraph,
|
49
|
+
SimsGewirtzGraph,
|
50
|
+
HigmanSimsGraph)
|
51
|
+
from sage.graphs.generators.platonic_solids import DodecahedralGraph
|
52
|
+
from sage.graphs.strongly_regular_db import strongly_regular_graph
|
53
|
+
|
54
|
+
codes = LazyImport('sage.coding', 'codes_catalog', as_name='codes')
|
55
|
+
libgap = LazyImport('sage.libs.gap.libgap', 'libgap')
|
56
|
+
Matrix = LazyImport('sage.matrix.constructor', 'Matrix')
|
57
|
+
VectorSpace = LazyImport('sage.modules.free_module', 'VectorSpace')
|
58
|
+
vector = LazyImport('sage.modules.free_module_element', 'vector')
|
59
|
+
GF = LazyImport('sage.rings.finite_rings.finite_field_constructor', 'GF')
|
60
|
+
|
61
|
+
|
62
|
+
def cocliques_HoffmannSingleton():
|
63
|
+
r"""
|
64
|
+
Return the graph obtained from the cocliques of the Hoffmann-Singleton graph.
|
65
|
+
|
66
|
+
This is a distance-regular graph with intersection array
|
67
|
+
`[15, 14, 10, 3; 1, 5, 12, 15]`.
|
68
|
+
|
69
|
+
EXAMPLES::
|
70
|
+
|
71
|
+
sage: # needs cliquer
|
72
|
+
sage: G = graphs.cocliques_HoffmannSingleton()
|
73
|
+
sage: G.is_distance_regular(True)
|
74
|
+
([15, 14, 10, 3, None], [None, 1, 5, 12, 15])
|
75
|
+
|
76
|
+
REFERENCES:
|
77
|
+
|
78
|
+
The construction of this graph can be found in [BCN1989]_ p. 392.
|
79
|
+
"""
|
80
|
+
from sage.graphs.graph_generators import GraphGenerators
|
81
|
+
|
82
|
+
D = GraphGenerators.HoffmanSingletonGraph()
|
83
|
+
DC = D.complement()
|
84
|
+
|
85
|
+
cocliques = [frozenset(c) for c in DC.cliques_maximum()] # 100 of this
|
86
|
+
|
87
|
+
edges = []
|
88
|
+
for c1, c2 in itertools.combinations(cocliques, 2):
|
89
|
+
if len(c1.intersection(c2)) == 8:
|
90
|
+
edges.append((c1, c2))
|
91
|
+
|
92
|
+
G = Graph(edges, format='list_of_edges')
|
93
|
+
return G
|
94
|
+
|
95
|
+
|
96
|
+
def locally_GQ42_distance_transitive_graph():
|
97
|
+
r"""
|
98
|
+
Return the unique amply regular graph with `\mu = 6` which is locally
|
99
|
+
a generalised quadrangle.
|
100
|
+
|
101
|
+
This graph is distance-regular with intersection array
|
102
|
+
`[45, 32, 12, 1; 1, 6, 32, 45]`.
|
103
|
+
|
104
|
+
This graph is also distance-transitive.
|
105
|
+
|
106
|
+
EXAMPLES::
|
107
|
+
|
108
|
+
sage: G = graphs.locally_GQ42_distance_transitive_graph() # optional - internet gap_package_atlasrep
|
109
|
+
sage: G.is_distance_regular(True) # optional - internet gap_package_atlasrep
|
110
|
+
([45, 32, 12, 1, None], [None, 1, 6, 32, 45])
|
111
|
+
|
112
|
+
REFERENCES:
|
113
|
+
|
114
|
+
A description of this graph can be found in [BCN1989]_ p.399.
|
115
|
+
This construction is due to Dima Pasechnik.
|
116
|
+
"""
|
117
|
+
H = libgap.AtlasGroup("3^2.U4(3).D8", libgap.NrMovedPoints, 756)
|
118
|
+
Ns = H.NormalSubgroups()
|
119
|
+
for N in Ns:
|
120
|
+
if len(N.GeneratorsSmallest()) == 7: # there is only one
|
121
|
+
break
|
122
|
+
|
123
|
+
G = Graph(libgap.Orbit(N, [1, 9], libgap.OnSets), format='list_of_edges')
|
124
|
+
G.name("locally GQ(4,2) distance transitive graph")
|
125
|
+
return G
|
126
|
+
|
127
|
+
|
128
|
+
def ConwaySmith_for_3S7():
|
129
|
+
r"""
|
130
|
+
Return the Conway-Smith graph related to `3 Sym(7)`.
|
131
|
+
|
132
|
+
This is a distance-regular graph with intersection array
|
133
|
+
`[10, 6, 4, 1; 1, 2, 6, 10]`.
|
134
|
+
|
135
|
+
EXAMPLES::
|
136
|
+
|
137
|
+
sage: G = graphs.ConwaySmith_for_3S7() # needs sage.modules sage.rings.finite_rings sage.rings.number_field
|
138
|
+
sage: G.is_distance_regular(True) # needs sage.modules sage.rings.finite_rings sage.rings.number_field
|
139
|
+
([10, 6, 4, 1, None], [None, 1, 2, 6, 10])
|
140
|
+
|
141
|
+
REFERENCES:
|
142
|
+
|
143
|
+
A description and construction of this graph can be found in
|
144
|
+
[BCN1989]_ p. 399.
|
145
|
+
"""
|
146
|
+
from sage.rings.number_field.number_field import CyclotomicField
|
147
|
+
|
148
|
+
F = CyclotomicField(3)
|
149
|
+
w = F.gen()
|
150
|
+
|
151
|
+
V = VectorSpace(GF(4), 6)
|
152
|
+
z2 = GF(4)('z2') # GF(4) = {0, 1, z2, z2+1}
|
153
|
+
|
154
|
+
W = V.span([(0, 0, 1, 1, 1, 1), (0, 1, 0, 1, z2, z2 + 1), (1, 0, 0, 1, z2 + 1, z2)])
|
155
|
+
# we only need the 45 vectors with 2 zero entries
|
156
|
+
# we also embed everything into CC
|
157
|
+
|
158
|
+
K = []
|
159
|
+
for v in W:
|
160
|
+
# check zero entries
|
161
|
+
zeros = 0
|
162
|
+
for x in v:
|
163
|
+
if x.is_zero():
|
164
|
+
zeros += 1
|
165
|
+
|
166
|
+
if zeros == 2:
|
167
|
+
# send to F and in K
|
168
|
+
# z2 -> w
|
169
|
+
# z2+1 -> w^2
|
170
|
+
vv = [] # new vector
|
171
|
+
for x in v:
|
172
|
+
if x == z2:
|
173
|
+
vv.append(w)
|
174
|
+
elif x == z2 + 1:
|
175
|
+
vv.append(w**2)
|
176
|
+
else:
|
177
|
+
vv.append(int(x))
|
178
|
+
|
179
|
+
# now vv is the new vector in F
|
180
|
+
vv = vector(F, vv)
|
181
|
+
K.append(vv)
|
182
|
+
|
183
|
+
# we need to add other vectors
|
184
|
+
for i in range(6):
|
185
|
+
# create e_i
|
186
|
+
ei = [0, 0, 0, 0, 0, 0]
|
187
|
+
ei[i] = 1
|
188
|
+
ei = vector(F, ei)
|
189
|
+
|
190
|
+
K.append(2 * ei)
|
191
|
+
K.append(2 * w * ei)
|
192
|
+
K.append(2 * w**2 * ei)
|
193
|
+
# now K is all the 63 vertices
|
194
|
+
|
195
|
+
for v in K:
|
196
|
+
v.set_immutable()
|
197
|
+
|
198
|
+
def has_edge(u, v):
|
199
|
+
return sum(u[i].conjugate() * v[i] for i in range(6)) == 2
|
200
|
+
|
201
|
+
G = Graph()
|
202
|
+
for Ki, Kj in itertools.combinations(K, 2):
|
203
|
+
if has_edge(Ki, Kj):
|
204
|
+
G.add_edge((Ki, Kj))
|
205
|
+
|
206
|
+
G.name("Conway-Smith graph for 3S7")
|
207
|
+
return G
|
208
|
+
|
209
|
+
|
210
|
+
def graph_3O73():
|
211
|
+
r"""
|
212
|
+
Return the graph related to the group `3 O(7,3)`.
|
213
|
+
|
214
|
+
This graph is distance-regular with intersection array
|
215
|
+
`[117, 80, 24, 1; 1, 12, 80, 117]`.
|
216
|
+
|
217
|
+
The graph is also distance transitive with `3.O(7,3)` as automorphism
|
218
|
+
group
|
219
|
+
|
220
|
+
EXAMPLES::
|
221
|
+
|
222
|
+
sage: G = graphs.graph_3O73() # optional - internet gap_package_atlasrep
|
223
|
+
sage: G.is_distance_regular(True) # optional - internet gap_package_atlasrep
|
224
|
+
([117, 80, 24, 1, None], [None, 1, 12, 80, 117])
|
225
|
+
|
226
|
+
REFERENCES:
|
227
|
+
|
228
|
+
A description and construction of this graph can be found in
|
229
|
+
[BCN1989]_ p. 400.
|
230
|
+
"""
|
231
|
+
group = libgap.AtlasGroup("3.O7(3)", libgap.NrMovedPoints, 1134)
|
232
|
+
G = Graph(libgap.Orbit(group, [1, 3], libgap.OnSets), format='list_of_edges')
|
233
|
+
G.name("Distance transitive graph with automorphism group 3.O_7(3)")
|
234
|
+
return G
|
235
|
+
|
236
|
+
|
237
|
+
def FosterGraph3S6():
|
238
|
+
r"""
|
239
|
+
Return the Foster graph for `3.Sym(6)`.
|
240
|
+
|
241
|
+
This graph is distance-regular with intersection array
|
242
|
+
`[6, 4, 2, 1; 1, 1, 4, 6]`.
|
243
|
+
|
244
|
+
The graph is also distance transitive.
|
245
|
+
|
246
|
+
EXAMPLES::
|
247
|
+
|
248
|
+
sage: G = graphs.FosterGraph3S6() # needs sage.libs.gap
|
249
|
+
sage: G.is_distance_regular(True) # needs sage.libs.gap
|
250
|
+
([6, 4, 2, 1, None], [None, 1, 1, 4, 6])
|
251
|
+
|
252
|
+
REFERENCES:
|
253
|
+
|
254
|
+
A description and construction of this graph can be found in
|
255
|
+
[BCN1989]_ p. 397.
|
256
|
+
"""
|
257
|
+
a = libgap.eval(("(2,6)(3,5)(4,11)(7,17)(8,16)(9,14)(13,22)(15,25)"
|
258
|
+
"(18,29)(19,28)(20,21)(24,30)(26,35)(27,33)(31,39)"
|
259
|
+
"(34,38)(36,43)(37,40)(42,44)"))
|
260
|
+
b = libgap.eval(("(1,2,7,12,4)(3,8,18,20,10)(5,9,19,21,11)(6,13,17,26,15)"
|
261
|
+
"(14,23,28,31,24)(16,22,29,36,27)(25,32,35,42,34)"
|
262
|
+
"(30,37,39,44,38)(33,40,43,45,41)"))
|
263
|
+
|
264
|
+
group = libgap.Group(a, b)
|
265
|
+
|
266
|
+
G = Graph(group.Orbit([1, 7], libgap.OnSets), format='list_of_edges')
|
267
|
+
G.name("Foster graph for 3.Sym(6) graph")
|
268
|
+
return G
|
269
|
+
|
270
|
+
|
271
|
+
def J2Graph():
|
272
|
+
r"""
|
273
|
+
Return the distance-transitive graph with automorphism group `J_2`.
|
274
|
+
|
275
|
+
EXAMPLES::
|
276
|
+
|
277
|
+
sage: G = graphs.J2Graph() # optional - internet gap_package_atlasrep
|
278
|
+
sage: G.is_distance_regular(True) # optional - internet gap_package_atlasrep
|
279
|
+
([10, 8, 8, 2, None], [None, 1, 1, 4, 5])
|
280
|
+
|
281
|
+
REFERENCES:
|
282
|
+
|
283
|
+
A description and construction of this graph can be found in
|
284
|
+
[BCN1989]_ p. 408.
|
285
|
+
"""
|
286
|
+
group = libgap.AtlasGroup("J2", libgap.NrMovedPoints, 315)
|
287
|
+
G = Graph(group.Orbit([1, 9], libgap.OnSets), format='list_of_edges')
|
288
|
+
G.name("J_2 graph")
|
289
|
+
return G
|
290
|
+
|
291
|
+
|
292
|
+
def IvanovIvanovFaradjevGraph():
|
293
|
+
r"""
|
294
|
+
Return the IvanovIvanovFaradjev graph.
|
295
|
+
|
296
|
+
The graph is distance-transitive with automorphism group `3.M_{22}`.
|
297
|
+
|
298
|
+
EXAMPLES::
|
299
|
+
|
300
|
+
sage: G = graphs.IvanovIvanovFaradjevGraph() # optional - internet gap_package_atlasrep
|
301
|
+
sage: G.is_distance_regular(True) # optional - internet gap_package_atlasrep
|
302
|
+
([7, 6, 4, 4, 4, 1, 1, 1, None], [None, 1, 1, 1, 2, 4, 4, 6, 7])
|
303
|
+
|
304
|
+
REFERENCES:
|
305
|
+
|
306
|
+
A description and construction of this graph can be found in
|
307
|
+
[BCN1989]_ p. 369.
|
308
|
+
"""
|
309
|
+
|
310
|
+
group = libgap.AtlasGroup("3.M22", libgap.NrMovedPoints, 990)
|
311
|
+
graph = Graph(group.Orbit([1, 22], libgap.OnSets), format='list_of_edges')
|
312
|
+
|
313
|
+
graph.name("Ivanov-Ivanov-Faradjev Graph")
|
314
|
+
return graph
|
315
|
+
|
316
|
+
|
317
|
+
def LargeWittGraph():
|
318
|
+
r"""
|
319
|
+
Return the large Witt graph.
|
320
|
+
|
321
|
+
This is a distance-regular graph with intersection array
|
322
|
+
`[30,28,24;1,3,15]`.
|
323
|
+
|
324
|
+
EXAMPLES::
|
325
|
+
|
326
|
+
sage: g = graphs.LargeWittGraph() # needs sage.libs.pari sage.modules
|
327
|
+
sage: g.is_distance_regular(True) # needs sage.libs.pari sage.modules
|
328
|
+
([30, 28, 24, None], [None, 1, 3, 15])
|
329
|
+
|
330
|
+
REFERENCES:
|
331
|
+
|
332
|
+
A description of this graph can be found in
|
333
|
+
[BCN1989]_ p. 366.
|
334
|
+
This construction is taken from
|
335
|
+
http://mathworld.wolfram.com/LargeWittGraph.html
|
336
|
+
"""
|
337
|
+
import itertools
|
338
|
+
|
339
|
+
C = codes.GolayCode(GF(2), extended=True)
|
340
|
+
vertices = [c for c in C if c.hamming_weight() == 8]
|
341
|
+
|
342
|
+
edges = []
|
343
|
+
for v, w in itertools.combinations(vertices, 2):
|
344
|
+
if not set(v.support()).intersection(w.support()):
|
345
|
+
edges.append((v, w))
|
346
|
+
|
347
|
+
W = Graph(edges, format='list_of_edges')
|
348
|
+
W.name("Large Witt graph")
|
349
|
+
return W
|
350
|
+
|
351
|
+
|
352
|
+
def TruncatedWittGraph():
|
353
|
+
r"""
|
354
|
+
Return the truncated Witt graph.
|
355
|
+
|
356
|
+
This builds the large Witt graph, then removes
|
357
|
+
all vertices whose codeword start with a 1.
|
358
|
+
|
359
|
+
The graph is distance-regular with intersection array
|
360
|
+
`[15,14,12;1,1,9]`.
|
361
|
+
|
362
|
+
EXAMPLES::
|
363
|
+
|
364
|
+
sage: # long time, needs sage.libs.pari sage.modules
|
365
|
+
sage: G = graphs.TruncatedWittGraph()
|
366
|
+
sage: G.is_distance_regular(True)
|
367
|
+
([15, 14, 12, None], [None, 1, 1, 9])
|
368
|
+
|
369
|
+
REFERENCES:
|
370
|
+
|
371
|
+
A description and construction of this graph can be found in
|
372
|
+
[BCN1989]_ p. 367.
|
373
|
+
"""
|
374
|
+
# get large witt graph and remove all vertices which start with a 1
|
375
|
+
G = LargeWittGraph()
|
376
|
+
G.delete_vertices(filter(lambda x: x[0] == 1, G.vertices(sort=False)))
|
377
|
+
|
378
|
+
G.name("Truncated Witt graph")
|
379
|
+
return G
|
380
|
+
|
381
|
+
|
382
|
+
def DoublyTruncatedWittGraph():
|
383
|
+
r"""
|
384
|
+
Return the doubly truncated Witt graph.
|
385
|
+
|
386
|
+
This builds the truncated Witt graph, then removes
|
387
|
+
all vertices whose codeword start with a 1.
|
388
|
+
|
389
|
+
The graph is distance-regular with intersection array
|
390
|
+
`[7,6,4,4;1,1,1,6]`.
|
391
|
+
|
392
|
+
EXAMPLES::
|
393
|
+
|
394
|
+
sage: G = graphs.DoublyTruncatedWittGraph() # needs sage.libs.pari sage.modules
|
395
|
+
sage: G.is_distance_regular(True) # needs sage.libs.pari sage.modules
|
396
|
+
([7, 6, 4, 4, None], [None, 1, 1, 1, 6])
|
397
|
+
|
398
|
+
REFERENCES:
|
399
|
+
|
400
|
+
A description and construction of this graph can be found in
|
401
|
+
[BCN1989]_ p. 368.
|
402
|
+
"""
|
403
|
+
G = TruncatedWittGraph()
|
404
|
+
G.delete_vertices(filter(lambda x: x[1] == 1, G.vertices(sort=False)))
|
405
|
+
|
406
|
+
G.name("Doubly Truncated Witt graph")
|
407
|
+
return G
|
408
|
+
|
409
|
+
|
410
|
+
def distance_3_doubly_truncated_Golay_code_graph():
|
411
|
+
r"""
|
412
|
+
Return a distance-regular graph with intersection array
|
413
|
+
`[9, 8, 6, 3; 1, 1, 3, 8]`.
|
414
|
+
|
415
|
+
EXAMPLES::
|
416
|
+
|
417
|
+
sage: # long time, needs sage.modules sage.rings.finite_rings
|
418
|
+
sage: G = graphs.distance_3_doubly_truncated_Golay_code_graph()
|
419
|
+
sage: G.is_distance_regular(True) # long time (due to above)
|
420
|
+
([9, 8, 6, 3, None], [None, 1, 1, 3, 8])
|
421
|
+
|
422
|
+
ALGORITHM:
|
423
|
+
|
424
|
+
Compute the binary Golay code and truncate it twice. Compute its coset graph.
|
425
|
+
Take a vertex and compute the set of vertices at distance 3
|
426
|
+
from the vertex chosen. This set constitutes the set of vertices of our
|
427
|
+
distance-regular graph. Moreover we have an edge `(u,v)` if the coset graph
|
428
|
+
contains such edge.
|
429
|
+
|
430
|
+
REFERENCES:
|
431
|
+
|
432
|
+
Description and construction of this graph are taken from [BCN1989]_ p. 364.
|
433
|
+
"""
|
434
|
+
G = codes.GolayCode(GF(2), extended=False).punctured([0, 1]).cosetGraph()
|
435
|
+
v = G.vertices(sort=False)[0]
|
436
|
+
it = G.breadth_first_search(v, distance=3, report_distance=True)
|
437
|
+
vertices = [w for (w, d) in it if d == 3]
|
438
|
+
|
439
|
+
edges = [(a, b) for a, b in itertools.combinations(vertices, 2)
|
440
|
+
if G.has_edge((a, b))]
|
441
|
+
|
442
|
+
H = Graph(edges, format='list_of_edges')
|
443
|
+
return H
|
444
|
+
|
445
|
+
|
446
|
+
def shortened_00_11_binary_Golay_code_graph():
|
447
|
+
r"""
|
448
|
+
Return a distance-regular graph with intersection array
|
449
|
+
`[21, 20, 16, 6, 2, 1; 1, 2, 6, 16, 20, 21]`.
|
450
|
+
|
451
|
+
EXAMPLES::
|
452
|
+
|
453
|
+
sage: # long time, needs sage.modules sage.rings.finite_rings
|
454
|
+
sage: G = graphs.shortened_00_11_binary_Golay_code_graph() # 9 s
|
455
|
+
sage: G.is_distance_regular(True)
|
456
|
+
([21, 20, 16, 6, 2, 1, None], [None, 1, 2, 6, 16, 20, 21])
|
457
|
+
|
458
|
+
ALGORITHM:
|
459
|
+
|
460
|
+
Compute the binary Golay code. Compute the subcode whose codewords start
|
461
|
+
with 00 or 11. Remove the first two entries from all codewords of the newly
|
462
|
+
found linear code and compute its coset graph.
|
463
|
+
|
464
|
+
REFERENCES:
|
465
|
+
|
466
|
+
Description and construction of this graph can be found in [BCN1989]_ p. 365.
|
467
|
+
"""
|
468
|
+
from sage.coding.linear_code import LinearCode
|
469
|
+
|
470
|
+
code = codes.GolayCode(GF(2), False)
|
471
|
+
C_basis = code.basis()
|
472
|
+
|
473
|
+
# Now special shortening
|
474
|
+
v = C_basis[0] + C_basis[1] # v has 11 at the start
|
475
|
+
C_basis = C_basis[2:]
|
476
|
+
C_basis.append(v)
|
477
|
+
C_basis = list(map(lambda x: x[2:], C_basis))
|
478
|
+
|
479
|
+
code = LinearCode(Matrix(GF(2), C_basis))
|
480
|
+
|
481
|
+
G = code.cosetGraph()
|
482
|
+
G.name("Shortened 00 11 binary Golay code")
|
483
|
+
return G
|
484
|
+
|
485
|
+
|
486
|
+
def shortened_000_111_extended_binary_Golay_code_graph():
|
487
|
+
r"""
|
488
|
+
Return a distance-regular graph with intersection array
|
489
|
+
`[21, 20, 16, 9, 2, 1; 1, 2, 3, 16, 20, 21]`.
|
490
|
+
|
491
|
+
EXAMPLES::
|
492
|
+
|
493
|
+
sage: # long time, needs sage.modules sage.rings.finite_rings
|
494
|
+
sage: G = graphs.shortened_000_111_extended_binary_Golay_code_graph() # 25 s
|
495
|
+
sage: G.is_distance_regular(True)
|
496
|
+
([21, 20, 16, 9, 2, 1, None], [None, 1, 2, 3, 16, 20, 21])
|
497
|
+
|
498
|
+
ALGORITHM:
|
499
|
+
|
500
|
+
Compute the extended binary Golay code. Compute its subcode whose codewords
|
501
|
+
start with 000 or 111. Remove the first 3 entries from all the codewords
|
502
|
+
from the new linear code and compute its coset graph.
|
503
|
+
|
504
|
+
REFERENCES:
|
505
|
+
|
506
|
+
Description and construction of this graph can be found in [BCN1989]_ p. 365.
|
507
|
+
"""
|
508
|
+
from sage.coding.linear_code import LinearCode
|
509
|
+
|
510
|
+
code = codes.GolayCode(GF(2))
|
511
|
+
C_basis = code.basis()
|
512
|
+
|
513
|
+
# now special shortening
|
514
|
+
v = C_basis[0] + C_basis[1] + C_basis[2] # v has 111 at the start
|
515
|
+
C_basis = C_basis[3:]
|
516
|
+
C_basis.append(v)
|
517
|
+
C_basis = list(map(lambda x: x[3:], C_basis))
|
518
|
+
|
519
|
+
code = LinearCode(Matrix(GF(2), C_basis))
|
520
|
+
|
521
|
+
G = code.cosetGraph()
|
522
|
+
G.name("Shortened 000 111 extended binary Golay code")
|
523
|
+
return G
|
524
|
+
|
525
|
+
|
526
|
+
def vanLintSchrijverGraph():
|
527
|
+
r"""
|
528
|
+
Return the van Lint-Schrijver graph.
|
529
|
+
|
530
|
+
The graph is distance-regular with intersection array
|
531
|
+
`[6, 5, 5, 4; 1, 1, 2, 6]`.
|
532
|
+
|
533
|
+
EXAMPLES::
|
534
|
+
|
535
|
+
sage: G = graphs.vanLintSchrijverGraph() # needs sage.modules
|
536
|
+
sage: G.is_distance_regular(True) # needs sage.modules
|
537
|
+
([6, 5, 5, 4, None], [None, 1, 1, 2, 6])
|
538
|
+
|
539
|
+
REFERENCES:
|
540
|
+
|
541
|
+
For a description of this graph see [BCN1989]_ p. 373.
|
542
|
+
"""
|
543
|
+
from sage.coding.linear_code import LinearCode
|
544
|
+
|
545
|
+
one = vector(GF(3), [1, 1, 1, 1, 1, 1])
|
546
|
+
G = LinearCode(Matrix(GF(3), one)).cosetGraph()
|
547
|
+
|
548
|
+
vertices = [v for v in G.vertices(sort=False) if v.dot_product(one) in {1, 2}]
|
549
|
+
edges = [(v, w) for v, w in itertools.combinations(vertices, 2)
|
550
|
+
if G.has_edge((v, w))]
|
551
|
+
|
552
|
+
H = Graph(edges, format='list_of_edges')
|
553
|
+
H.name("Linst-Schrijver graph")
|
554
|
+
return H
|
555
|
+
|
556
|
+
|
557
|
+
def LeonardGraph():
|
558
|
+
r"""
|
559
|
+
Return the Leonard graph.
|
560
|
+
|
561
|
+
The graph is distance-regular with intersection array
|
562
|
+
`[12, 11, 10, 7; 1, 2, 5, 12]`.
|
563
|
+
|
564
|
+
EXAMPLES::
|
565
|
+
|
566
|
+
sage: G = graphs.LeonardGraph() # needs cliquer sage.combinat sage.modules
|
567
|
+
sage: G.is_distance_regular(True) # needs cliquer sage.combinat sage.modules
|
568
|
+
([12, 11, 10, 7, None], [None, 1, 2, 5, 12])
|
569
|
+
|
570
|
+
REFERENCES:
|
571
|
+
|
572
|
+
For a description of this graph see [BCN1989]_ p. 371.
|
573
|
+
"""
|
574
|
+
from sage.combinat.matrices.hadamard_matrix import hadamard_matrix
|
575
|
+
|
576
|
+
M = hadamard_matrix(12)
|
577
|
+
edges = []
|
578
|
+
for i, j, k, l in itertools.product(range(12), repeat=4):
|
579
|
+
if i == k or j == l:
|
580
|
+
continue
|
581
|
+
if M[i, j] * M[i, l] * M[k, j] * M[k, l] == -1:
|
582
|
+
edges.append(((i, j), (k, l)))
|
583
|
+
|
584
|
+
D = Graph(edges, format='list_of_edges')
|
585
|
+
blocks = [frozenset(cl) for cl in D.cliques_maximum()]
|
586
|
+
|
587
|
+
edges = [(p, b) for b in blocks for p in b]
|
588
|
+
G = Graph(edges, format='list_of_edges')
|
589
|
+
return G
|
590
|
+
|
591
|
+
|
592
|
+
def UstimenkoGraph(const int m, const int q):
|
593
|
+
r"""
|
594
|
+
Return the Ustimenko graph with parameters `(m, q)`.
|
595
|
+
|
596
|
+
This is the distance 1 or 2 graph of the dual polar graph `C_{m-1}(q)`.
|
597
|
+
The graph is distance-regular with parameters
|
598
|
+
`(d,q^2, \binom{3}{1}_q -1, \binom{m+1}{1}_q -1)`,
|
599
|
+
where `\binom{n}{k}_q` is the `q`-binomial coefficient.
|
600
|
+
|
601
|
+
INPUT:
|
602
|
+
|
603
|
+
- ``m``, ``q`` -- integers; `q` must be a prime power and `m > 1`
|
604
|
+
|
605
|
+
EXAMPLES::
|
606
|
+
|
607
|
+
sage: G = graphs.UstimenkoGraph(4, 2) # needs sage.libs.gap
|
608
|
+
sage: G.is_distance_regular(True) # needs sage.libs.gap
|
609
|
+
([70, 32, None], [None, 1, 35])
|
610
|
+
|
611
|
+
REFERENCES:
|
612
|
+
|
613
|
+
See [BCN1989]_ p. 279 or [VDKT2016]_ p. 22.
|
614
|
+
|
615
|
+
TESTS::
|
616
|
+
|
617
|
+
sage: # long time, needs sage.libs.gap
|
618
|
+
sage: G = graphs.UstimenkoGraph(5, 2)
|
619
|
+
sage: G.order()
|
620
|
+
2295
|
621
|
+
sage: G.is_distance_regular(True)
|
622
|
+
([310, 224, None], [None, 1, 35])
|
623
|
+
sage: G = graphs.UstimenkoGraph(4,3)
|
624
|
+
sage: G.is_distance_regular(True)
|
625
|
+
([390, 243, None], [None, 1, 130])
|
626
|
+
"""
|
627
|
+
from sage.graphs.graph_generators import graphs
|
628
|
+
|
629
|
+
G = graphs.SymplecticDualPolarGraph(2*m - 2, q)
|
630
|
+
|
631
|
+
edgesToAdd = []
|
632
|
+
for v in G:
|
633
|
+
for w in G.neighbor_iterator(v):
|
634
|
+
for u in G.neighbor_iterator(w):
|
635
|
+
sig_check()
|
636
|
+
if u != v and not G.has_edge(u, v):
|
637
|
+
# then u,v are at distance 2
|
638
|
+
edgesToAdd.append((u, v))
|
639
|
+
|
640
|
+
G.add_edges(edgesToAdd)
|
641
|
+
G.name(f"Ustimenko graph ({m}, {q})")
|
642
|
+
return G
|
643
|
+
|
644
|
+
|
645
|
+
def BilinearFormsGraph(const int d, const int e, const int q):
|
646
|
+
r"""
|
647
|
+
Return a bilinear forms graph with the given parameters.
|
648
|
+
|
649
|
+
This builds a graph whose vertices are all `d`x`e` matrices over
|
650
|
+
`GF(q)`. Two vertices are adjacent if the difference of the two
|
651
|
+
matrices has rank 1.
|
652
|
+
|
653
|
+
The graph is distance-regular with classical parameters
|
654
|
+
`(\min(d, e), q, q-1 , q^{\max(d, e)}-1)`.
|
655
|
+
|
656
|
+
INPUT:
|
657
|
+
|
658
|
+
- ``d``, ``e`` -- integers; dimension of the matrices
|
659
|
+
- ``q`` -- integer; a prime power
|
660
|
+
|
661
|
+
EXAMPLES::
|
662
|
+
|
663
|
+
sage: # needs sage.modules
|
664
|
+
sage: G = graphs.BilinearFormsGraph(3, 3, 2)
|
665
|
+
sage: G.is_distance_regular(True)
|
666
|
+
([49, 36, 16, None], [None, 1, 6, 28])
|
667
|
+
sage: G = graphs.BilinearFormsGraph(3,3,3) # not tested (20 s) # needs sage.rings.finite_rings
|
668
|
+
sage: G.order() # not tested (due to above) # needs sage.rings.finite_rings
|
669
|
+
19683
|
670
|
+
sage: G = graphs.BilinearFormsGraph(3, 4, 2) # long time # needs sage.rings.finite_rings
|
671
|
+
sage: G.is_distance_regular(True) # long time # needs sage.rings.finite_rings
|
672
|
+
([105, 84, 48, None], [None, 1, 6, 28])
|
673
|
+
|
674
|
+
REFERENCES:
|
675
|
+
|
676
|
+
See [BCN1989]_ pp. 280-282 for a rather detailed discussion, otherwise
|
677
|
+
see [VDKT2016]_ p. 21.
|
678
|
+
|
679
|
+
TESTS::
|
680
|
+
|
681
|
+
sage: # needs sage.modules
|
682
|
+
sage: G = graphs.BilinearFormsGraph(2,3,2)
|
683
|
+
sage: G.is_distance_regular(True)
|
684
|
+
([21, 12, None], [None, 1, 6])
|
685
|
+
sage: H = graphs.BilinearFormsGraph(3,2,2)
|
686
|
+
sage: H.is_isomorphic(G)
|
687
|
+
True
|
688
|
+
sage: G = graphs.BilinearFormsGraph(5, 1, 3)
|
689
|
+
sage: K = graphs.CompleteGraph(G.order())
|
690
|
+
sage: K.is_isomorphic(G)
|
691
|
+
True
|
692
|
+
"""
|
693
|
+
from itertools import product as cartprod
|
694
|
+
|
695
|
+
Fq = GF(q)
|
696
|
+
Fqelems = list(Fq)
|
697
|
+
FqToInt = {x: n for n, x in enumerate(Fqelems)}
|
698
|
+
dim = d * e
|
699
|
+
matricesOverq = cartprod(range(q), repeat=dim)
|
700
|
+
qto = [int(q**jj) for jj in range(dim)]
|
701
|
+
|
702
|
+
rank1Matrices = []
|
703
|
+
for u in VectorSpace(Fq, d):
|
704
|
+
if u.is_zero() or not u[u.support()[0]].is_one():
|
705
|
+
continue
|
706
|
+
|
707
|
+
for v in VectorSpace(Fq, e):
|
708
|
+
if v.is_zero():
|
709
|
+
continue
|
710
|
+
|
711
|
+
sig_check()
|
712
|
+
M = [0] * dim
|
713
|
+
for row in range(d):
|
714
|
+
for col in range(e):
|
715
|
+
M[e*row + col] = u[row] * v[col]
|
716
|
+
|
717
|
+
rank1Matrices.append(M)
|
718
|
+
|
719
|
+
edges = []
|
720
|
+
for m1 in matricesOverq:
|
721
|
+
intM1 = 0 # represents vector m1 as integer base q
|
722
|
+
for jj in range(dim):
|
723
|
+
intM1 += m1[jj] * qto[jj]
|
724
|
+
|
725
|
+
for m2 in rank1Matrices:
|
726
|
+
sig_check()
|
727
|
+
intM3 = 0
|
728
|
+
for jj in range(dim):
|
729
|
+
intM3 += FqToInt[Fqelems[m1[jj]] + Fqelems[m2[jj]]] * qto[jj]
|
730
|
+
|
731
|
+
edges.append((intM1, intM3))
|
732
|
+
|
733
|
+
G = Graph(edges, format='list_of_edges')
|
734
|
+
G.name("Bilinear forms graphs over F_%d with parameters (%d, %d)" % (q, d, e))
|
735
|
+
return G
|
736
|
+
|
737
|
+
|
738
|
+
def AlternatingFormsGraph(const int n, const int q):
|
739
|
+
r"""
|
740
|
+
Return the alternating forms graph with the given parameters.
|
741
|
+
|
742
|
+
This builds a graph whose vertices are all `n`x`n` skew-symmetric
|
743
|
+
matrices over `GF(q)` with zero diagonal. Two vertices are adjacent
|
744
|
+
if and only if the difference of the two matrices has rank 2.
|
745
|
+
|
746
|
+
This graph is distance-regular with classical parameters
|
747
|
+
`(\lfloor \frac n 2 \rfloor, q^2, q^2 - 1, q^{2 \lceil \frac n 2 \rceil -1})`.
|
748
|
+
|
749
|
+
INPUT:
|
750
|
+
|
751
|
+
- ``n`` -- integer
|
752
|
+
- ``q`` -- a prime power
|
753
|
+
|
754
|
+
EXAMPLES::
|
755
|
+
|
756
|
+
sage: G = graphs.AlternatingFormsGraph(5, 2) # long time
|
757
|
+
sage: G.is_distance_regular(True) # long time
|
758
|
+
([155, 112, None], [None, 1, 20])
|
759
|
+
|
760
|
+
REFERENCES:
|
761
|
+
|
762
|
+
See [BCN1989]_ pp. 282-284 for a rather detailed discussion, otherwise
|
763
|
+
see [VDKT2016]_ p. 22.
|
764
|
+
|
765
|
+
TESTS::
|
766
|
+
|
767
|
+
sage: # needs sage.modules
|
768
|
+
sage: G = graphs.AlternatingFormsGraph(6,2) # not tested (2 min) # needs sage.rings.finite_rings
|
769
|
+
sage: G.order() # not tested (because of above) # needs sage.rings.finite_rings
|
770
|
+
32768
|
771
|
+
sage: G.is_distance_regular(True) # not tested (33 min) # needs sage.rings.finite_rings
|
772
|
+
([651, 560, 256, None], [None, 1, 20, 336])
|
773
|
+
sage: G = graphs.AlternatingFormsGraph(4, 3)
|
774
|
+
sage: G.is_distance_regular(True)
|
775
|
+
([260, 162, None], [None, 1, 90])
|
776
|
+
"""
|
777
|
+
# n x n zero-diagonal skew-symmetric matrix
|
778
|
+
# can be represented by the upper triangular entries
|
779
|
+
# there are n*(n-1) // 2 of them
|
780
|
+
size = (n * (n-1)) // 2
|
781
|
+
V = VectorSpace(GF(q), size)
|
782
|
+
|
783
|
+
# construct all rank 2 matrices
|
784
|
+
rank2Matrices = set()
|
785
|
+
Vn = VectorSpace(GF(q), n)
|
786
|
+
basis = set(Vn.basis())
|
787
|
+
e = [Vn([0]*i + [1] + [0]*(n - i - 1)) for i in range(n)]
|
788
|
+
for v in e:
|
789
|
+
v.set_immutable()
|
790
|
+
|
791
|
+
scalars = [x for x in GF(q) if not x.is_zero()]
|
792
|
+
Vseen = set()
|
793
|
+
for v in Vn:
|
794
|
+
if v.is_zero() or not v[v.support()[0]].is_one():
|
795
|
+
continue
|
796
|
+
v.set_immutable()
|
797
|
+
# remove from basis e_i s.t. (v[i-1] =) v_i != 0
|
798
|
+
i = v.support()[0]
|
799
|
+
Ubasis = basis.difference([e[i]])
|
800
|
+
|
801
|
+
for u in Vn.span_of_basis(Ubasis):
|
802
|
+
sig_check()
|
803
|
+
if u.is_zero() or not u[u.support()[0]].is_one():
|
804
|
+
continue
|
805
|
+
u.set_immutable()
|
806
|
+
if u in Vseen:
|
807
|
+
continue
|
808
|
+
|
809
|
+
M = []
|
810
|
+
for row in range(n - 1):
|
811
|
+
upperRow = [0] * (n - 1 - row)
|
812
|
+
for col in range(row + 1, n):
|
813
|
+
upperRow[col - row - 1] = v[row]*u[col] - u[row]*v[col]
|
814
|
+
M += upperRow
|
815
|
+
|
816
|
+
for scalar in scalars:
|
817
|
+
N = tuple(map(lambda x: scalar * x, M))
|
818
|
+
rank2Matrices.add(N)
|
819
|
+
|
820
|
+
Vseen.add(v)
|
821
|
+
|
822
|
+
# now we have all matrices of rank 2
|
823
|
+
edges = []
|
824
|
+
for m1 in V:
|
825
|
+
t1 = tuple(m1)
|
826
|
+
for m2 in rank2Matrices:
|
827
|
+
sig_check()
|
828
|
+
t3 = tuple([t1[i] + m2[i] for i in range(size)])
|
829
|
+
edges.append((t1, t3))
|
830
|
+
|
831
|
+
G = Graph(edges, format='list_of_edges')
|
832
|
+
G.name("Alternating forms graph on (F_%d)^%d" % (q, n))
|
833
|
+
return G
|
834
|
+
|
835
|
+
|
836
|
+
def HermitianFormsGraph(const int n, const int r):
|
837
|
+
r"""
|
838
|
+
Return the Hermitian forms graph with the given parameters.
|
839
|
+
|
840
|
+
We build a graph whose vertices are all `n \times n` Hermitian matrices
|
841
|
+
over ``GF(r^2)``. Two vertices are adjacent if the difference of the two
|
842
|
+
vertices has rank 1.
|
843
|
+
|
844
|
+
This graph is distance-regular with classical parameters
|
845
|
+
`(n, - r, - r - 1, - (- r)^d - 1)`.
|
846
|
+
|
847
|
+
INPUT:
|
848
|
+
|
849
|
+
- ``n`` -- integer
|
850
|
+
- ``r`` -- a prime power
|
851
|
+
|
852
|
+
EXAMPLES::
|
853
|
+
|
854
|
+
sage: # needs sage.modules sage.rings.finite_rings
|
855
|
+
sage: G = graphs.HermitianFormsGraph(2, 2)
|
856
|
+
sage: G.is_distance_regular(True)
|
857
|
+
([5, 4, None], [None, 1, 2])
|
858
|
+
sage: G = graphs.HermitianFormsGraph(3, 3) # not tested (2 min)
|
859
|
+
sage: G.order() # not tested (because of the above)
|
860
|
+
19683
|
861
|
+
|
862
|
+
REFERENCES:
|
863
|
+
|
864
|
+
See [BCN1989]_ p. 285 or [VDKT2016]_ p. 22.
|
865
|
+
|
866
|
+
TESTS::
|
867
|
+
|
868
|
+
sage: # needs sage.modules sage.rings.finite_rings
|
869
|
+
sage: G = graphs.HermitianFormsGraph(3, 2)
|
870
|
+
sage: G.is_distance_regular(True)
|
871
|
+
([21, 20, 16, None], [None, 1, 2, 12])
|
872
|
+
sage: G = graphs.HermitianFormsGraph(2, 3)
|
873
|
+
sage: G.is_distance_regular(True)
|
874
|
+
([20, 18, None], [None, 1, 6])
|
875
|
+
"""
|
876
|
+
q = r * r
|
877
|
+
Fr = GF(r)
|
878
|
+
Fq = GF(q)
|
879
|
+
i = Fq.gen()
|
880
|
+
ir = i**r
|
881
|
+
|
882
|
+
toR = {(a + i*b): (a + ir*b) for a, b in itertools.product(Fr, repeat=2)}
|
883
|
+
|
884
|
+
def build_mat(v, w):
|
885
|
+
# get upper diagonal entries
|
886
|
+
res = []
|
887
|
+
used_v = 0
|
888
|
+
used_w = 0
|
889
|
+
for row in range(n):
|
890
|
+
res += [v[used_v]] + [v[used_v + 1 + j] + i * w[used_w + j]
|
891
|
+
for j in range(n - 1 - row)]
|
892
|
+
used_v += n - row
|
893
|
+
used_w += n - 1 - row
|
894
|
+
|
895
|
+
return tuple(res)
|
896
|
+
|
897
|
+
# produce all rank1 matrices
|
898
|
+
rank1Matrices = []
|
899
|
+
for w1 in VectorSpace(Fr, n):
|
900
|
+
if not w1.is_zero():
|
901
|
+
# build matrix
|
902
|
+
nonZero = 0
|
903
|
+
while nonZero < n and w1[nonZero] == 0:
|
904
|
+
nonZero += 1
|
905
|
+
|
906
|
+
for w2 in VectorSpace(Fr, n - nonZero - 1):
|
907
|
+
# get upper triangular entries
|
908
|
+
sig_check()
|
909
|
+
|
910
|
+
v = [w1[nonZero]] + \
|
911
|
+
[w1[nonZero + 1 + j] + i * w2[j]
|
912
|
+
for j in range(n - nonZero - 1)]
|
913
|
+
|
914
|
+
res = []
|
915
|
+
for row in range(nonZero):
|
916
|
+
res += [0] * (n - row)
|
917
|
+
|
918
|
+
res += v
|
919
|
+
|
920
|
+
for row in range(1, n - nonZero):
|
921
|
+
factor = toR[v[row]] / v[0]
|
922
|
+
res += list(map(lambda x: factor * x, v[row:]))
|
923
|
+
|
924
|
+
rank1Matrices.append(res)
|
925
|
+
|
926
|
+
Vs = VectorSpace(Fr, (n * (n+1)) // 2)
|
927
|
+
Va = VectorSpace(Fr, (n * (n-1)) // 2)
|
928
|
+
|
929
|
+
edges = []
|
930
|
+
for a, b in itertools.product(Vs, Va):
|
931
|
+
M = build_mat(a, b)
|
932
|
+
for R in rank1Matrices:
|
933
|
+
N = tuple([M[i] + R[i] for i in range((n * (n+1)) // 2)])
|
934
|
+
edges.append((M, N))
|
935
|
+
|
936
|
+
G = Graph(edges, format='list_of_edges')
|
937
|
+
G.name(f"Hermitian forms graph on (F_{q})^{n}")
|
938
|
+
return G
|
939
|
+
|
940
|
+
|
941
|
+
def DoubleOddGraph(const int n):
|
942
|
+
r"""
|
943
|
+
Return the double odd graph on `2n+1` points.
|
944
|
+
|
945
|
+
The graph is obtained using the subsets of size `n` and `n+1`
|
946
|
+
of `{1, 2, ..., 2n+1}` as vertices. Two vertices are adjacent if one
|
947
|
+
is included in the other.
|
948
|
+
|
949
|
+
The graph is distance-transitive.
|
950
|
+
|
951
|
+
INPUT:
|
952
|
+
|
953
|
+
- ``n`` -- integer; must be greater than 0
|
954
|
+
|
955
|
+
EXAMPLES::
|
956
|
+
|
957
|
+
sage: G = graphs.DoubleOddGraph(5)
|
958
|
+
sage: G.is_distance_regular(True)
|
959
|
+
([6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, None],
|
960
|
+
[None, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6])
|
961
|
+
sage: G = graphs.DoubleOddGraph(3)
|
962
|
+
sage: G.diameter()
|
963
|
+
7
|
964
|
+
sage: G.is_distance_regular(True)
|
965
|
+
([4, 3, 3, 2, 2, 1, 1, None], [None, 1, 1, 2, 2, 3, 3, 4])
|
966
|
+
|
967
|
+
REFERENCES:
|
968
|
+
|
969
|
+
See [BCN1989]_ pp. 259-261 or [VDKT2016]_ p. 25.
|
970
|
+
|
971
|
+
TESTS:
|
972
|
+
|
973
|
+
DoubleOddGraph is bipartite double of OddGraph::
|
974
|
+
|
975
|
+
sage: H = graphs.OddGraph(4)
|
976
|
+
sage: G1 = graphs.DoubleOddGraph(3)
|
977
|
+
sage: vertices = [(x, 0) for x in H] + [(x, 1) for x in H]
|
978
|
+
sage: G2 = Graph([vertices, lambda i, j:
|
979
|
+
....: i[1] != j[1] and H.has_edge(i[0], j[0])])
|
980
|
+
sage: G2.is_isomorphic(G1)
|
981
|
+
True
|
982
|
+
"""
|
983
|
+
from sage.combinat.integer_vector import IntegerVectors
|
984
|
+
|
985
|
+
if n < 1:
|
986
|
+
raise ValueError("n must be >= 1")
|
987
|
+
|
988
|
+
cdef list edges, s1
|
989
|
+
cdef int i
|
990
|
+
|
991
|
+
# a binary vector of size 2n + 1 represents a set
|
992
|
+
edges = []
|
993
|
+
for s in IntegerVectors(n, k=2*n + 1, max_part=1):
|
994
|
+
s1 = list(s)
|
995
|
+
for i in range(2*n + 1):
|
996
|
+
sig_check()
|
997
|
+
if s1[i] == 0:
|
998
|
+
s2 = list(s) # duplicate list
|
999
|
+
s2[i] = 1
|
1000
|
+
edges.append((tuple(s1), tuple(s2)))
|
1001
|
+
|
1002
|
+
G = Graph(edges, format='list_of_edges')
|
1003
|
+
G.name("Bipartite double of Odd graph on a set of %d elements" % (2*n + 1))
|
1004
|
+
return G
|
1005
|
+
|
1006
|
+
|
1007
|
+
def HalfCube(const int n):
|
1008
|
+
r"""
|
1009
|
+
Return the halved cube in `n` dimensions.
|
1010
|
+
|
1011
|
+
The graph is distance-regular with classical parameters
|
1012
|
+
`(\lfloor \frac n 2 \rfloor, 1, 2, 2 \lceil \frac n 2 \rceil -1)`.
|
1013
|
+
|
1014
|
+
INPUT:
|
1015
|
+
|
1016
|
+
- ``n`` -- integer; must be greater than 2
|
1017
|
+
|
1018
|
+
EXAMPLES::
|
1019
|
+
|
1020
|
+
sage: G = graphs.HalfCube(8)
|
1021
|
+
sage: G.is_distance_regular(True)
|
1022
|
+
([28, 15, 6, 1, None], [None, 1, 6, 15, 28])
|
1023
|
+
sage: G = graphs.HalfCube(4)
|
1024
|
+
sage: G.is_distance_regular(True)
|
1025
|
+
([6, 1, None], [None, 1, 6])
|
1026
|
+
|
1027
|
+
REFERENCES:
|
1028
|
+
|
1029
|
+
See [BCN1989]_ pp. 264, 265 or [VDKT2016]_ p. 21.
|
1030
|
+
This construction can be found on
|
1031
|
+
:wikipedia:`Halved_cube_graph#Equivalent_constructions`
|
1032
|
+
|
1033
|
+
TESTS:
|
1034
|
+
|
1035
|
+
HalfCube is a half of the CubeGraph::
|
1036
|
+
|
1037
|
+
sage: H = graphs.CubeGraph(8)
|
1038
|
+
sage: s1, s2 = H.bipartite_sets()
|
1039
|
+
sage: G1 = Graph([s1, lambda i, j: H.distance(i, j) == 2])
|
1040
|
+
sage: G2 = graphs.HalfCube(8)
|
1041
|
+
sage: G1.is_isomorphic(G2)
|
1042
|
+
True
|
1043
|
+
"""
|
1044
|
+
from math import cos, sin, pi
|
1045
|
+
|
1046
|
+
if n < 2:
|
1047
|
+
raise ValueError("the dimension must be n > 1")
|
1048
|
+
|
1049
|
+
cdef int u, uu, v, i, j
|
1050
|
+
cdef list E = []
|
1051
|
+
cdef dict pos = {} # dictionary of positions
|
1052
|
+
cdef float theta = pi / (n - 1)
|
1053
|
+
cdef list cosi = [<float>cos(i*theta) for i in range(n - 1)]
|
1054
|
+
cdef list sini = [<float>sin(i*theta) for i in range(n - 1)]
|
1055
|
+
|
1056
|
+
for u in range(2**(n - 1)):
|
1057
|
+
sig_check()
|
1058
|
+
pos[u] = (sum(((u >> (n-2-i)) & 1) * cosi[i] for i in range(n - 1)),
|
1059
|
+
sum(((u >> (n-2-i)) & 1) * sini[i] for i in range(n - 1)))
|
1060
|
+
|
1061
|
+
for i in range(n - 1):
|
1062
|
+
uu = u ^ (1 << i)
|
1063
|
+
if u < uu:
|
1064
|
+
E.append((u, uu))
|
1065
|
+
for j in range(i + 1, n - 1):
|
1066
|
+
v = uu ^ (1 << j)
|
1067
|
+
if u < v:
|
1068
|
+
E.append((u, v))
|
1069
|
+
|
1070
|
+
G = Graph([range(2**(n - 1)), E], format='vertices_and_edges')
|
1071
|
+
G.set_pos(pos)
|
1072
|
+
G.name("Half %d Cube" % n)
|
1073
|
+
return G
|
1074
|
+
|
1075
|
+
|
1076
|
+
def GrassmannGraph(const int q, const int n, const int input_e):
|
1077
|
+
r"""
|
1078
|
+
Return the Grassmann graph with parameters `(q, n, e)`.
|
1079
|
+
|
1080
|
+
This builds the Grassmann graph `J_q(n,e)`. That is, for a vector
|
1081
|
+
space `V = \mathbb F(q)^n` the output is the graph on the subspaces
|
1082
|
+
of dimension `e` where two subspaces are adjacent if their intersection
|
1083
|
+
has dimension `e-1`.
|
1084
|
+
|
1085
|
+
This graph is distance-regular with classical parameters
|
1086
|
+
`(\min(e, n-e), q, q, \genfrac {[}{]} {0pt} {} {n-e+1} 1 _q -1)`
|
1087
|
+
|
1088
|
+
INPUT:
|
1089
|
+
|
1090
|
+
- ``q`` -- a prime power
|
1091
|
+
- ``n``, ``e`` -- integers with `n > e+1`
|
1092
|
+
|
1093
|
+
EXAMPLES::
|
1094
|
+
|
1095
|
+
sage: G = graphs.GrassmannGraph(2, 4, 2) # needs sage.modules sage.rings.finite_rings
|
1096
|
+
sage: G.is_distance_regular(True) # needs sage.modules sage.rings.finite_rings
|
1097
|
+
([18, 8, None], [None, 1, 9])
|
1098
|
+
|
1099
|
+
REFERENCES:
|
1100
|
+
|
1101
|
+
See [BCN1989]_ pp. 268-272 or [VDKT2016]_ p. 21.
|
1102
|
+
|
1103
|
+
TESTS::
|
1104
|
+
|
1105
|
+
sage: # needs sage.modules sage.rings.finite_rings
|
1106
|
+
sage: G = graphs.GrassmannGraph(2, 6, 3) # long time
|
1107
|
+
sage: G.is_distance_regular(True) # long time
|
1108
|
+
([98, 72, 32, None], [None, 1, 9, 49])
|
1109
|
+
sage: G = graphs.GrassmannGraph(3, 4, 2)
|
1110
|
+
sage: G.is_distance_regular(True)
|
1111
|
+
([48, 27, None], [None, 1, 16])
|
1112
|
+
"""
|
1113
|
+
from sage.combinat.designs import design_catalog as designs
|
1114
|
+
|
1115
|
+
if n <= input_e + 1:
|
1116
|
+
raise ValueError(f"Impossible parameters n <= e+1 ({n} > {input_e + 1})")
|
1117
|
+
|
1118
|
+
e = input_e
|
1119
|
+
if n < 2 * input_e:
|
1120
|
+
e = n - input_e
|
1121
|
+
|
1122
|
+
PG = designs.ProjectiveGeometryDesign(n - 1, e - 1, q)
|
1123
|
+
# we want the intersection graph
|
1124
|
+
# the size of the intersection must be (q^{e-1} - 1) / (q-1)
|
1125
|
+
size = (q**(e - 1) - 1) // (q - 1)
|
1126
|
+
G = PG.intersection_graph([size])
|
1127
|
+
G.name("Grassmann graph J_%d(%d, %d)" % (q, n, e))
|
1128
|
+
return G
|
1129
|
+
|
1130
|
+
|
1131
|
+
def DoubleGrassmannGraph(const int q, const int e):
|
1132
|
+
r"""
|
1133
|
+
Return the bipartite double of the distance-`e` graph of the Grassmann graph `J_q(n,e)`.
|
1134
|
+
|
1135
|
+
This graph can also be described as follows:
|
1136
|
+
Let `V` be the vector space of dimension `n` over `GF(q)`.
|
1137
|
+
The vertex set is the set of `e+1` or `e` subspaces of `V`.
|
1138
|
+
Two vertices are adjacent if one subspace is contained in the other.
|
1139
|
+
|
1140
|
+
This graph is distance-transitive.
|
1141
|
+
|
1142
|
+
INPUT:
|
1143
|
+
|
1144
|
+
- ``q`` -- a prime power
|
1145
|
+
- ``e`` -- integer
|
1146
|
+
|
1147
|
+
EXAMPLES::
|
1148
|
+
|
1149
|
+
sage: G = graphs.DoubleGrassmannGraph(2,1) # needs sage.modules
|
1150
|
+
sage: G.diameter() # needs sage.modules
|
1151
|
+
3
|
1152
|
+
sage: G.is_distance_regular(True) # needs sage.modules
|
1153
|
+
([3, 2, 2, None], [None, 1, 1, 3])
|
1154
|
+
|
1155
|
+
|
1156
|
+
REFERENCES:
|
1157
|
+
|
1158
|
+
See [BCN1989]_ pp. 272, 273 or [VDKT2016]_ p. 25.
|
1159
|
+
|
1160
|
+
TESTS::
|
1161
|
+
|
1162
|
+
sage: # needs sage.modules
|
1163
|
+
sage: G = graphs.DoubleGrassmannGraph(5,1)
|
1164
|
+
sage: G.order()
|
1165
|
+
62
|
1166
|
+
sage: G.is_distance_regular(True)
|
1167
|
+
([6, 5, 5, None], [None, 1, 1, 6])
|
1168
|
+
sage: G = graphs.DoubleGrassmannGraph(3, 2) # long time # needs sage.rings.finite_rings
|
1169
|
+
sage: G.order() # long time # needs sage.rings.finite_rings
|
1170
|
+
2420
|
1171
|
+
sage: G.is_distance_regular(True) # long time # needs sage.rings.finite_rings
|
1172
|
+
([13, 12, 12, 9, 9, None], [None, 1, 1, 4, 4, 13])
|
1173
|
+
"""
|
1174
|
+
n = 2*e + 1
|
1175
|
+
V = VectorSpace(GF(q), n)
|
1176
|
+
|
1177
|
+
edges = []
|
1178
|
+
for W in V.subspaces(e + 1):
|
1179
|
+
Wbasis = frozenset(W.basis())
|
1180
|
+
for U in W.subspaces(e):
|
1181
|
+
sig_check()
|
1182
|
+
Ubasis = frozenset(U.basis())
|
1183
|
+
edges.append((Wbasis, Ubasis))
|
1184
|
+
|
1185
|
+
G = Graph(edges, format='list_of_edges')
|
1186
|
+
G.name("Double Grassmann graph (%d, %d, %d)" % (n, e, q))
|
1187
|
+
return G
|
1188
|
+
|
1189
|
+
|
1190
|
+
def is_from_GQ_spread(list arr):
|
1191
|
+
r"""
|
1192
|
+
Return a pair `(s, t)` if the graph obtained from a GQ of order `(s, t)`
|
1193
|
+
with a spread has the intersection array passed. We also require that such
|
1194
|
+
GQ can be built by Sage.
|
1195
|
+
If no such pair exists, then return ``False``.
|
1196
|
+
|
1197
|
+
INPUT:
|
1198
|
+
|
1199
|
+
- ``arr`` -- list; an intersection array
|
1200
|
+
|
1201
|
+
EXAMPLES::
|
1202
|
+
|
1203
|
+
sage: from sage.graphs.generators.distance_regular import \
|
1204
|
+
....: is_from_GQ_spread, graph_from_GQ_spread
|
1205
|
+
sage: is_from_GQ_spread([125, 120, 1, 1, 24, 125]) # needs sage.libs.flint sage.libs.pari
|
1206
|
+
(5, 25)
|
1207
|
+
sage: G = graph_from_GQ_spread(5, 25) # needs sage.libs.flint sage.libs.pari
|
1208
|
+
sage: G.is_distance_regular(True) # needs sage.libs.flint sage.libs.pari
|
1209
|
+
([125, 120, 1, None], [None, 1, 24, 125])
|
1210
|
+
|
1211
|
+
REFERENCES:
|
1212
|
+
|
1213
|
+
The graphs we are looking for are antipodal covers of complete graphs.
|
1214
|
+
See [BCN1989]_ pp. 385, 386 for a discussion on these particular case.
|
1215
|
+
|
1216
|
+
TESTS::
|
1217
|
+
|
1218
|
+
sage: from sage.graphs.generators.distance_regular import \
|
1219
|
+
....: is_from_GQ_spread
|
1220
|
+
sage: is_from_GQ_spread([343, 336, 1, 1, 48, 343]) # needs sage.libs.flint sage.libs.pari
|
1221
|
+
(7, 49)
|
1222
|
+
sage: is_from_GQ_spread([343, 336, 1, 2, 48, 343]) # needs sage.libs.flint sage.libs.pari
|
1223
|
+
False
|
1224
|
+
|
1225
|
+
Check that we don't get ``True`` for inexisting GQs::
|
1226
|
+
|
1227
|
+
sage: from sage.graphs.generators.distance_regular import \
|
1228
|
+
....: is_from_GQ_spread
|
1229
|
+
sage: s = 5
|
1230
|
+
sage: t = 6
|
1231
|
+
sage: [s * t, s * (t-1), 1, 1, t - 1, s * t]
|
1232
|
+
[30, 25, 1, 1, 5, 30]
|
1233
|
+
sage: is_from_GQ_spread([30, 25, 1, 1, 5, 30]) # needs sage.libs.flint sage.libs.pari
|
1234
|
+
False
|
1235
|
+
"""
|
1236
|
+
from sage.combinat.designs import design_catalog as designs
|
1237
|
+
|
1238
|
+
if len(arr) != 6:
|
1239
|
+
return False
|
1240
|
+
|
1241
|
+
t = arr[4] + 1
|
1242
|
+
if t <= 1: # avoid division by 0
|
1243
|
+
return False
|
1244
|
+
|
1245
|
+
s = arr[1] // (t-1)
|
1246
|
+
if s == 1 and t == 1: # in this case we don't get a connected graph
|
1247
|
+
return False
|
1248
|
+
|
1249
|
+
if arr != [s * t, s * (t-1), 1, 1, t - 1, s * t]:
|
1250
|
+
return False
|
1251
|
+
|
1252
|
+
# check Sage can build it (it may not exist)
|
1253
|
+
if designs.generalised_quadrangle_with_spread(s, t, existence=True) \
|
1254
|
+
is not True:
|
1255
|
+
return False
|
1256
|
+
|
1257
|
+
return (s, t)
|
1258
|
+
|
1259
|
+
|
1260
|
+
def graph_from_GQ_spread(const int s, const int t):
|
1261
|
+
r"""
|
1262
|
+
Return the point graph of the generalised quadrangle with
|
1263
|
+
order `(s, t)` after removing one of its spreads.
|
1264
|
+
|
1265
|
+
These graphs are antipodal covers of complete graphs and, in particular,
|
1266
|
+
distance-regular graphs of diameter 3.
|
1267
|
+
|
1268
|
+
INPUT:
|
1269
|
+
|
1270
|
+
- ``s``, ``t`` -- integers; order of the generalised quadrangle
|
1271
|
+
|
1272
|
+
EXAMPLES::
|
1273
|
+
|
1274
|
+
sage: from sage.graphs.generators.distance_regular import \
|
1275
|
+
....: graph_from_GQ_spread
|
1276
|
+
sage: G = graph_from_GQ_spread(4, 16) # needs sage.libs.flint sage.libs.pari
|
1277
|
+
sage: G.is_distance_regular(True) # needs sage.libs.flint sage.libs.pari
|
1278
|
+
([64, 60, 1, None], [None, 1, 15, 64])
|
1279
|
+
|
1280
|
+
REFERENCES:
|
1281
|
+
|
1282
|
+
The graphs constructed here follow [BCN1989]_ pp. 385, 386.
|
1283
|
+
|
1284
|
+
TESTS::
|
1285
|
+
|
1286
|
+
sage: from sage.graphs.generators.distance_regular import \
|
1287
|
+
....: graph_from_GQ_spread, is_from_GQ_spread
|
1288
|
+
sage: is_from_GQ_spread([64, 60, 1, 1, 15, 64]) # needs sage.libs.flint sage.libs.pari
|
1289
|
+
(4, 16)
|
1290
|
+
sage: graph_from_GQ_spread(*is_from_GQ_spread([27, 24, 1, 1, 8, 27])) # needs sage.libs.flint sage.libs.pari
|
1291
|
+
Graph on 112 vertices
|
1292
|
+
sage: _.is_distance_regular(True) # needs sage.libs.flint sage.libs.pari
|
1293
|
+
([27, 24, 1, None], [None, 1, 8, 27])
|
1294
|
+
"""
|
1295
|
+
from sage.combinat.designs import design_catalog as designs
|
1296
|
+
|
1297
|
+
(GQ, S) = designs.generalised_quadrangle_with_spread(s, t, check=False)
|
1298
|
+
|
1299
|
+
edges = []
|
1300
|
+
for b in GQ.blocks():
|
1301
|
+
if b in S: # skip blocks in spread
|
1302
|
+
continue
|
1303
|
+
for p1, p2 in itertools.combinations(b, 2):
|
1304
|
+
sig_check()
|
1305
|
+
edges.append((p1, p2))
|
1306
|
+
|
1307
|
+
return Graph(edges, format='list_of_edges')
|
1308
|
+
|
1309
|
+
|
1310
|
+
def GeneralisedDodecagonGraph(const int s, const int t):
|
1311
|
+
r"""
|
1312
|
+
Return the point-graph of a generalised dodecagon of order `(s,t)`.
|
1313
|
+
|
1314
|
+
INPUT:
|
1315
|
+
|
1316
|
+
- ``s``, ``t`` -- integers; order of the generalised dodecagon
|
1317
|
+
|
1318
|
+
EXAMPLES::
|
1319
|
+
|
1320
|
+
sage: # optional - gap_package_atlasrep internet
|
1321
|
+
sage: G = graphs.GeneralisedDodecagonGraph(1, 5)
|
1322
|
+
sage: G.is_distance_regular(True)
|
1323
|
+
([6, 5, 5, 5, 5, 5, None], [None, 1, 1, 1, 1, 1, 6])
|
1324
|
+
sage: H = graphs.GeneralisedDodecagonGraph(5, 1)
|
1325
|
+
sage: H.order()
|
1326
|
+
23436
|
1327
|
+
sage: H.is_distance_regular(True) # not tested (6 min)
|
1328
|
+
([10, 5, 5, 5, 5, 5, None], [None, 1, 1, 1, 1, 1, 2])
|
1329
|
+
|
1330
|
+
.. NOTE::
|
1331
|
+
|
1332
|
+
This function indirectly uses the GAP's AtlasRep package.
|
1333
|
+
Thus you may need an internet connection and the optional Sage's
|
1334
|
+
package ``gap_packages``.
|
1335
|
+
|
1336
|
+
REFERENCES:
|
1337
|
+
|
1338
|
+
See [BCN1989]_ pp. 200-205 for a discussion of distance-regular graphs from
|
1339
|
+
generalised polygons.
|
1340
|
+
|
1341
|
+
TESTS:
|
1342
|
+
|
1343
|
+
Test all graphs of order `(1, q)`::
|
1344
|
+
|
1345
|
+
sage: # optional - gap_package_atlasrep internet
|
1346
|
+
sage: G = graphs.GeneralisedDodecagonGraph(1, 4)
|
1347
|
+
sage: G.is_distance_regular(True)
|
1348
|
+
([5, 4, 4, 4, 4, 4, None], [None, 1, 1, 1, 1, 1, 5])
|
1349
|
+
sage: G = graphs.GeneralisedDodecagonGraph(1, 3)
|
1350
|
+
sage: G.is_distance_regular(True)
|
1351
|
+
([4, 3, 3, 3, 3, 3, None], [None, 1, 1, 1, 1, 1, 4])
|
1352
|
+
sage: G = graphs.GeneralisedDodecagonGraph(1, 2)
|
1353
|
+
sage: G.is_distance_regular(True)
|
1354
|
+
([3, 2, 2, 2, 2, 2, None], [None, 1, 1, 1, 1, 1, 3])
|
1355
|
+
sage: G = graphs.GeneralisedDodecagonGraph(1, 1)
|
1356
|
+
sage: G.is_distance_regular(True)
|
1357
|
+
([2, 1, 1, 1, 1, 1, None], [None, 1, 1, 1, 1, 1, 2])
|
1358
|
+
|
1359
|
+
Now test all graphs of order `(q, 1)`::
|
1360
|
+
|
1361
|
+
sage: # optional - gap_package_atlasrep internet
|
1362
|
+
sage: G = graphs.GeneralisedDodecagonGraph(4, 1)
|
1363
|
+
sage: G.is_distance_regular(True)
|
1364
|
+
([8, 4, 4, 4, 4, 4, None], [None, 1, 1, 1, 1, 1, 2])
|
1365
|
+
sage: G = graphs.GeneralisedDodecagonGraph(3, 1)
|
1366
|
+
sage: G.is_distance_regular(True)
|
1367
|
+
([6, 3, 3, 3, 3, 3, None], [None, 1, 1, 1, 1, 1, 2])
|
1368
|
+
sage: G = graphs.GeneralisedDodecagonGraph(2, 1)
|
1369
|
+
sage: G.is_distance_regular(True)
|
1370
|
+
([4, 2, 2, 2, 2, 2, None], [None, 1, 1, 1, 1, 1, 2])
|
1371
|
+
"""
|
1372
|
+
from sage.arith.misc import is_prime_power
|
1373
|
+
|
1374
|
+
cdef int q = 0
|
1375
|
+
cdef int orderType = 0
|
1376
|
+
|
1377
|
+
# decide the type of graph
|
1378
|
+
if s == 1: # (1, q)
|
1379
|
+
q = t
|
1380
|
+
elif t == 1: # (q, 1)
|
1381
|
+
q = s
|
1382
|
+
orderType = 1
|
1383
|
+
else:
|
1384
|
+
raise ValueError(
|
1385
|
+
f"Generalised dodecagon of order ({s}, {t}) does not exist")
|
1386
|
+
|
1387
|
+
if q == 1: # order (1, 1)
|
1388
|
+
from sage.graphs.generators.basic import CycleGraph
|
1389
|
+
return CycleGraph(12)
|
1390
|
+
|
1391
|
+
if not is_prime_power(q):
|
1392
|
+
raise ValueError(
|
1393
|
+
f"No generalised dodecagon of order ({s}, {t}) is known")
|
1394
|
+
|
1395
|
+
if orderType == 0:
|
1396
|
+
# incidence graph of hexagon (q,q)
|
1397
|
+
H = GeneralisedHexagonGraph(q, q)
|
1398
|
+
lines = _extract_lines(H)
|
1399
|
+
|
1400
|
+
edges = []
|
1401
|
+
for l in lines:
|
1402
|
+
for p in l:
|
1403
|
+
sig_check()
|
1404
|
+
edges.append((p, l))
|
1405
|
+
|
1406
|
+
G = Graph(edges, format='list_of_edges')
|
1407
|
+
G.name("Generalised dodecagon of order (1, %d)" % q)
|
1408
|
+
return G
|
1409
|
+
|
1410
|
+
else: # orderType == 1
|
1411
|
+
# dual
|
1412
|
+
H = GeneralisedDodecagonGraph(t, s)
|
1413
|
+
G = _line_graph_generalised_polygon(H)
|
1414
|
+
G.name("Generalised dodecagon of order (%s, %d)" % (s, t))
|
1415
|
+
return G
|
1416
|
+
|
1417
|
+
|
1418
|
+
def GeneralisedOctagonGraph(const int s, const int t):
|
1419
|
+
r"""
|
1420
|
+
Return the point-graph of a generalised octagon of order `(s,t)`.
|
1421
|
+
|
1422
|
+
INPUT:
|
1423
|
+
|
1424
|
+
- ``s``, ``t`` -- integers; order of the generalised octagon
|
1425
|
+
|
1426
|
+
EXAMPLES::
|
1427
|
+
|
1428
|
+
sage: # needs sage.libs.gap
|
1429
|
+
sage: G = graphs.GeneralisedOctagonGraph(1, 4) # optional - database_graphs
|
1430
|
+
sage: G.is_distance_regular(True) # optional - database_graphs
|
1431
|
+
([5, 4, 4, 4, None], [None, 1, 1, 1, 5])
|
1432
|
+
sage: G = graphs.GeneralisedOctagonGraph(2, 4) # optional - gap_package_atlasrep internet
|
1433
|
+
sage: G.is_distance_regular(True) # optional - gap_package_atlasrep internet
|
1434
|
+
([10, 8, 8, 8, None], [None, 1, 1, 1, 5])
|
1435
|
+
sage: G = graphs.GeneralisedOctagonGraph(5, 1) # optional - database_graphs
|
1436
|
+
sage: G.is_distance_regular(True) # optional - database_graphs
|
1437
|
+
([10, 5, 5, 5, None], [None, 1, 1, 1, 2])
|
1438
|
+
|
1439
|
+
.. NOTE::
|
1440
|
+
|
1441
|
+
This function uses the GAP's AtlasRep package to build the graphs
|
1442
|
+
of order `(2, 4)` or `(4, 2)`. For those graphs you need an internet
|
1443
|
+
connection and Sage's optional package ``gap_packages``.
|
1444
|
+
|
1445
|
+
REFERENCES:
|
1446
|
+
|
1447
|
+
See [BCN1989]_ pp. 200-205 for a discussion of distance-regular graphs from
|
1448
|
+
generalised polygons.
|
1449
|
+
|
1450
|
+
TESTS::
|
1451
|
+
|
1452
|
+
sage: G = graphs.GeneralisedOctagonGraph(8, 64) # needs sage.libs.gap
|
1453
|
+
Traceback (most recent call last):
|
1454
|
+
...
|
1455
|
+
NotImplementedError: Graph would be too big
|
1456
|
+
sage: G = graphs.GeneralisedOctagonGraph(4, 16) # needs sage.libs.gap
|
1457
|
+
Traceback (most recent call last):
|
1458
|
+
...
|
1459
|
+
ValueError: generalised octagons of order (q, q^2) are known only for odd powers q of 2
|
1460
|
+
"""
|
1461
|
+
from sage.arith.misc import is_prime_power
|
1462
|
+
from sage.libs.gap.libgap import libgap
|
1463
|
+
from sage.graphs.strongly_regular_db import strongly_regular_graph
|
1464
|
+
|
1465
|
+
cdef int q = 0
|
1466
|
+
cdef int orderType = 0
|
1467
|
+
|
1468
|
+
if s == 1: # (1, q)
|
1469
|
+
q = t
|
1470
|
+
elif t == 1: # (q, 1)
|
1471
|
+
q = s
|
1472
|
+
orderType = 1
|
1473
|
+
elif s**2 == t: # (q, q^2)
|
1474
|
+
q = s
|
1475
|
+
(p, k) = is_prime_power(q, get_data=True)
|
1476
|
+
|
1477
|
+
if p != 2 or k % 2 != 1:
|
1478
|
+
raise ValueError(("generalised octagons of order (q, q^2) "
|
1479
|
+
"are known only for odd powers q of 2"))
|
1480
|
+
orderType = 2
|
1481
|
+
elif t**2 == s: # (q^2, q)
|
1482
|
+
q = t
|
1483
|
+
orderType = 1
|
1484
|
+
else:
|
1485
|
+
raise ValueError(f"No generalised octagon of order ({s}, {t}) is known")
|
1486
|
+
|
1487
|
+
if q == 1: # order (1, 1)
|
1488
|
+
from sage.graphs.generators.basic import CycleGraph
|
1489
|
+
return CycleGraph(8)
|
1490
|
+
|
1491
|
+
if not is_prime_power(q):
|
1492
|
+
raise ValueError(f"No generalised octagon of order ({s}, {t}) is known")
|
1493
|
+
|
1494
|
+
if orderType == 0:
|
1495
|
+
# incidence graph of generalised quadrangle (q, q)
|
1496
|
+
|
1497
|
+
H = strongly_regular_graph((q+1) * (q*q + 1), q * (q+1), q - 1, q + 1,
|
1498
|
+
check=False)
|
1499
|
+
|
1500
|
+
lines = _extract_lines(H)
|
1501
|
+
|
1502
|
+
edges = []
|
1503
|
+
for l in lines:
|
1504
|
+
for p in l:
|
1505
|
+
sig_check()
|
1506
|
+
edges.append((p, l))
|
1507
|
+
|
1508
|
+
G = Graph(edges, format='list_of_edges')
|
1509
|
+
G.name("Generalised octagon of order (1, %d)" % q)
|
1510
|
+
return G
|
1511
|
+
|
1512
|
+
elif orderType == 1:
|
1513
|
+
# dual
|
1514
|
+
H = GeneralisedOctagonGraph(t, s)
|
1515
|
+
G = _line_graph_generalised_polygon(H)
|
1516
|
+
G.name("Generalised octagon of order(%d, %d)" % (s, t))
|
1517
|
+
return G
|
1518
|
+
else:
|
1519
|
+
if q == 2:
|
1520
|
+
group = libgap.AtlasGroup("2F4(2)", libgap.NrMovedPoints, 1755)
|
1521
|
+
G = Graph(libgap.Orbit(group, [1, 73], libgap.OnSets),
|
1522
|
+
format='list_of_edges')
|
1523
|
+
G.name("Generalised octagon of order (2, 4)")
|
1524
|
+
return G
|
1525
|
+
else:
|
1526
|
+
raise NotImplementedError("Graph would be too big")
|
1527
|
+
|
1528
|
+
|
1529
|
+
def GeneralisedHexagonGraph(const int s, const int t):
|
1530
|
+
r"""
|
1531
|
+
Return the point-graph of a generalised hexagon of order `(s,t)`.
|
1532
|
+
|
1533
|
+
INPUT:
|
1534
|
+
|
1535
|
+
- ``s``, ``t`` -- integers; order of the generalised hexagon
|
1536
|
+
|
1537
|
+
EXAMPLES::
|
1538
|
+
|
1539
|
+
sage: # needs sage.libs.gap
|
1540
|
+
sage: G = graphs.GeneralisedHexagonGraph(5, 5) # optional - gap_package_atlasrep internet
|
1541
|
+
sage: G.is_distance_regular(True) # optional - gap_package_atlasrep internet
|
1542
|
+
([30, 25, 25, None], [None, 1, 1, 6])
|
1543
|
+
sage: G = graphs.GeneralisedHexagonGraph(7, 1)
|
1544
|
+
sage: G.is_distance_regular(True)
|
1545
|
+
([14, 7, 7, None], [None, 1, 1, 2])
|
1546
|
+
sage: graphs.GeneralisedHexagonGraph(1, 1)
|
1547
|
+
Cycle graph: Graph on 6 vertices
|
1548
|
+
|
1549
|
+
.. NOTE::
|
1550
|
+
|
1551
|
+
This function uses the GAP's AtlasRep package to build GHs
|
1552
|
+
of order `(q, q)`, `(q, q^3)` or `(q^3, q)`. For those graphs you need
|
1553
|
+
an internet connection and Sage's optional package ``gap_packages``.
|
1554
|
+
|
1555
|
+
REFERENCES:
|
1556
|
+
|
1557
|
+
See [BCN1989]_ pp. 200-205 for a discussion of distance-regular graphs from
|
1558
|
+
generalised polygons.
|
1559
|
+
|
1560
|
+
TESTS::
|
1561
|
+
|
1562
|
+
sage: # optional - gap_package_atlasrep internet
|
1563
|
+
sage: G = graphs.GeneralisedHexagonGraph(4, 4)
|
1564
|
+
sage: G.is_distance_regular(True)
|
1565
|
+
([20, 16, 16, None], [None, 1, 1, 5])
|
1566
|
+
sage: G = graphs.GeneralisedHexagonGraph(3, 3)
|
1567
|
+
sage: G.is_distance_regular(True)
|
1568
|
+
([12, 9, 9, None], [None, 1, 1, 4])
|
1569
|
+
sage: G = graphs.GeneralisedHexagonGraph(2, 2)
|
1570
|
+
sage: G.is_distance_regular(True)
|
1571
|
+
([6, 4, 4, None], [None, 1, 1, 3])
|
1572
|
+
sage: G = graphs.GeneralisedHexagonGraph(2, 8)
|
1573
|
+
sage: G.is_distance_regular(True)
|
1574
|
+
([18, 16, 16, None], [None, 1, 1, 9])
|
1575
|
+
"""
|
1576
|
+
from sage.arith.misc import is_prime_power
|
1577
|
+
from sage.libs.gap.libgap import libgap
|
1578
|
+
from sage.combinat.designs import design_catalog as designs
|
1579
|
+
|
1580
|
+
cdef int q = 0
|
1581
|
+
cdef int orderType = 0
|
1582
|
+
|
1583
|
+
if s == 1: # (1, q)
|
1584
|
+
q = t
|
1585
|
+
elif t == 1: # (q, 1)
|
1586
|
+
q = s
|
1587
|
+
orderType = 1
|
1588
|
+
elif s == t: # (q, q)
|
1589
|
+
q = s
|
1590
|
+
orderType = 2
|
1591
|
+
elif s**3 == t: # (q, q^3)
|
1592
|
+
q = s
|
1593
|
+
orderType = 3
|
1594
|
+
elif t**3 == s: # (q^3, q)
|
1595
|
+
q = t
|
1596
|
+
orderType = 1
|
1597
|
+
else:
|
1598
|
+
raise ValueError(f"No generalised hexagon of order ({s}, {t}) is known")
|
1599
|
+
|
1600
|
+
if q == 1: # order (1, 1)
|
1601
|
+
from sage.graphs.generators.basic import CycleGraph
|
1602
|
+
return CycleGraph(6)
|
1603
|
+
|
1604
|
+
if not is_prime_power(q):
|
1605
|
+
raise ValueError(f"No generalised hexagon of order ({s}, {t}) is known")
|
1606
|
+
|
1607
|
+
if orderType == 0:
|
1608
|
+
# incidence graph of generalised 3-gon of order (q, q)
|
1609
|
+
PG2 = designs.ProjectiveGeometryDesign(2, 1, q)
|
1610
|
+
|
1611
|
+
edges = []
|
1612
|
+
for l in PG2.blocks():
|
1613
|
+
for p in l:
|
1614
|
+
sig_check()
|
1615
|
+
edges.append((p, tuple(l)))
|
1616
|
+
|
1617
|
+
G = Graph(edges, format='list_of_edges')
|
1618
|
+
G.name("Generalised hexagon of order (1, %d)" % q)
|
1619
|
+
return G
|
1620
|
+
|
1621
|
+
elif orderType == 1:
|
1622
|
+
# dual graph
|
1623
|
+
H = GeneralisedHexagonGraph(t, s)
|
1624
|
+
G = _line_graph_generalised_polygon(H)
|
1625
|
+
G.name("Generalised hexagon of order(%d, %d)" % (s, t))
|
1626
|
+
return G
|
1627
|
+
|
1628
|
+
elif orderType == 2:
|
1629
|
+
# we use the group G2(q)
|
1630
|
+
# if q == 2, then G2(2) is isomorphic to U3(3).2
|
1631
|
+
if q == 2:
|
1632
|
+
group = libgap.AtlasGroup("U3(3).2", libgap.NrMovedPoints, 63)
|
1633
|
+
G = Graph(libgap.Orbit(group, [1, 19], libgap.OnSets),
|
1634
|
+
format='list_of_edges')
|
1635
|
+
G.name("Generalised hexagon of order (%d, %d)" % (q, q))
|
1636
|
+
return G
|
1637
|
+
|
1638
|
+
elif q == 3: # we don't have permutation representation; so we build it
|
1639
|
+
matrixRep = libgap.AtlasGroup("G2(3)", libgap.Position, 7)
|
1640
|
+
e1 = vector(GF(3), [1, 0, 0, 0, 0, 0, 0])
|
1641
|
+
orb = libgap.Orbit(matrixRep, e1, libgap.OnLines)
|
1642
|
+
group = libgap.Action(matrixRep, orb, libgap.OnLines)
|
1643
|
+
|
1644
|
+
# now group is our permutation representation
|
1645
|
+
G = Graph(libgap.Orbit(group, [1, 52], libgap.OnSets),
|
1646
|
+
format='list_of_edges')
|
1647
|
+
G.name("Generalised hexagon of order (%d, %d)" % (q, q))
|
1648
|
+
return G
|
1649
|
+
|
1650
|
+
elif q <= 5:
|
1651
|
+
n = 1365 if q == 4 else 3906
|
1652
|
+
p = 43 if q == 4 else 185
|
1653
|
+
group = libgap.AtlasGroup("G2(%d)" % q, libgap.NrMovedPoints, n)
|
1654
|
+
|
1655
|
+
G = Graph(libgap.Orbit(group, [1, p], libgap.OnSets),
|
1656
|
+
format='list_of_edges')
|
1657
|
+
G.name("Generalised hexagon of order (%d, %d)" % (q, q))
|
1658
|
+
return G
|
1659
|
+
|
1660
|
+
else:
|
1661
|
+
raise NotImplementedError("Graph would be too big")
|
1662
|
+
|
1663
|
+
elif orderType == 3:
|
1664
|
+
if q > 3:
|
1665
|
+
raise NotImplementedError("Graph would be too big")
|
1666
|
+
|
1667
|
+
movedPoints = 819 if q == 2 else 26572
|
1668
|
+
group = libgap.AtlasGroup("3D4(%d)" % q, libgap.NrMovedPoints, movedPoints)
|
1669
|
+
|
1670
|
+
G = Graph(libgap.Orbit(group, [1, 2], libgap.OnSets),
|
1671
|
+
format='list_of_edges')
|
1672
|
+
G.name("Generalised hexagon of order (%d, %d)" % (q, q**3))
|
1673
|
+
return G
|
1674
|
+
|
1675
|
+
|
1676
|
+
def _extract_lines(G):
|
1677
|
+
r"""
|
1678
|
+
Return the set of lines from the point-graph of a generalised polygon.
|
1679
|
+
|
1680
|
+
In particular, given a graph `G` we return the set of singular lines:
|
1681
|
+
Let `(x,y)` be an edge, then `\{x,y\}^\bot^\bot` is a singular line.
|
1682
|
+
We define `x^\bot =` neighbours of `x` and `x` for `x` a vertex and
|
1683
|
+
`S^\bot =` intersection of `x^\bot` for all `x \in S`.
|
1684
|
+
|
1685
|
+
INPUT:
|
1686
|
+
|
1687
|
+
- ``G`` -- a graph
|
1688
|
+
|
1689
|
+
OUTPUT:
|
1690
|
+
|
1691
|
+
A list of tuples where each tuple represent a line via the vertices
|
1692
|
+
contained in that line.
|
1693
|
+
|
1694
|
+
EXAMPLES::
|
1695
|
+
|
1696
|
+
sage: # needs sage.libs.gap
|
1697
|
+
sage: from sage.graphs.generators.distance_regular import _extract_lines
|
1698
|
+
sage: G = graphs.GeneralisedHexagonGraph(1, 8)
|
1699
|
+
sage: lines = _extract_lines(G)
|
1700
|
+
sage: len(lines)
|
1701
|
+
657
|
1702
|
+
sage: type(lines)
|
1703
|
+
<class 'list'>
|
1704
|
+
sage: line = lines[0]
|
1705
|
+
sage: type(line)
|
1706
|
+
<class 'tuple'>
|
1707
|
+
sage: line[0] in G # the elements in the line are vertices
|
1708
|
+
True
|
1709
|
+
|
1710
|
+
REFERENCES:
|
1711
|
+
|
1712
|
+
See [BCN1989]_ pp. 200-205 for a discussion of distance-regular graphs from
|
1713
|
+
generalised polygons. See also [BCN1989]_ pp. 28, 29 for some theory about
|
1714
|
+
singular lines.
|
1715
|
+
"""
|
1716
|
+
lines = []
|
1717
|
+
edges = set(G.edges(labels=False, sort=False))
|
1718
|
+
|
1719
|
+
while edges:
|
1720
|
+
(x, y) = edges.pop()
|
1721
|
+
|
1722
|
+
# compute line
|
1723
|
+
botX = set(G.neighbors(x, closed=True))
|
1724
|
+
botY = set(G.neighbors(y, closed=True))
|
1725
|
+
bot1 = botX.intersection(botY)
|
1726
|
+
|
1727
|
+
b = bot1.pop()
|
1728
|
+
bot2 = frozenset(G.neighbors(b, closed=True))
|
1729
|
+
for v in bot1:
|
1730
|
+
sig_check()
|
1731
|
+
s = frozenset(G.neighbors(v, closed=True))
|
1732
|
+
bot2 = bot2.intersection(s)
|
1733
|
+
|
1734
|
+
# now bot2 is a line
|
1735
|
+
lines.append(tuple(bot2)) # we need tuple or GAP will complain later
|
1736
|
+
|
1737
|
+
# remove already handled edges
|
1738
|
+
for u, v in itertools.product(bot2, repeat=2):
|
1739
|
+
sig_check()
|
1740
|
+
try:
|
1741
|
+
edges.remove((u, v))
|
1742
|
+
except KeyError:
|
1743
|
+
pass # ignore this
|
1744
|
+
# end while edges
|
1745
|
+
|
1746
|
+
return lines
|
1747
|
+
|
1748
|
+
|
1749
|
+
def _line_graph_generalised_polygon(H):
|
1750
|
+
r"""
|
1751
|
+
Return the line-graph of the generalised polygon whose point-graph is `H`.
|
1752
|
+
|
1753
|
+
In particular, return the line-graph of the incidence structure defined
|
1754
|
+
by the singular lines of the graph `H`.
|
1755
|
+
|
1756
|
+
See also :func:`sage.graphs.generators.distance_regular._extract_lines`.
|
1757
|
+
|
1758
|
+
INPUT:
|
1759
|
+
|
1760
|
+
- ``H`` -- a graph
|
1761
|
+
|
1762
|
+
EXAMPLES::
|
1763
|
+
|
1764
|
+
sage: # needs sage.libs.gap
|
1765
|
+
sage: from sage.graphs.generators.distance_regular import (
|
1766
|
+
....: _line_graph_generalised_polygon)
|
1767
|
+
sage: G = graphs.GeneralisedHexagonGraph(1, 8)
|
1768
|
+
sage: H = _line_graph_generalised_polygon(G)
|
1769
|
+
sage: H.is_distance_regular(True)
|
1770
|
+
([16, 8, 8, None], [None, 1, 1, 2])
|
1771
|
+
sage: G = graphs.GeneralisedHexagonGraph(3, 3) # optional - gap_package_atlasrep internet
|
1772
|
+
sage: H = _line_graph_generalised_polygon(G) # optional - gap_package_atlasrep internet
|
1773
|
+
sage: G.is_isomorphic(H) # optional - gap_package_atlasrep internet
|
1774
|
+
True
|
1775
|
+
|
1776
|
+
REFERENCES:
|
1777
|
+
|
1778
|
+
See [BCN1989]_ pp. 200-205 for a discussion of distance-regular graphs from
|
1779
|
+
generalised polygons. See also [BCN1989]_ pp. 28, 29 for some theory about
|
1780
|
+
singular lines.
|
1781
|
+
"""
|
1782
|
+
lines = _extract_lines(H)
|
1783
|
+
|
1784
|
+
# get a map (point -> all lines incident to point)
|
1785
|
+
vToLines = {v: [] for v in H}
|
1786
|
+
for l in lines:
|
1787
|
+
for p in l:
|
1788
|
+
sig_check()
|
1789
|
+
vToLines[p].append(l)
|
1790
|
+
|
1791
|
+
edges = []
|
1792
|
+
for v in vToLines:
|
1793
|
+
lines = vToLines[v]
|
1794
|
+
for l1, l2 in itertools.combinations(lines, 2):
|
1795
|
+
sig_check()
|
1796
|
+
edges.append((l1, l2))
|
1797
|
+
|
1798
|
+
return Graph(edges, format='list_of_edges')
|
1799
|
+
|
1800
|
+
|
1801
|
+
def _intersection_array_from_graph(G):
|
1802
|
+
r"""
|
1803
|
+
Return the intersection array of the graph `G`.
|
1804
|
+
If `G` is not distance-regular, then return ``False``.
|
1805
|
+
|
1806
|
+
This is a simple wrapper around
|
1807
|
+
:meth:`sage.graphs.distances_all_pairs.is_distance_regular` to return a list
|
1808
|
+
instead of a pair of lists
|
1809
|
+
|
1810
|
+
INPUT:
|
1811
|
+
|
1812
|
+
- ``G`` -- a graph
|
1813
|
+
|
1814
|
+
EXAMPLES::
|
1815
|
+
|
1816
|
+
sage: from sage.graphs.generators.distance_regular import \
|
1817
|
+
....: _intersection_array_from_graph
|
1818
|
+
sage: _intersection_array_from_graph(graphs.FosterGraph()) # needs networkx
|
1819
|
+
[3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3]
|
1820
|
+
sage: graphs.FosterGraph().is_distance_regular(True) # needs networkx
|
1821
|
+
([3, 2, 2, 2, 2, 1, 1, 1, None], [None, 1, 1, 1, 1, 2, 2, 2, 3])
|
1822
|
+
sage: graphs.DartGraph().is_distance_regular()
|
1823
|
+
False
|
1824
|
+
sage: _intersection_array_from_graph(graphs.DartGraph())
|
1825
|
+
False
|
1826
|
+
|
1827
|
+
TESTS::
|
1828
|
+
|
1829
|
+
sage: from sage.graphs.generators.distance_regular import \
|
1830
|
+
....: _intersection_array_from_graph
|
1831
|
+
sage: _intersection_array_from_graph(Graph())
|
1832
|
+
[]
|
1833
|
+
sage: _intersection_array_from_graph(Graph(3))
|
1834
|
+
[]
|
1835
|
+
sage: _intersection_array_from_graph(graphs.CompleteGraph(7))
|
1836
|
+
[6, 1]
|
1837
|
+
"""
|
1838
|
+
t = G.is_distance_regular(True)
|
1839
|
+
if t is False:
|
1840
|
+
return False
|
1841
|
+
|
1842
|
+
return t[0][:-1] + t[1][1:]
|
1843
|
+
|
1844
|
+
|
1845
|
+
cdef enum ClassicalParametersGraph:
|
1846
|
+
NonExisting = 0,
|
1847
|
+
Johnson,
|
1848
|
+
Hamming,
|
1849
|
+
HalvedCube,
|
1850
|
+
UnitaryDualPolar,
|
1851
|
+
HermitianForms,
|
1852
|
+
GeneralisedHexagon,
|
1853
|
+
Grassmann,
|
1854
|
+
OrthogonalDualPolar1,
|
1855
|
+
SymplecticDualPolar,
|
1856
|
+
OrthogonalDualPolar2,
|
1857
|
+
UnitaryDualPolar1,
|
1858
|
+
UnitaryDualPolar2,
|
1859
|
+
Ustimenko,
|
1860
|
+
BilinearForms,
|
1861
|
+
AlternatingForms,
|
1862
|
+
LieE77,
|
1863
|
+
AffineE6
|
1864
|
+
|
1865
|
+
|
1866
|
+
def is_classical_parameters_graph(list array):
|
1867
|
+
r"""
|
1868
|
+
Return a tuple of parameters representing the array given. If such no tuple
|
1869
|
+
can be produced, it returns ``False``.
|
1870
|
+
|
1871
|
+
Given an intersection array, if it represents a family of distance-regular
|
1872
|
+
graphs with classical parameters, then this function returns a tuple
|
1873
|
+
consisting of the parameters `(d, b, \alpha, \beta)` and a fourth parameter
|
1874
|
+
which is the enum ``CalssicalParametersGraph`` indicating the family with
|
1875
|
+
the given intersection array.
|
1876
|
+
If the array does not belong to any classical parameter graph, then this
|
1877
|
+
function returns ``False``.
|
1878
|
+
If the array belongs to a sporadic graph rather than a family of graphs,
|
1879
|
+
then the function returns ``False``. This is to reduce the overlap with
|
1880
|
+
``sage.graphs.generators.distance_regular._sporadic_graph_database``.
|
1881
|
+
|
1882
|
+
|
1883
|
+
.. NOTE::
|
1884
|
+
|
1885
|
+
The array given as an input is expected to be an intersection array.
|
1886
|
+
If this is not the case, then some exception may be raised.
|
1887
|
+
|
1888
|
+
INPUT:
|
1889
|
+
|
1890
|
+
- ``array`` -- list; an intersection array
|
1891
|
+
|
1892
|
+
OUTPUT:
|
1893
|
+
|
1894
|
+
``False`` or a tuple ``(d, b, alpha, beta, gamma)``.
|
1895
|
+
|
1896
|
+
EXAMPLES::
|
1897
|
+
|
1898
|
+
sage: from sage.graphs.generators.distance_regular import \
|
1899
|
+
....: is_classical_parameters_graph
|
1900
|
+
sage: G = graphs.HammingGraph(5, 4)
|
1901
|
+
sage: G.is_distance_regular(True)
|
1902
|
+
([15, 12, 9, 6, 3, None], [None, 1, 2, 3, 4, 5])
|
1903
|
+
sage: is_classical_parameters_graph([15, 12, 9, 6, 3, 1, 2, 3, 4, 5]) # needs sage.combinat
|
1904
|
+
(5, 1, 0, 3, 2)
|
1905
|
+
|
1906
|
+
REFERENCES:
|
1907
|
+
|
1908
|
+
See [BCN1989]_ chapter 9 for a discussion of distance-regular graphs with
|
1909
|
+
classical parameters. See [BCN1989]_ chapter 6.2 for a method to compute
|
1910
|
+
the classical parameters of a graph. See also [VDKT2016]_ section 3.1.1.
|
1911
|
+
|
1912
|
+
TESTS::
|
1913
|
+
|
1914
|
+
sage: from sage.graphs.generators.distance_regular import \
|
1915
|
+
....: is_classical_parameters_graph
|
1916
|
+
sage: is_classical_parameters_graph([68, 64, 1, 17]) # srg not drg # needs sage.combinat
|
1917
|
+
False
|
1918
|
+
sage: G = graphs.GossetGraph() # sporadic classical parameters graph
|
1919
|
+
sage: G.is_distance_regular(True)
|
1920
|
+
([27, 10, 1, None], [None, 1, 10, 27])
|
1921
|
+
sage: is_classical_parameters_graph([27, 10, 1, 1, 10, 27]) # needs sage.combinat
|
1922
|
+
False
|
1923
|
+
"""
|
1924
|
+
from sage.misc.functional import log
|
1925
|
+
from sage.rings.integer_ring import ZZ
|
1926
|
+
from sage.arith.misc import is_prime_power
|
1927
|
+
from sage.combinat.q_analogues import q_binomial
|
1928
|
+
|
1929
|
+
def integral_log(const int x, const int b):
|
1930
|
+
# compute log_b(x) if is not a positive integer, return -1
|
1931
|
+
if x <= 0:
|
1932
|
+
return -1
|
1933
|
+
k = log(x, b)
|
1934
|
+
if k in ZZ and k > 0:
|
1935
|
+
return int(k)
|
1936
|
+
return -1
|
1937
|
+
|
1938
|
+
def check_parameters(int d, int b, int alpha, int beta, list arr):
|
1939
|
+
bs = [(q_binomial(d, 1, b) - q_binomial(i, 1, b)) *
|
1940
|
+
(beta - alpha * q_binomial(i, 1, b)) for i in range(d)]
|
1941
|
+
cs = [q_binomial(i, 1, b) * (1 + alpha*q_binomial(i-1, 1, b))
|
1942
|
+
for i in range(1, d+1)]
|
1943
|
+
return arr == bs + cs
|
1944
|
+
|
1945
|
+
def b_(i):
|
1946
|
+
if i == d:
|
1947
|
+
return 0
|
1948
|
+
return array[i]
|
1949
|
+
|
1950
|
+
def c_(i):
|
1951
|
+
if i == 0:
|
1952
|
+
return 0
|
1953
|
+
return array[d - 1 + i]
|
1954
|
+
|
1955
|
+
def a_(i):
|
1956
|
+
return b_(0) - b_(i) - c_(i)
|
1957
|
+
|
1958
|
+
if len(array) % 2:
|
1959
|
+
return False
|
1960
|
+
|
1961
|
+
d = len(array) // 2
|
1962
|
+
if d < 3:
|
1963
|
+
return False
|
1964
|
+
|
1965
|
+
# if array is classical parameters, then we have 2 cases:
|
1966
|
+
# 1. a_i != \lambda c_i for 2<= i <= d
|
1967
|
+
# 2. a_i == \lambda c_i for 0<= i <= d
|
1968
|
+
if all(a_(i) == a_(1) * c_(i) for i in range(2, d+1)):
|
1969
|
+
case = 2
|
1970
|
+
elif all(a_(i) != a_(1) * c_(i) for i in range(2, d+1)):
|
1971
|
+
case = 1
|
1972
|
+
else:
|
1973
|
+
return False
|
1974
|
+
|
1975
|
+
if case == 1:
|
1976
|
+
# b = (a_2 c_3 - c_2 a_3) / (a_1 c_3 - a_3)
|
1977
|
+
num = a_(2) * c_(3) - c_(2) * a_(3)
|
1978
|
+
den = a_(1) * c_(3) - a_(3)
|
1979
|
+
b = num / den
|
1980
|
+
if b in {0, -1}:
|
1981
|
+
return False
|
1982
|
+
|
1983
|
+
alpha = c_(2) / (1 + b) - 1
|
1984
|
+
beta = b_(0) / q_binomial(d, 1, b)
|
1985
|
+
|
1986
|
+
else: # case 2
|
1987
|
+
# b is either c_2 - 1 or - a_1 - 1
|
1988
|
+
# try c_2 - 1
|
1989
|
+
valid = True
|
1990
|
+
b = c_(2) - 1
|
1991
|
+
if b in {0, -1}:
|
1992
|
+
valid = False
|
1993
|
+
else:
|
1994
|
+
alpha = c_(2) / (1 + b) - 1
|
1995
|
+
beta = b_(0) / q_binomial(d, 1, b)
|
1996
|
+
valid = check_parameters(d, b, alpha, beta, array)
|
1997
|
+
|
1998
|
+
if not valid: # must have -a_1 - 1
|
1999
|
+
b = -a_(1) - 1
|
2000
|
+
if b in {0, -1}:
|
2001
|
+
return False # even this case is invalid
|
2002
|
+
|
2003
|
+
alpha = c_(2) / (1 + b) - 1
|
2004
|
+
beta = a_(1) + 1 - alpha*(q_binomial(d, 1, b) - 1)
|
2005
|
+
|
2006
|
+
if not check_parameters(d, b, alpha, beta, array):
|
2007
|
+
return False
|
2008
|
+
|
2009
|
+
gamma = ClassicalParametersGraph.NonExisting
|
2010
|
+
|
2011
|
+
if b == 1:
|
2012
|
+
if alpha == 1 and beta >= d: # since beta+d = n >= 2*d
|
2013
|
+
# Johnson Graph
|
2014
|
+
gamma = ClassicalParametersGraph.Johnson
|
2015
|
+
elif alpha == 0:
|
2016
|
+
# Hamming Graph
|
2017
|
+
gamma = ClassicalParametersGraph.Hamming
|
2018
|
+
elif alpha == 2 and (beta == 2*d + 1 or beta == 2*d - 1):
|
2019
|
+
# Halved cube graph
|
2020
|
+
gamma = ClassicalParametersGraph.HalvedCube
|
2021
|
+
else:
|
2022
|
+
return False # no other (unbounbded) drg exists with b = 1
|
2023
|
+
|
2024
|
+
elif b < 0 and is_prime_power(-b):
|
2025
|
+
if (alpha + 1 == (1 + b*b) / (1 + b) and
|
2026
|
+
beta + 1 == (1 - b**(d + 1)) / (1 + b)):
|
2027
|
+
# U(2d,r)
|
2028
|
+
gamma = ClassicalParametersGraph.UnitaryDualPolar1
|
2029
|
+
elif alpha + 1 == b and beta + 1 == - (b**d):
|
2030
|
+
gamma = ClassicalParametersGraph.HermitianForms
|
2031
|
+
elif (d == 3 and alpha + 1 == 1 / (1+b) and
|
2032
|
+
beta + 1 == q_binomial(3, 1, -b)):
|
2033
|
+
gamma = ClassicalParametersGraph.GeneralisedHexagon
|
2034
|
+
else:
|
2035
|
+
return False
|
2036
|
+
|
2037
|
+
if gamma != ClassicalParametersGraph.NonExisting:
|
2038
|
+
return (d, b, alpha, beta, gamma)
|
2039
|
+
|
2040
|
+
# all remaining cases need b to be a prime power
|
2041
|
+
(p, k) = is_prime_power(b, get_data=True)
|
2042
|
+
if k == 0: # b not a prime power
|
2043
|
+
return False
|
2044
|
+
r = p**(k//2) # will be used later
|
2045
|
+
|
2046
|
+
if alpha == b and integral_log((beta+1) * (b-1) + 1, b) >= d + 1:
|
2047
|
+
# we checked that beta + 1 = (b^(n-d+1) - 1)/(b - 1) for n >= 2d
|
2048
|
+
# Grassmann graph
|
2049
|
+
gamma = ClassicalParametersGraph.Grassmann
|
2050
|
+
|
2051
|
+
elif alpha == 0 and beta * beta in {1, b, b * b, b**3, b**4}:
|
2052
|
+
# checked beta in {b^0, b^(0.5), b, b^(1.5), b^2}
|
2053
|
+
# dual polar graphs
|
2054
|
+
if beta == 1:
|
2055
|
+
gamma = ClassicalParametersGraph.OrthogonalDualPolar1
|
2056
|
+
elif beta == b:
|
2057
|
+
gamma = ClassicalParametersGraph.SymplecticDualPolar
|
2058
|
+
elif beta == b * b:
|
2059
|
+
gamma = ClassicalParametersGraph.OrthogonalDualPolar2
|
2060
|
+
else:
|
2061
|
+
if k % 2 == 1:
|
2062
|
+
return False # we need b a square
|
2063
|
+
if beta == r**3:
|
2064
|
+
gamma = ClassicalParametersGraph.UnitaryDualPolar1
|
2065
|
+
elif beta == r:
|
2066
|
+
gamma = ClassicalParametersGraph.UnitaryDualPolar2
|
2067
|
+
|
2068
|
+
elif (k % 2 == 0 and alpha + 1 == q_binomial(3, 1, r) and
|
2069
|
+
beta + 1 in {q_binomial(2*d + 2, 1, r),
|
2070
|
+
q_binomial(2*d + 1, 1, r)}):
|
2071
|
+
gamma = ClassicalParametersGraph.Ustimenko
|
2072
|
+
|
2073
|
+
elif alpha + 1 == b and integral_log(beta + 1, b) >= d:
|
2074
|
+
gamma = ClassicalParametersGraph.BilinearForms
|
2075
|
+
|
2076
|
+
elif (k % 2 == 0 and alpha + 1 == b and
|
2077
|
+
beta + 1 in {r**(2*d - 1), r**(2*d + 1)}):
|
2078
|
+
gamma = ClassicalParametersGraph.AlternatingForms
|
2079
|
+
|
2080
|
+
elif (d == 3 and k % 4 == 0 and alpha + 1 == q_binomial(5, 1, p**(k//4)) and
|
2081
|
+
beta + 1 == q_binomial(10, 1, p**(k//4))):
|
2082
|
+
gamma = ClassicalParametersGraph.LieE77
|
2083
|
+
|
2084
|
+
elif d == 3 and k % 4 == 0 and alpha + 1 == b and beta + 1 == (p**(k//4))**9:
|
2085
|
+
gamma = ClassicalParametersGraph.AffineE6
|
2086
|
+
|
2087
|
+
if gamma == ClassicalParametersGraph.NonExisting:
|
2088
|
+
return False
|
2089
|
+
return (d, b, alpha, beta, gamma)
|
2090
|
+
|
2091
|
+
|
2092
|
+
def graph_with_classical_parameters(int d, int b, alpha_in, beta_in, int gamma):
|
2093
|
+
r"""
|
2094
|
+
Return the graph with the classical parameters given.
|
2095
|
+
|
2096
|
+
The last parameter ``gamma`` is meant to be an element of the enum
|
2097
|
+
``ClassicalParametersGraph`` used to identify the family of graphs to
|
2098
|
+
construct.
|
2099
|
+
In particular this function doesn't build any sporadic graph.
|
2100
|
+
To build such a graph use
|
2101
|
+
:func:`sage.graphs.generators.distance_regular.distance_regular_graph`.
|
2102
|
+
|
2103
|
+
INPUT:
|
2104
|
+
|
2105
|
+
- ``d``, ``b``, ``alpha_in``, ``beta_in`` -- numbers; the parameters of the
|
2106
|
+
graph; ``d`` and ``b`` must be integers
|
2107
|
+
|
2108
|
+
- ``gamma`` -- element of the enum ``ClassicalParametersGraph``
|
2109
|
+
|
2110
|
+
EXAMPLES::
|
2111
|
+
|
2112
|
+
sage: from sage.graphs.generators.distance_regular import *
|
2113
|
+
sage: graph_with_classical_parameters(3, 1, 1, 3, 1)
|
2114
|
+
Johnson graph with parameters 6,3: Graph on 20 vertices
|
2115
|
+
|
2116
|
+
The last parameter is very important as it takes precedence.
|
2117
|
+
This function will not check that the other four parameters match the correct
|
2118
|
+
family. Use
|
2119
|
+
:func:`sage.graphs.generators.distance_regular.is_classical_parameters_graph`
|
2120
|
+
to check the parameters::
|
2121
|
+
|
2122
|
+
sage: from sage.graphs.generators.distance_regular import *
|
2123
|
+
sage: graph_with_classical_parameters(3, 1, 1, 3, 2)
|
2124
|
+
Hamming Graph with parameters 3,4: Graph on 64 vertices
|
2125
|
+
sage: G = _; G.is_distance_regular(True)
|
2126
|
+
([9, 6, 3, None], [None, 1, 2, 3])
|
2127
|
+
sage: is_classical_parameters_graph([9, 6, 3, 1, 2, 3]) # needs sage.combinat
|
2128
|
+
(3, 1, 0, 3, 2)
|
2129
|
+
|
2130
|
+
Two families of graphs are not implemented yet::
|
2131
|
+
|
2132
|
+
sage: from sage.graphs.generators.distance_regular import *
|
2133
|
+
sage: graph_with_classical_parameters(3, 16, 15, 511, 17)
|
2134
|
+
Traceback (most recent call last):
|
2135
|
+
...
|
2136
|
+
NotImplementedError: Graph would be too big
|
2137
|
+
sage: graph_with_classical_parameters(3, 16, 30, 1022, 16)
|
2138
|
+
Traceback (most recent call last):
|
2139
|
+
...
|
2140
|
+
NotImplementedError: Graph would be too big
|
2141
|
+
|
2142
|
+
REFERENCES:
|
2143
|
+
|
2144
|
+
See [BCN1989]_ chapter 9 for a discussion of distance-regular graphs with
|
2145
|
+
classical parameters. See also [VDKT2016]_ section 3.1.1.
|
2146
|
+
|
2147
|
+
TESTS::
|
2148
|
+
|
2149
|
+
sage: graph_with_classical_parameters(3, 1, 2, 3, 3)
|
2150
|
+
Half 4 Cube: Graph on 8 vertices
|
2151
|
+
sage: graph_with_classical_parameters(3, 2, 0, 2, 9) # needs sage.libs.gap
|
2152
|
+
Symplectic Dual Polar Graph DSp(6, 2): Graph on 135 vertices
|
2153
|
+
sage: graph_with_classical_parameters(3, 2, 2, 14, 7) # long time # needs sage.symbolic
|
2154
|
+
Grassmann graph J_2(6, 3): Graph on 1395 vertices
|
2155
|
+
sage: graph_with_classical_parameters(3, -2, -2, 6, 6) # optional - gap_package_atlasrep internet
|
2156
|
+
Generalised hexagon of order (2, 8): Graph on 819 vertices
|
2157
|
+
"""
|
2158
|
+
from sage.rings.rational import Rational
|
2159
|
+
from sage.misc.functional import sqrt, log
|
2160
|
+
from sage.graphs.generators.families import JohnsonGraph, HammingGraph
|
2161
|
+
from sage.graphs.generators.classical_geometries import \
|
2162
|
+
UnitaryDualPolarGraph, OrthogonalDualPolarGraph, SymplecticDualPolarGraph
|
2163
|
+
|
2164
|
+
alpha = Rational(alpha_in)
|
2165
|
+
beta = Rational(beta_in)
|
2166
|
+
if alpha.is_integer():
|
2167
|
+
alpha = int(alpha)
|
2168
|
+
if beta.is_integer():
|
2169
|
+
beta = int(beta)
|
2170
|
+
|
2171
|
+
if gamma == ClassicalParametersGraph.Johnson:
|
2172
|
+
return JohnsonGraph(beta + d, d)
|
2173
|
+
|
2174
|
+
elif gamma == ClassicalParametersGraph.Hamming:
|
2175
|
+
return HammingGraph(d, beta + 1)
|
2176
|
+
|
2177
|
+
elif gamma == ClassicalParametersGraph.HalvedCube:
|
2178
|
+
a = 0 if beta == 2*d + 1 else 1
|
2179
|
+
return HalfCube(beta + a)
|
2180
|
+
|
2181
|
+
elif gamma == ClassicalParametersGraph.UnitaryDualPolar:
|
2182
|
+
return UnitaryDualPolarGraph(2 * d, -b)
|
2183
|
+
|
2184
|
+
elif gamma == ClassicalParametersGraph.HermitianForms:
|
2185
|
+
return HermitianFormsGraph(d, (-b)**2)
|
2186
|
+
|
2187
|
+
elif gamma == ClassicalParametersGraph.GeneralisedHexagon:
|
2188
|
+
q = -b
|
2189
|
+
return GeneralisedHexagonGraph(q, q**3)
|
2190
|
+
|
2191
|
+
elif gamma == ClassicalParametersGraph.Grassmann:
|
2192
|
+
n = int(log((beta + 1) * (b - 1) + 1, b)) + d - 1
|
2193
|
+
return GrassmannGraph(b, n, d)
|
2194
|
+
|
2195
|
+
elif gamma == ClassicalParametersGraph.OrthogonalDualPolar1:
|
2196
|
+
return OrthogonalDualPolarGraph(1, d, b)
|
2197
|
+
|
2198
|
+
elif gamma == ClassicalParametersGraph.SymplecticDualPolar:
|
2199
|
+
return SymplecticDualPolarGraph(2 * d, b)
|
2200
|
+
|
2201
|
+
elif gamma == ClassicalParametersGraph.OrthogonalDualPolar2:
|
2202
|
+
return OrthogonalDualPolarGraph(-1, d, b)
|
2203
|
+
|
2204
|
+
elif gamma == ClassicalParametersGraph.UnitaryDualPolar1:
|
2205
|
+
r = int(sqrt(b))
|
2206
|
+
return UnitaryDualPolarGraph(2*d + 1, r)
|
2207
|
+
|
2208
|
+
elif gamma == ClassicalParametersGraph.UnitaryDualPolar2:
|
2209
|
+
r = int(sqrt(b))
|
2210
|
+
return UnitaryDualPolarGraph(2 * d, r)
|
2211
|
+
|
2212
|
+
elif gamma == ClassicalParametersGraph.Ustimenko:
|
2213
|
+
q = int(sqrt(b))
|
2214
|
+
m = int(log((beta+1) * (q-1) + 1, q)) - 1
|
2215
|
+
UstimenkoGraph(m, q)
|
2216
|
+
|
2217
|
+
elif gamma == ClassicalParametersGraph.BilinearForms:
|
2218
|
+
e = int(log(beta + 1, b))
|
2219
|
+
return BilinearFormsGraph(d, e, b)
|
2220
|
+
|
2221
|
+
elif gamma == ClassicalParametersGraph.AlternatingForms:
|
2222
|
+
q = int(sqrt(b))
|
2223
|
+
a = 0 if beta + 1 == q**(2*d - 1) else 1
|
2224
|
+
return AlternatingFormsGraph(2*d + a, q)
|
2225
|
+
|
2226
|
+
elif (gamma == ClassicalParametersGraph.LieE77 or
|
2227
|
+
gamma == ClassicalParametersGraph.AffineE6):
|
2228
|
+
raise NotImplementedError("Graph would be too big")
|
2229
|
+
|
2230
|
+
raise ValueError("Incorrect family of graphs")
|
2231
|
+
|
2232
|
+
|
2233
|
+
def is_pseudo_partition_graph(list arr):
|
2234
|
+
r"""
|
2235
|
+
Return `(m, a)` if the intersection array given satisfies:
|
2236
|
+
`b_i = (m - i)(1 + a(m - 1 - i))` for `0 \leq i < d`
|
2237
|
+
`c_i = i(1 + a(i - 1))` for `0 \leq i < d`
|
2238
|
+
`c_d = (2d + 2 - m) d (1 + a(d - 1))` where `d` is the diameter of the graph.
|
2239
|
+
|
2240
|
+
If such pair `(m, a)` doesn't exist or the diameter is less than 3, then
|
2241
|
+
this function returns ``False``.
|
2242
|
+
|
2243
|
+
These graphs are called pseudo partition graphs in [BCN1989]_ chapter 6.3.
|
2244
|
+
|
2245
|
+
INPUT:
|
2246
|
+
|
2247
|
+
- ``arr`` -- list; intersection array
|
2248
|
+
|
2249
|
+
OUTPUT:
|
2250
|
+
|
2251
|
+
A pair `(m, a)` of integers or ``False`` if such pair doesn't exist.
|
2252
|
+
|
2253
|
+
EXAMPLES::
|
2254
|
+
|
2255
|
+
sage: from sage.graphs.generators.distance_regular import *
|
2256
|
+
sage: is_pseudo_partition_graph([36, 25, 16, 1, 4, 18])
|
2257
|
+
(6, 1)
|
2258
|
+
sage: pseudo_partition_graph(6, 1) # long time
|
2259
|
+
Folded Johnson graph with parameters 12,6: Graph on 462 vertices
|
2260
|
+
sage: _.is_distance_regular(True) # long time
|
2261
|
+
([36, 25, 16, None], [None, 1, 4, 18])
|
2262
|
+
|
2263
|
+
REFERENCE:
|
2264
|
+
|
2265
|
+
See [BCN1989]_ pp. 197, 198 or [VDKT2016]_ pp. 38, 39.
|
2266
|
+
|
2267
|
+
TESTS::
|
2268
|
+
|
2269
|
+
sage: from sage.graphs.generators.distance_regular import *
|
2270
|
+
sage: is_pseudo_partition_graph([])
|
2271
|
+
False
|
2272
|
+
sage: is_pseudo_partition_graph([36, 25, 16, 1, 0, 18])
|
2273
|
+
False
|
2274
|
+
sage: is_pseudo_partition_graph([217, 156, 105, 1, 12, 33])
|
2275
|
+
(7, 5)
|
2276
|
+
sage: pseudo_partition_graph(7, 5)
|
2277
|
+
Traceback (most recent call last):
|
2278
|
+
...
|
2279
|
+
ValueError: No known graph exists
|
2280
|
+
"""
|
2281
|
+
d = len(arr)
|
2282
|
+
if d % 2 != 0:
|
2283
|
+
return False
|
2284
|
+
|
2285
|
+
d = d // 2
|
2286
|
+
|
2287
|
+
if d < 3:
|
2288
|
+
return False
|
2289
|
+
|
2290
|
+
# c_2 = 2 (1+a)
|
2291
|
+
c2 = arr[d+1]
|
2292
|
+
if c2 % 2 != 0:
|
2293
|
+
return False
|
2294
|
+
a = c2//2 - 1
|
2295
|
+
|
2296
|
+
cd = arr[2*d - 1]
|
2297
|
+
K = d * (1+a * (d-1))
|
2298
|
+
if cd % K != 0:
|
2299
|
+
return False
|
2300
|
+
|
2301
|
+
gamma = cd // K
|
2302
|
+
m = 2*d + 2 - gamma
|
2303
|
+
|
2304
|
+
# we must have m = 2*d or 2*d +1
|
2305
|
+
if m not in {2*d, 2*d + 1}:
|
2306
|
+
return False
|
2307
|
+
|
2308
|
+
newArr = [(m-i) * (1 + a * (m-1-i)) for i in range(d)] + \
|
2309
|
+
[i * (1 + a * (i-1)) for i in range(1, d)] + \
|
2310
|
+
[(2*d + 2 - m) * d * (1 + a * (d-1))]
|
2311
|
+
|
2312
|
+
if arr == newArr:
|
2313
|
+
return (m, a)
|
2314
|
+
|
2315
|
+
return False
|
2316
|
+
|
2317
|
+
|
2318
|
+
def pseudo_partition_graph(int m, int a):
|
2319
|
+
r"""
|
2320
|
+
Return a pseudo partition graph with the given parameters.
|
2321
|
+
|
2322
|
+
A graph is a pseudo partition graph if it is distance-regular with
|
2323
|
+
diameter at least 3 and whose intersection numbers satisfy:
|
2324
|
+
`b_i = (m - i)(1 + a(m - 1 - i))` for `0 \leq i < d`
|
2325
|
+
`c_i = i(1 + a(i - 1))` for `0 \leq i < d`
|
2326
|
+
`c_d = (2d + 2 - m) d (1 + a(d - 1))` where `d` is the diameter of the graph.
|
2327
|
+
|
2328
|
+
INPUT:
|
2329
|
+
|
2330
|
+
- ``m``, ``a`` -- integers; parameters of the graph
|
2331
|
+
|
2332
|
+
EXAMPLES::
|
2333
|
+
|
2334
|
+
sage: from sage.graphs.generators.distance_regular import *
|
2335
|
+
sage: pseudo_partition_graph(6, 1) # long time
|
2336
|
+
Folded Johnson graph with parameters 12,6: Graph on 462 vertices
|
2337
|
+
|
2338
|
+
Not all graphs built with this function are pseudo partition graphs as
|
2339
|
+
intended by
|
2340
|
+
:func:`sage.graphs.generators.distance_regular.is_pseudo_partition_graph`,
|
2341
|
+
since that function requires the diameter to be at least 3::
|
2342
|
+
|
2343
|
+
sage: from sage.graphs.generators.distance_regular import *
|
2344
|
+
sage: pseudo_partition_graph(3, 1)
|
2345
|
+
Folded Johnson graph with parameters 6,3: Graph on 10 vertices
|
2346
|
+
sage: G=_; G.is_distance_regular(True)
|
2347
|
+
([9, None], [None, 1])
|
2348
|
+
sage: is_pseudo_partition_graph([9, 1])
|
2349
|
+
False
|
2350
|
+
|
2351
|
+
REFERENCES:
|
2352
|
+
|
2353
|
+
See [BCN1989]_ pp. 197, 198 or [VDKT2016]_ pp. 38, 39 for a discussion of
|
2354
|
+
known pseudo partition graphs.
|
2355
|
+
|
2356
|
+
TESTS::
|
2357
|
+
|
2358
|
+
sage: from sage.graphs.generators.distance_regular import *
|
2359
|
+
sage: pseudo_partition_graph(3, 3)
|
2360
|
+
Traceback (most recent call last):
|
2361
|
+
...
|
2362
|
+
ValueError: No known graph exists
|
2363
|
+
sage: pseudo_partition_graph(8, 0).is_distance_regular(True)
|
2364
|
+
([8, 7, 6, 5, None], [None, 1, 2, 3, 8])
|
2365
|
+
sage: pseudo_partition_graph(6, 2).is_distance_regular(True)
|
2366
|
+
([66, 45, 28, None], [None, 1, 6, 30])
|
2367
|
+
"""
|
2368
|
+
from sage.graphs.generators.families import JohnsonGraph, FoldedCubeGraph
|
2369
|
+
from sage.graphs.bipartite_graph import BipartiteGraph
|
2370
|
+
|
2371
|
+
if a == 0:
|
2372
|
+
return FoldedCubeGraph(m)
|
2373
|
+
elif a == 1:
|
2374
|
+
return JohnsonGraph(2 * m, m).folded_graph()
|
2375
|
+
elif a == 2:
|
2376
|
+
return BipartiteGraph(FoldedCubeGraph(2 * m)).project_left()
|
2377
|
+
|
2378
|
+
raise ValueError("No known graph exists")
|
2379
|
+
|
2380
|
+
|
2381
|
+
cdef enum NearPolygonGraph:
|
2382
|
+
RegularPolygon = 0,
|
2383
|
+
GeneralisedPolygon,
|
2384
|
+
OddGraph,
|
2385
|
+
DoubleOdd,
|
2386
|
+
DoubleGrassmann,
|
2387
|
+
FoldedCube,
|
2388
|
+
HammingGraph,
|
2389
|
+
DualPolarGraph
|
2390
|
+
|
2391
|
+
|
2392
|
+
def is_near_polygon(array):
|
2393
|
+
r"""
|
2394
|
+
Return a tuple of parameters which identify the near polygon graph with
|
2395
|
+
the given intersection array. If such tuple doesn't exist, return ``False``.
|
2396
|
+
|
2397
|
+
Note that ``array`` may be the intersection array of a near polygon, but if
|
2398
|
+
such graph has diameter less than 3, then this function will return
|
2399
|
+
``False``.
|
2400
|
+
|
2401
|
+
INPUT:
|
2402
|
+
|
2403
|
+
- ``array`` -- list; intersection array
|
2404
|
+
|
2405
|
+
OUTPUT:
|
2406
|
+
|
2407
|
+
The tuple has the form ``(id, params)`` where ``id`` is a value of the
|
2408
|
+
enum `NearPolygonGraph` which identify a family of graphs and ``params``
|
2409
|
+
are all parameters needed to construct the final graph.
|
2410
|
+
|
2411
|
+
EXAMPLES::
|
2412
|
+
|
2413
|
+
sage: from sage.graphs.generators.distance_regular import (
|
2414
|
+
....: is_near_polygon, near_polygon_graph)
|
2415
|
+
sage: is_near_polygon([7, 6, 6, 5, 5, 4, 1, 1, 2, 2, 3, 3]) # needs sage.combinat
|
2416
|
+
(2, 7)
|
2417
|
+
sage: near_polygon_graph(2, 7)
|
2418
|
+
Odd Graph with parameter 7: Graph on 1716 vertices
|
2419
|
+
sage: _.is_distance_regular(True)
|
2420
|
+
([7, 6, 6, 5, 5, 4, None], [None, 1, 1, 2, 2, 3, 3])
|
2421
|
+
|
2422
|
+
REFERENCES:
|
2423
|
+
|
2424
|
+
See [BCN1989]_ pp. 198-206 for some theory about near polygons as well as
|
2425
|
+
a list of known examples.
|
2426
|
+
|
2427
|
+
TESTS::
|
2428
|
+
|
2429
|
+
sage: # needs sage.combinat sage.libs.pari
|
2430
|
+
sage: from sage.graphs.generators.distance_regular import (
|
2431
|
+
....: is_near_polygon, near_polygon_graph)
|
2432
|
+
sage: is_near_polygon([7, 6, 6, 4, 4, 1, 1, 3, 3, 7])
|
2433
|
+
(4, (2, 2))
|
2434
|
+
sage: near_polygon_graph(4, (2, 2))
|
2435
|
+
Double Grassmann graph (5, 2, 2): Graph on 310 vertices
|
2436
|
+
sage: near_polygon_graph(*is_near_polygon([3, 2, 2, 1, 1, 3])) # needs sage.rings.finite_rings
|
2437
|
+
Generalised hexagon of order (1, 2): Graph on 14 vertices
|
2438
|
+
sage: is_near_polygon([16, 12, 8, 4, 1, 2, 3, 4])
|
2439
|
+
(6, (4, 5))
|
2440
|
+
sage: is_near_polygon([])
|
2441
|
+
False
|
2442
|
+
sage: is_near_polygon([25, 16, 9, 4, 1, 1, 4, 9, 16, 25]) # JohnsonGraph
|
2443
|
+
False
|
2444
|
+
"""
|
2445
|
+
from sage.arith.misc import is_prime_power
|
2446
|
+
from sage.combinat.q_analogues import q_binomial
|
2447
|
+
from sage.misc.functional import log
|
2448
|
+
|
2449
|
+
if len(array) % 2 != 0:
|
2450
|
+
return False
|
2451
|
+
|
2452
|
+
d = len(array) // 2
|
2453
|
+
|
2454
|
+
if d < 3:
|
2455
|
+
return False
|
2456
|
+
|
2457
|
+
k = array[0]
|
2458
|
+
l = k - array[1] - 1
|
2459
|
+
|
2460
|
+
if l < 0:
|
2461
|
+
return False
|
2462
|
+
|
2463
|
+
if (any(array[i] != k - (l + 1) * array[d - 1 + i] for i in range(1, d)) or
|
2464
|
+
k < (l+1) * array[2*d - 1]):
|
2465
|
+
return False
|
2466
|
+
|
2467
|
+
# additional checks
|
2468
|
+
if k < (l + 1) * array[2*d - 1] or k % (l + 1) != 0:
|
2469
|
+
return False
|
2470
|
+
|
2471
|
+
# check if it is known example
|
2472
|
+
if (k == (l + 1) * array[2*d - 1] and
|
2473
|
+
all(array[d + i] == 1 for i in range(d - 1)) and
|
2474
|
+
(l + 1 > 1 or array[2*d - 1] - 1 > 1)): # last 2 reject regular polygons
|
2475
|
+
# generalised polygon
|
2476
|
+
s = l + 1
|
2477
|
+
t = array[2*d - 1] - 1
|
2478
|
+
|
2479
|
+
if ((d == 3 and (s == 1 or t == 1) and is_prime_power(s * t)) or
|
2480
|
+
(d, s, t) in {(3, 2, 2), (3, 3, 3), (3, 4, 4), (3, 5, 5), (3, 2, 8),
|
2481
|
+
(3, 8, 2), (3, 3, 27), (3, 27, 3), (4, 2, 4), (4, 4, 2),
|
2482
|
+
(6, 1, 2), (6, 1, 3), (6, 1, 4), (6, 1, 5), (6, 2, 1),
|
2483
|
+
(6, 3, 1), (6, 4, 1), (6, 5, 1)}):
|
2484
|
+
return (NearPolygonGraph.GeneralisedPolygon, (d, s, t))
|
2485
|
+
|
2486
|
+
if d == 4 and (s == 1 or t == 1):
|
2487
|
+
q = s * t
|
2488
|
+
if strongly_regular_graph((q + 1) * (q*q + 1), q * (q + 1), q - 1, q + 1,
|
2489
|
+
existence=True):
|
2490
|
+
return (NearPolygonGraph.GeneralisedPolygon, (d, s, t))
|
2491
|
+
|
2492
|
+
# otherwise not known generalised polygon
|
2493
|
+
return False
|
2494
|
+
|
2495
|
+
n = 2 * d if k == (l + 1) * array[2*d - 1] else 2*d + 1
|
2496
|
+
|
2497
|
+
if (k == 2 and l == 0 and all(array[d + i] == 1 for i in range(d - 1)) and
|
2498
|
+
array[2*d - 1] in {1, 2}):
|
2499
|
+
return (NearPolygonGraph.RegularPolygon, 2*d + 2 - array[2*d - 1])
|
2500
|
+
|
2501
|
+
if (l == 0 and k == d + 1 and n == 2*d + 1 and
|
2502
|
+
all(array[d + i] == (i + 2) // 2 for i in range(d))):
|
2503
|
+
return (NearPolygonGraph.OddGraph, d + 1)
|
2504
|
+
|
2505
|
+
if (l == 0 and k == n and all(array[d - 1 + i] == i for i in range(1, d))
|
2506
|
+
and array[2*d - 1] == d * (2*d + 2 - n)):
|
2507
|
+
return (NearPolygonGraph.FoldedCube, k)
|
2508
|
+
|
2509
|
+
if (l == 0 and n == 2 * d and d % 2 == 1 and (d - 1) // 2 + 1 == k and
|
2510
|
+
all(array[d - 1 + i] == (i + 1) // 2 for i in range(1, d + 1))):
|
2511
|
+
return (NearPolygonGraph.DoubleOdd, k - 1)
|
2512
|
+
|
2513
|
+
if (l == 0 and n == 2 * d and d % 2 == 1 and
|
2514
|
+
is_prime_power(array[d + 2] - 1) and
|
2515
|
+
all(array[d - 1 + i] == q_binomial((i + 1) // 2, 1, array[d + 2] - 1)
|
2516
|
+
for i in range(1, d + 1)) and
|
2517
|
+
k == q_binomial((d - 1) // 2 + 1, 1, array[d + 2] - 1)):
|
2518
|
+
return (NearPolygonGraph.DoubleGrassmann, (array[d + 2] - 1, (d - 1) // 2))
|
2519
|
+
|
2520
|
+
if (n == 2 * d and k == (l+1) * d and
|
2521
|
+
all(array[d - 1 + i] == i for i in range(1, d + 1))):
|
2522
|
+
return (NearPolygonGraph.HammingGraph, (d, l + 2))
|
2523
|
+
|
2524
|
+
if (n == 2 * d and is_prime_power(array[d + 1] - 1) and
|
2525
|
+
(l + 1) in [(array[d + 1] - 1) ** e for e in [0, 0.5, 1, 1.5, 2]] and
|
2526
|
+
k == (l + 1) * q_binomial(d, 1, array[d + 1] - 1) and
|
2527
|
+
all(array[d - 1 + i] == q_binomial(i, 1, array[d + 1] - 1)
|
2528
|
+
for i in range(1, d + 1))):
|
2529
|
+
return (NearPolygonGraph.DualPolarGraph, (d, array[d + 1] - 1,
|
2530
|
+
log(l + 1, array[d + 1] - 1)))
|
2531
|
+
|
2532
|
+
# otherwise we don't know the near polygon
|
2533
|
+
return False
|
2534
|
+
|
2535
|
+
|
2536
|
+
def near_polygon_graph(family, params):
|
2537
|
+
r"""
|
2538
|
+
Return the near polygon graph with the given parameters.
|
2539
|
+
|
2540
|
+
The input is expected to be the result of the function
|
2541
|
+
:func:`sage.graphs.generators.distance_regular.is_near_polygon`.
|
2542
|
+
|
2543
|
+
INPUT:
|
2544
|
+
|
2545
|
+
- ``family`` -- integer; an element of the enum ``NearPolygonGraph``
|
2546
|
+
|
2547
|
+
- ``params`` -- integer or tuple; the parameters needed to construct a graph
|
2548
|
+
of the family ``family``
|
2549
|
+
|
2550
|
+
EXAMPLES::
|
2551
|
+
|
2552
|
+
sage: from sage.graphs.generators.distance_regular import is_near_polygon, near_polygon_graph
|
2553
|
+
sage: near_polygon_graph(*is_near_polygon( # needs sage.combinat
|
2554
|
+
....: [6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6]))
|
2555
|
+
Bipartite double of Odd graph on a set of 11 elements: Graph on 924 vertices
|
2556
|
+
sage: G=_; G.is_distance_regular(True) # needs sage.combinat
|
2557
|
+
([6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, None],
|
2558
|
+
[None, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6])
|
2559
|
+
|
2560
|
+
REFERENCES:
|
2561
|
+
|
2562
|
+
See [BCN1989]_ pp. 198-206 for some theory about near polygons as well as
|
2563
|
+
a list of known examples.
|
2564
|
+
|
2565
|
+
TESTS::
|
2566
|
+
|
2567
|
+
sage: # needs sage.combinat
|
2568
|
+
sage: near_polygon_graph(12, 9)
|
2569
|
+
Traceback (most recent call last):
|
2570
|
+
...
|
2571
|
+
ValueError: No known near polygons with the given parameters
|
2572
|
+
sage: is_near_polygon([2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2])
|
2573
|
+
(0, 12)
|
2574
|
+
sage: near_polygon_graph((0, 12))
|
2575
|
+
Traceback (most recent call last):
|
2576
|
+
...
|
2577
|
+
TypeError: ...near_polygon_graph() takes exactly 2 positional arguments (1 given)
|
2578
|
+
sage: near_polygon_graph(0, 12)
|
2579
|
+
Cycle graph: Graph on 12 vertices
|
2580
|
+
sage: near_polygon_graph(*is_near_polygon([8, 7, 6, 5, 1, 2, 3, 8]))
|
2581
|
+
Folded Cube Graph: Graph on 128 vertices
|
2582
|
+
"""
|
2583
|
+
|
2584
|
+
if family == NearPolygonGraph.RegularPolygon:
|
2585
|
+
from sage.graphs.generators.basic import CycleGraph
|
2586
|
+
return CycleGraph(params)
|
2587
|
+
|
2588
|
+
if family == NearPolygonGraph.GeneralisedPolygon:
|
2589
|
+
d, s, t = params
|
2590
|
+
if d == 3:
|
2591
|
+
return GeneralisedHexagonGraph(s, t)
|
2592
|
+
if d == 4:
|
2593
|
+
return GeneralisedOctagonGraph(s, t)
|
2594
|
+
if d == 6:
|
2595
|
+
return GeneralisedDodecagonGraph(s, t)
|
2596
|
+
|
2597
|
+
if family == NearPolygonGraph.OddGraph:
|
2598
|
+
from sage.graphs.generators.families import OddGraph
|
2599
|
+
return OddGraph(params)
|
2600
|
+
|
2601
|
+
if family == NearPolygonGraph.DoubleOdd:
|
2602
|
+
return DoubleOddGraph(params)
|
2603
|
+
|
2604
|
+
if family == NearPolygonGraph.DoubleGrassmann:
|
2605
|
+
return DoubleGrassmannGraph(*params)
|
2606
|
+
|
2607
|
+
if family == NearPolygonGraph.FoldedCube:
|
2608
|
+
from sage.graphs.generators.families import FoldedCubeGraph
|
2609
|
+
return FoldedCubeGraph(params)
|
2610
|
+
|
2611
|
+
if family == NearPolygonGraph.HammingGraph:
|
2612
|
+
from sage.graphs.generators.families import HammingGraph
|
2613
|
+
return HammingGraph(*params)
|
2614
|
+
|
2615
|
+
if family == NearPolygonGraph.DualPolarGraph:
|
2616
|
+
from sage.graphs.generators.classical_geometries import (
|
2617
|
+
UnitaryDualPolarGraph,
|
2618
|
+
OrthogonalDualPolarGraph,
|
2619
|
+
SymplecticDualPolarGraph)
|
2620
|
+
|
2621
|
+
d, q, e = params
|
2622
|
+
if e == 0:
|
2623
|
+
return OrthogonalDualPolarGraph(1, d, q)
|
2624
|
+
if e == 0.5:
|
2625
|
+
return UnitaryDualPolarGraph(2 * d, int(q**0.5))
|
2626
|
+
if e == 1:
|
2627
|
+
return SymplecticDualPolarGraph(2 * d, q)
|
2628
|
+
if e == 1.5:
|
2629
|
+
return UnitaryDualPolarGraph(2*d + 1, int(q**0.5))
|
2630
|
+
if e == 2:
|
2631
|
+
return OrthogonalDualPolarGraph(-1, d, q)
|
2632
|
+
|
2633
|
+
raise ValueError("No known near polygons with the given parameters")
|
2634
|
+
|
2635
|
+
|
2636
|
+
# dictionary intersection_array (as tuple) -> construction
|
2637
|
+
# of sporadic distance-regular graphs
|
2638
|
+
_sporadic_graph_database = {
|
2639
|
+
(3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3): FosterGraph,
|
2640
|
+
(7, 6, 4, 4, 4, 1, 1, 1, 1, 1, 1, 2, 4, 4, 6, 7): IvanovIvanovFaradjevGraph,
|
2641
|
+
(3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3): BiggsSmithGraph,
|
2642
|
+
(22, 21, 20, 16, 6, 2, 1, 1, 2, 6, 16, 20, 21, 22): lambda:
|
2643
|
+
codes.GolayCode(GF(2), False).punctured([0]).cosetGraph().bipartite_double(),
|
2644
|
+
(23, 22, 21, 20, 3, 2, 1, 1, 2, 3, 20, 21, 22, 23): lambda:
|
2645
|
+
codes.GolayCode(GF(2), False).cosetGraph().bipartite_double(),
|
2646
|
+
(21, 20, 16, 6, 2, 1, 1, 2, 6, 16, 20, 21):
|
2647
|
+
shortened_00_11_binary_Golay_code_graph,
|
2648
|
+
(21, 20, 16, 9, 2, 1, 1, 2, 3, 16, 20, 21):
|
2649
|
+
shortened_000_111_extended_binary_Golay_code_graph,
|
2650
|
+
(22, 21, 20, 3, 2, 1, 1, 2, 3, 20, 21, 22): lambda:
|
2651
|
+
codes.GolayCode(GF(2), extended=False).shortened([0]).cosetGraph(),
|
2652
|
+
(3, 2, 1, 1, 1, 1, 1, 1, 2, 3): DodecahedralGraph,
|
2653
|
+
(22, 20, 18, 2, 1, 1, 2, 9, 20, 22): lambda:
|
2654
|
+
codes.GolayCode(GF(3)).shortened([0]).cosetGraph(),
|
2655
|
+
(7, 6, 6, 1, 1, 1, 1, 6, 6, 7): lambda:
|
2656
|
+
HoffmanSingletonGraph().bipartite_double(),
|
2657
|
+
(10, 9, 8, 2, 1, 1, 2, 8, 9, 10): lambda:
|
2658
|
+
SimsGewirtzGraph().bipartite_double(),
|
2659
|
+
(16, 15, 12, 4, 1, 1, 4, 12, 15, 16): lambda:
|
2660
|
+
strongly_regular_graph(77, 16, 0, check=False).bipartite_double(),
|
2661
|
+
(22, 21, 16, 6, 1, 1, 6, 16, 21, 22): lambda:
|
2662
|
+
HigmanSimsGraph().bipartite_double(),
|
2663
|
+
(3, 2, 2, 1, 1, 1, 1, 2): CoxeterGraph,
|
2664
|
+
(6, 5, 5, 4, 1, 1, 2, 6): vanLintSchrijverGraph,
|
2665
|
+
(7, 6, 4, 4, 1, 1, 1, 6): DoublyTruncatedWittGraph,
|
2666
|
+
(9, 8, 6, 3, 1, 1, 3, 8): distance_3_doubly_truncated_Golay_code_graph,
|
2667
|
+
(10, 8, 8, 2, 1, 1, 4, 5): J2Graph,
|
2668
|
+
(11, 10, 6, 1, 1, 1, 5, 11): LivingstoneGraph,
|
2669
|
+
(5, 4, 1, 1, 1, 1, 4, 5): WellsGraph,
|
2670
|
+
(6, 4, 2, 1, 1, 1, 4, 6): FosterGraph3S6,
|
2671
|
+
(10, 6, 4, 1, 1, 2, 6, 10): ConwaySmith_for_3S7,
|
2672
|
+
(20, 18, 4, 1, 1, 2, 18, 20): lambda:
|
2673
|
+
codes.GolayCode(GF(3), extended=False).shortened([0]).cosetGraph(),
|
2674
|
+
(45, 32, 12, 1, 1, 6, 32, 45): locally_GQ42_distance_transitive_graph,
|
2675
|
+
(117, 80, 24, 1, 1, 12, 80, 117): graph_3O73,
|
2676
|
+
(22, 21, 20, 1, 2, 6): lambda:
|
2677
|
+
codes.GolayCode(GF(2), extended=False).punctured([0]).cosetGraph(),
|
2678
|
+
(23, 22, 21, 1, 2, 3): lambda:
|
2679
|
+
codes.GolayCode(GF(2), extended=False).cosetGraph(),
|
2680
|
+
(24, 23, 22, 21, 1, 2, 3, 24): lambda: codes.GolayCode(GF(2)).cosetGraph(),
|
2681
|
+
(12, 11, 10, 7, 1, 2, 5, 12): LeonardGraph,
|
2682
|
+
(15, 14, 10, 3, 1, 5, 12, 15): cocliques_HoffmannSingleton,
|
2683
|
+
(27, 10, 1, 1, 10, 27): GossetGraph,
|
2684
|
+
(30, 28, 24, 1, 3, 15): LargeWittGraph,
|
2685
|
+
(15, 14, 12, 1, 1, 9): TruncatedWittGraph,
|
2686
|
+
(24, 22, 20, 1, 2, 12): lambda: codes.GolayCode(GF(3)).cosetGraph(),
|
2687
|
+
(21, 20, 16, 1, 2, 12): lambda:
|
2688
|
+
codes.GolayCode(GF(2), extended=False).punctured([0, 1]).cosetGraph()
|
2689
|
+
}
|
2690
|
+
|
2691
|
+
_infinite_families_database = [
|
2692
|
+
(is_classical_parameters_graph, graph_with_classical_parameters),
|
2693
|
+
(is_pseudo_partition_graph, pseudo_partition_graph),
|
2694
|
+
(is_near_polygon, near_polygon_graph),
|
2695
|
+
(is_from_GQ_spread, graph_from_GQ_spread),
|
2696
|
+
]
|
2697
|
+
|
2698
|
+
|
2699
|
+
def distance_regular_graph(list arr, existence=False, check=True):
|
2700
|
+
r"""
|
2701
|
+
Return a distance-regular graph with the intersection array given.
|
2702
|
+
|
2703
|
+
INPUT:
|
2704
|
+
|
2705
|
+
- ``arr`` -- list; intersection array of the graph
|
2706
|
+
|
2707
|
+
- ``existence`` -- boolean (optional); instead of building the graph return:
|
2708
|
+
|
2709
|
+
- ``True`` -- if a graph with the given intersection array exists;
|
2710
|
+
|
2711
|
+
- ``False`` -- if there is no graph with the given intersection array;
|
2712
|
+
|
2713
|
+
- ``Unknown`` -- if Sage doesn't know if such a graph exists
|
2714
|
+
|
2715
|
+
- ``check`` -- boolean (default: ``True``); if ``True``, then checks that the result
|
2716
|
+
of this function has the given intersection array
|
2717
|
+
|
2718
|
+
EXAMPLES::
|
2719
|
+
|
2720
|
+
sage: graphs.distance_regular_graph([21,20,16,1,2,12], existence=True)
|
2721
|
+
True
|
2722
|
+
sage: G = graphs.distance_regular_graph([12,11,10,7,1,2,5,12], check=False) # needs cliquer sage.combinat sage.modules
|
2723
|
+
sage: G.is_distance_regular(True) # needs cliquer sage.combinat sage.modules
|
2724
|
+
([12, 11, 10, 7, None], [None, 1, 2, 5, 12])
|
2725
|
+
|
2726
|
+
REFERENCES:
|
2727
|
+
|
2728
|
+
See [BCN1989]_ and [VDKT2016]_.
|
2729
|
+
|
2730
|
+
TESTS::
|
2731
|
+
|
2732
|
+
sage: graphs.distance_regular_graph([3, 2, 2, 1, 1, 1, 1, 2, 2, 3], # needs sage.combinat
|
2733
|
+
....: existence=True)
|
2734
|
+
True
|
2735
|
+
sage: graphs.distance_regular_graph([3, 2, 2, 1, 2, 1, 1, 2, 2, 3],
|
2736
|
+
....: existence=True)
|
2737
|
+
False
|
2738
|
+
sage: graphs.distance_regular_graph([18, 16, 16, 1, 1, 9]) # optional - internet gap_package_atlasrep
|
2739
|
+
Generalised hexagon of order (2, 8): Graph on 819 vertices
|
2740
|
+
|
2741
|
+
sage: # needs sage.combinat
|
2742
|
+
sage: graphs.distance_regular_graph([14, 12, 10, 8, 6, 4, 2,
|
2743
|
+
....: 1, 2, 3, 4, 5, 6, 7])
|
2744
|
+
Hamming Graph with parameters 7,3: Graph on 2187 vertices
|
2745
|
+
sage: graphs.distance_regular_graph([66, 45, 28, 1, 6, 30])
|
2746
|
+
Graph on 1024 vertices
|
2747
|
+
sage: graphs.distance_regular_graph([6,5,5,5,1,1,1,6]) # optional - database_graphs
|
2748
|
+
Generalised octagon of order (1, 5): Graph on 312 vertices
|
2749
|
+
sage: graphs.distance_regular_graph([64, 60, 1, 1, 15, 64], check=True) # needs sage.libs.gap
|
2750
|
+
Graph on 325 vertices
|
2751
|
+
"""
|
2752
|
+
from sage.misc.unknown import Unknown
|
2753
|
+
from sage.categories.sets_cat import EmptySetError
|
2754
|
+
|
2755
|
+
# check if drg module is installed
|
2756
|
+
try:
|
2757
|
+
import drg
|
2758
|
+
from drg import InfeasibleError
|
2759
|
+
drgModule = True
|
2760
|
+
except ModuleNotFoundError:
|
2761
|
+
drgModule = False
|
2762
|
+
|
2763
|
+
def result(G):
|
2764
|
+
if check:
|
2765
|
+
array = _intersection_array_from_graph(G)
|
2766
|
+
if array != arr:
|
2767
|
+
raise RuntimeError(("Sage built the wrong distance-regular "
|
2768
|
+
f"graph; expected {arr}, result {array}"))
|
2769
|
+
return G
|
2770
|
+
|
2771
|
+
def is_iterable(obj):
|
2772
|
+
try:
|
2773
|
+
iter(obj)
|
2774
|
+
return True
|
2775
|
+
except TypeError:
|
2776
|
+
return False
|
2777
|
+
|
2778
|
+
n = len(arr)
|
2779
|
+
d = n // 2
|
2780
|
+
|
2781
|
+
# check that arr makes sense:
|
2782
|
+
if drgModule:
|
2783
|
+
try:
|
2784
|
+
parameters = drg.DRGParameters(arr[:d], arr[d:])
|
2785
|
+
except (AssertionError, InfeasibleError, TypeError) as err:
|
2786
|
+
if existence:
|
2787
|
+
return False
|
2788
|
+
raise EmptySetError(("No distance-regular graphs with "
|
2789
|
+
f"parameters {arr} exists; error: {err}"))
|
2790
|
+
else:
|
2791
|
+
# basic checks
|
2792
|
+
if len(arr) % 2 == 1 or any([i <= 0 for i in arr]) or \
|
2793
|
+
any([x != int(x) for x in arr]) or \
|
2794
|
+
any([(arr[i] - arr[i + 1]) < 0 for i in range(d - 1)]) or \
|
2795
|
+
any([(arr[d + i + 1] - arr[d + i]) < 0 for i in range(d - 1)]):
|
2796
|
+
if existence:
|
2797
|
+
return False
|
2798
|
+
raise EmptySetError(("No distance-regular graphs with "
|
2799
|
+
f"parameters {arr} exists"))
|
2800
|
+
|
2801
|
+
# handle diameter < 3
|
2802
|
+
if d == 1 and arr[1] == 1:
|
2803
|
+
if existence:
|
2804
|
+
return True
|
2805
|
+
from sage.graphs.generators.basic import CompleteGraph
|
2806
|
+
return result(CompleteGraph(arr[0] + 1))
|
2807
|
+
|
2808
|
+
if d == 2:
|
2809
|
+
k = arr[0]
|
2810
|
+
mu = arr[3]
|
2811
|
+
l = k - arr[1] - 1 # a1 = k - b1 - c1
|
2812
|
+
v = (k * (k - l - 1)) // mu + k + 1
|
2813
|
+
|
2814
|
+
if existence:
|
2815
|
+
return strongly_regular_graph(v, k, l, mu, existence=True)
|
2816
|
+
return result(strongly_regular_graph(v, k, l, mu))
|
2817
|
+
|
2818
|
+
t = tuple(arr)
|
2819
|
+
if t in _sporadic_graph_database:
|
2820
|
+
if existence:
|
2821
|
+
return True
|
2822
|
+
return result(_sporadic_graph_database[t]())
|
2823
|
+
|
2824
|
+
for (f, g) in _infinite_families_database:
|
2825
|
+
t = f(arr)
|
2826
|
+
if t is not False:
|
2827
|
+
if existence:
|
2828
|
+
return True
|
2829
|
+
|
2830
|
+
G = g(*t) if is_iterable(t) else g(t)
|
2831
|
+
return result(G)
|
2832
|
+
|
2833
|
+
# now try drg feasibility
|
2834
|
+
if drgModule:
|
2835
|
+
try:
|
2836
|
+
parameters.check_feasible()
|
2837
|
+
except (InfeasibleError, TypeError, AssertionError) as err:
|
2838
|
+
if existence:
|
2839
|
+
return False
|
2840
|
+
raise EmptySetError(("No distance-regular graphs with "
|
2841
|
+
f"parameters {arr} exists; reason: {err}"))
|
2842
|
+
|
2843
|
+
if existence:
|
2844
|
+
return Unknown
|
2845
|
+
raise RuntimeError(
|
2846
|
+
f"No distance-regular graph with intersection array {arr} known")
|