passagemath-graphs 10.6.1rc1__cp310-cp310-musllinux_1_2_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
  2. passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
  3. passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
  4. passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
  5. passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
  6. passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
  7. passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
  8. sage/all__sagemath_graphs.py +39 -0
  9. sage/combinat/abstract_tree.py +2723 -0
  10. sage/combinat/all__sagemath_graphs.py +34 -0
  11. sage/combinat/binary_tree.py +5306 -0
  12. sage/combinat/cluster_algebra_quiver/all.py +22 -0
  13. sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
  14. sage/combinat/cluster_algebra_quiver/interact.py +124 -0
  15. sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
  16. sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
  17. sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
  18. sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
  19. sage/combinat/designs/MOLS_handbook_data.py +570 -0
  20. sage/combinat/designs/all.py +58 -0
  21. sage/combinat/designs/bibd.py +1655 -0
  22. sage/combinat/designs/block_design.py +1071 -0
  23. sage/combinat/designs/covering_array.py +269 -0
  24. sage/combinat/designs/covering_design.py +530 -0
  25. sage/combinat/designs/database.py +5615 -0
  26. sage/combinat/designs/design_catalog.py +122 -0
  27. sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  28. sage/combinat/designs/designs_pyx.pxd +21 -0
  29. sage/combinat/designs/designs_pyx.pyx +993 -0
  30. sage/combinat/designs/difference_family.py +3951 -0
  31. sage/combinat/designs/difference_matrices.py +279 -0
  32. sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  33. sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
  34. sage/combinat/designs/ext_rep.py +1064 -0
  35. sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
  36. sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
  37. sage/combinat/designs/group_divisible_designs.py +361 -0
  38. sage/combinat/designs/incidence_structures.py +2357 -0
  39. sage/combinat/designs/latin_squares.py +581 -0
  40. sage/combinat/designs/orthogonal_arrays.py +2244 -0
  41. sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
  42. sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
  43. sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
  44. sage/combinat/designs/resolvable_bibd.py +815 -0
  45. sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
  46. sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
  47. sage/combinat/designs/subhypergraph_search.pyx +530 -0
  48. sage/combinat/designs/twographs.py +306 -0
  49. sage/combinat/finite_state_machine.py +14874 -0
  50. sage/combinat/finite_state_machine_generators.py +2006 -0
  51. sage/combinat/graph_path.py +448 -0
  52. sage/combinat/interval_posets.py +3908 -0
  53. sage/combinat/nu_tamari_lattice.py +269 -0
  54. sage/combinat/ordered_tree.py +1446 -0
  55. sage/combinat/posets/all.py +46 -0
  56. sage/combinat/posets/bubble_shuffle.py +247 -0
  57. sage/combinat/posets/cartesian_product.py +493 -0
  58. sage/combinat/posets/d_complete.py +182 -0
  59. sage/combinat/posets/elements.py +273 -0
  60. sage/combinat/posets/forest.py +30 -0
  61. sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
  62. sage/combinat/posets/hasse_cython.pyx +174 -0
  63. sage/combinat/posets/hasse_diagram.py +3672 -0
  64. sage/combinat/posets/hochschild_lattice.py +158 -0
  65. sage/combinat/posets/incidence_algebras.py +794 -0
  66. sage/combinat/posets/lattices.py +5117 -0
  67. sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
  68. sage/combinat/posets/linear_extension_iterator.pyx +292 -0
  69. sage/combinat/posets/linear_extensions.py +1037 -0
  70. sage/combinat/posets/mobile.py +275 -0
  71. sage/combinat/posets/moebius_algebra.py +776 -0
  72. sage/combinat/posets/poset_examples.py +2178 -0
  73. sage/combinat/posets/posets.py +9360 -0
  74. sage/combinat/rooted_tree.py +1070 -0
  75. sage/combinat/shard_order.py +239 -0
  76. sage/combinat/tamari_lattices.py +384 -0
  77. sage/combinat/yang_baxter_graph.py +923 -0
  78. sage/databases/all__sagemath_graphs.py +1 -0
  79. sage/databases/knotinfo_db.py +1231 -0
  80. sage/ext_data/all__sagemath_graphs.py +1 -0
  81. sage/ext_data/graphs/graph_plot_js.html +330 -0
  82. sage/ext_data/kenzo/CP2.txt +45 -0
  83. sage/ext_data/kenzo/CP3.txt +349 -0
  84. sage/ext_data/kenzo/CP4.txt +4774 -0
  85. sage/ext_data/kenzo/README.txt +49 -0
  86. sage/ext_data/kenzo/S4.txt +20 -0
  87. sage/graphs/all.py +42 -0
  88. sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
  89. sage/graphs/asteroidal_triples.pyx +320 -0
  90. sage/graphs/base/all.py +1 -0
  91. sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  92. sage/graphs/base/boost_graph.pxd +106 -0
  93. sage/graphs/base/boost_graph.pyx +3045 -0
  94. sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  95. sage/graphs/base/c_graph.pxd +106 -0
  96. sage/graphs/base/c_graph.pyx +5096 -0
  97. sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  98. sage/graphs/base/dense_graph.pxd +28 -0
  99. sage/graphs/base/dense_graph.pyx +801 -0
  100. sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
  101. sage/graphs/base/graph_backends.pxd +5 -0
  102. sage/graphs/base/graph_backends.pyx +797 -0
  103. sage/graphs/base/overview.py +85 -0
  104. sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  105. sage/graphs/base/sparse_graph.pxd +90 -0
  106. sage/graphs/base/sparse_graph.pyx +1653 -0
  107. sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  108. sage/graphs/base/static_dense_graph.pxd +5 -0
  109. sage/graphs/base/static_dense_graph.pyx +1032 -0
  110. sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
  111. sage/graphs/base/static_sparse_backend.pxd +27 -0
  112. sage/graphs/base/static_sparse_backend.pyx +1583 -0
  113. sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  114. sage/graphs/base/static_sparse_graph.pxd +37 -0
  115. sage/graphs/base/static_sparse_graph.pyx +1375 -0
  116. sage/graphs/bipartite_graph.py +2732 -0
  117. sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
  118. sage/graphs/centrality.pyx +1038 -0
  119. sage/graphs/cographs.py +519 -0
  120. sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
  121. sage/graphs/comparability.pyx +851 -0
  122. sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  123. sage/graphs/connectivity.pxd +157 -0
  124. sage/graphs/connectivity.pyx +4813 -0
  125. sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
  126. sage/graphs/convexity_properties.pxd +16 -0
  127. sage/graphs/convexity_properties.pyx +870 -0
  128. sage/graphs/digraph.py +4754 -0
  129. sage/graphs/digraph_generators.py +1993 -0
  130. sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
  131. sage/graphs/distances_all_pairs.pxd +12 -0
  132. sage/graphs/distances_all_pairs.pyx +2938 -0
  133. sage/graphs/domination.py +1363 -0
  134. sage/graphs/dot2tex_utils.py +100 -0
  135. sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  136. sage/graphs/edge_connectivity.pyx +1215 -0
  137. sage/graphs/generators/all.py +1 -0
  138. sage/graphs/generators/basic.py +1769 -0
  139. sage/graphs/generators/chessboard.py +538 -0
  140. sage/graphs/generators/classical_geometries.py +1611 -0
  141. sage/graphs/generators/degree_sequence.py +235 -0
  142. sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
  143. sage/graphs/generators/distance_regular.pyx +2846 -0
  144. sage/graphs/generators/families.py +4759 -0
  145. sage/graphs/generators/intersection.py +565 -0
  146. sage/graphs/generators/platonic_solids.py +262 -0
  147. sage/graphs/generators/random.py +2623 -0
  148. sage/graphs/generators/smallgraphs.py +5741 -0
  149. sage/graphs/generators/world_map.py +724 -0
  150. sage/graphs/generic_graph.py +26867 -0
  151. sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  152. sage/graphs/generic_graph_pyx.pxd +34 -0
  153. sage/graphs/generic_graph_pyx.pyx +1673 -0
  154. sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
  155. sage/graphs/genus.pyx +622 -0
  156. sage/graphs/graph.py +9645 -0
  157. sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
  158. sage/graphs/graph_coloring.pyx +2284 -0
  159. sage/graphs/graph_database.py +1177 -0
  160. sage/graphs/graph_decompositions/all.py +1 -0
  161. sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  162. sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
  163. sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
  164. sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
  165. sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  166. sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
  167. sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
  168. sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
  169. sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
  170. sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
  171. sage/graphs/graph_decompositions/graph_products.pyx +508 -0
  172. sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  173. sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
  174. sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
  175. sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  176. sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
  177. sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
  178. sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  179. sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
  180. sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
  181. sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
  182. sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
  183. sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
  184. sage/graphs/graph_editor.py +82 -0
  185. sage/graphs/graph_generators.py +3314 -0
  186. sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  187. sage/graphs/graph_generators_pyx.pyx +95 -0
  188. sage/graphs/graph_input.py +812 -0
  189. sage/graphs/graph_latex.py +2064 -0
  190. sage/graphs/graph_list.py +410 -0
  191. sage/graphs/graph_plot.py +1756 -0
  192. sage/graphs/graph_plot_js.py +338 -0
  193. sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
  194. sage/graphs/hyperbolicity.pyx +1704 -0
  195. sage/graphs/hypergraph_generators.py +364 -0
  196. sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  197. sage/graphs/independent_sets.pxd +13 -0
  198. sage/graphs/independent_sets.pyx +402 -0
  199. sage/graphs/isgci.py +1033 -0
  200. sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
  201. sage/graphs/isoperimetric_inequalities.pyx +489 -0
  202. sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  203. sage/graphs/line_graph.pyx +743 -0
  204. sage/graphs/lovasz_theta.py +77 -0
  205. sage/graphs/matching.py +1633 -0
  206. sage/graphs/matching_covered_graph.py +3590 -0
  207. sage/graphs/orientations.py +1489 -0
  208. sage/graphs/partial_cube.py +459 -0
  209. sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
  210. sage/graphs/path_enumeration.pyx +2040 -0
  211. sage/graphs/pq_trees.py +1129 -0
  212. sage/graphs/print_graphs.py +201 -0
  213. sage/graphs/schnyder.py +865 -0
  214. sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
  215. sage/graphs/spanning_tree.pyx +1457 -0
  216. sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
  217. sage/graphs/strongly_regular_db.pyx +3340 -0
  218. sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
  219. sage/graphs/traversals.pxd +9 -0
  220. sage/graphs/traversals.pyx +1872 -0
  221. sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
  222. sage/graphs/trees.pxd +15 -0
  223. sage/graphs/trees.pyx +310 -0
  224. sage/graphs/tutte_polynomial.py +713 -0
  225. sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
  226. sage/graphs/views.pyx +794 -0
  227. sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
  228. sage/graphs/weakly_chordal.pyx +604 -0
  229. sage/groups/all__sagemath_graphs.py +1 -0
  230. sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
  231. sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
  232. sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
  233. sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
  234. sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
  235. sage/knots/all.py +6 -0
  236. sage/knots/free_knotinfo_monoid.py +507 -0
  237. sage/knots/gauss_code.py +291 -0
  238. sage/knots/knot.py +682 -0
  239. sage/knots/knot_table.py +284 -0
  240. sage/knots/knotinfo.py +2900 -0
  241. sage/knots/link.py +4715 -0
  242. sage/sandpiles/all.py +13 -0
  243. sage/sandpiles/examples.py +225 -0
  244. sage/sandpiles/sandpile.py +6365 -0
  245. sage/topology/all.py +22 -0
  246. sage/topology/cell_complex.py +1214 -0
  247. sage/topology/cubical_complex.py +1976 -0
  248. sage/topology/delta_complex.py +1806 -0
  249. sage/topology/filtered_simplicial_complex.py +744 -0
  250. sage/topology/moment_angle_complex.py +823 -0
  251. sage/topology/simplicial_complex.py +5160 -0
  252. sage/topology/simplicial_complex_catalog.py +92 -0
  253. sage/topology/simplicial_complex_examples.py +1680 -0
  254. sage/topology/simplicial_complex_homset.py +205 -0
  255. sage/topology/simplicial_complex_morphism.py +836 -0
  256. sage/topology/simplicial_set.py +4102 -0
  257. sage/topology/simplicial_set_catalog.py +55 -0
  258. sage/topology/simplicial_set_constructions.py +2954 -0
  259. sage/topology/simplicial_set_examples.py +865 -0
  260. sage/topology/simplicial_set_morphism.py +1464 -0
@@ -0,0 +1,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")