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,3340 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ r"""
3
+ Database of strongly regular graphs
4
+
5
+ This module manages a database associating to a set of four integers
6
+ `(v,k,\lambda,\mu)` a strongly regular graphs with these parameters, when one
7
+ exists.
8
+
9
+ Using Andries Brouwer's `database of strongly regular graphs
10
+ <https://www.win.tue.nl/~aeb/graphs/srg/srgtab.html>`__, it can also return
11
+ non-existence results. Note that some constructions are missing, and that some
12
+ strongly regular graphs that exist in the database cannot be automatically built
13
+ by Sage. Help us if you know any.
14
+ An outline of the implementation can be found in [CP2016]_.
15
+
16
+ .. NOTE::
17
+
18
+ Any missing/incorrect information in the database must be reported to
19
+ `Andries E. Brouwer <https://www.win.tue.nl/~aeb/>`__ directly, in order to
20
+ have a unique and updated source of information.
21
+
22
+ REFERENCES:
23
+
24
+ [BL1984]_
25
+
26
+ Functions
27
+ ---------
28
+ """
29
+
30
+ import json
31
+ import os
32
+
33
+ from libc.math cimport sqrt, floor
34
+ from libc.stdint cimport uint_fast32_t
35
+
36
+ from sage.arith.misc import divisors, is_prime_power, is_square
37
+ from sage.categories.sets_cat import EmptySetError
38
+ from sage.graphs.graph import Graph
39
+ from sage.misc.cachefunc import cached_function
40
+ from sage.misc.lazy_import import LazyImport
41
+ from sage.misc.unknown import Unknown
42
+ from sage.rings.sum_of_squares cimport two_squares_c
43
+
44
+ orthogonal_array = LazyImport('sage.combinat.designs.orthogonal_arrays', 'orthogonal_array')
45
+ balanced_incomplete_block_design = LazyImport('sage.combinat.designs.bibd', 'balanced_incomplete_block_design')
46
+ GF = LazyImport('sage.rings.finite_rings.finite_field_constructor', 'GF')
47
+ Matrix = LazyImport('sage.matrix.constructor', 'Matrix')
48
+ LinearCode = LazyImport('sage.coding.linear_code', 'LinearCode')
49
+
50
+ cdef dict _brouwer_database = None
51
+ _small_srg_database = None
52
+
53
+
54
+ @cached_function
55
+ def is_paley(int v, int k, int l, int mu):
56
+ r"""
57
+ Test whether some Paley graph is `(v,k,\lambda,\mu)`-strongly regular.
58
+
59
+ INPUT:
60
+
61
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
62
+
63
+ OUTPUT:
64
+
65
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
66
+ exists, and ``None`` otherwise.
67
+
68
+ EXAMPLES::
69
+
70
+ sage: from sage.graphs.strongly_regular_db import is_paley
71
+ sage: t = is_paley(13,6,2,3); t
72
+ (..., 13)
73
+ sage: g = t[0](*t[1:]); g # needs sage.rings.finite_rings
74
+ Paley graph with parameter 13: Graph on 13 vertices
75
+ sage: g.is_strongly_regular(parameters=True) # needs sage.rings.finite_rings
76
+ (13, 6, 2, 3)
77
+ sage: t = is_paley(5,5,5,5); t
78
+ """
79
+ if (v % 4 == 1 and is_prime_power(v) and
80
+ k == (v - 1)//2 and
81
+ l == (v - 5)//4 and
82
+ mu == (v - 1)//4):
83
+ from sage.graphs.generators.families import PaleyGraph
84
+ return (PaleyGraph, v)
85
+
86
+
87
+ @cached_function
88
+ def is_mathon_PC_srg(int v, int k, int l, int mu):
89
+ r"""
90
+ Test whether some Mathon's Pseudocyclic s.r.g. is `(v,k,\lambda,\mu)`-strongly regular.
91
+
92
+ INPUT:
93
+
94
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
95
+
96
+ OUTPUT:
97
+
98
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
99
+ exists, and ``None`` otherwise.
100
+
101
+ .. TODO::
102
+
103
+ The current implementation only gives a subset of all possible graphs that can be
104
+ obtained using this construction. A full implementation should rely on a database
105
+ of conference matrices (or, equivalently, on a database of s.r.g.'s with parameters
106
+ `(4t+1,2t,t-1,t)`. Currently we make an extra assumption that `4t+1` is a prime power.
107
+ The first case where we miss a construction is `t=11`, where we could (recursively)
108
+ use the graph for `t=1` to construct a graph on 83205 vertices.
109
+
110
+ EXAMPLES::
111
+
112
+ sage: from sage.graphs.strongly_regular_db import is_mathon_PC_srg
113
+ sage: t = is_mathon_PC_srg(45,22,10,11); t # needs sage.libs.pari
114
+ (..., 1)
115
+ sage: g = t[0](*t[1:]); g # needs database_graphs sage.libs.pari
116
+ Mathon's PC SRG on 45 vertices: Graph on 45 vertices
117
+ sage: g.is_strongly_regular(parameters=True) # needs database_graphs sage.libs.pari
118
+ (45, 22, 10, 11)
119
+
120
+ TESTS::
121
+
122
+ sage: t = is_mathon_PC_srg(5,5,5,5); t # needs sage.libs.pari
123
+ sage: mu = 1895 # t=5 case -- the construction cannot work # needs sage.libs.pari
124
+ sage: t = is_mathon_PC_srg(4*mu+1,2*mu,mu-1,mu); t # needs sage.libs.pari
125
+ """
126
+ cdef int t
127
+ if (v % 4 == 1 and
128
+ k == (v - 1)//2 and
129
+ l == (v - 5)//4 and
130
+ mu == (v - 1)//4):
131
+ from sage.rings.integer_ring import ZZ
132
+ K = ZZ['x']
133
+ x = K.gen()
134
+ rpoly = (w for w in (x*(4*x*(4*x - 1) - 1) - mu).roots() if w[0] > 0)
135
+ try:
136
+ t = next(rpoly)[0]
137
+ if (is_prime_power(4*t - 1) and
138
+ is_prime_power(4*t + 1)): # extra assumption in TODO!
139
+ from sage.graphs.generators.families import \
140
+ MathonPseudocyclicStronglyRegularGraph
141
+ return (MathonPseudocyclicStronglyRegularGraph, t)
142
+ except StopIteration:
143
+ pass
144
+
145
+
146
+ @cached_function
147
+ def is_muzychuk_S6(int v, int k, int l, int mu):
148
+ r"""
149
+ Test whether some Muzychuk S6 graph is (v, k, l, mu)-strongly regular.
150
+
151
+ Tests whether a :func:`~sage.graphs.graph_generators.GraphGenerators.MuzychukS6Graph`
152
+ has parameters (v, k, l, mu).
153
+
154
+ INPUT:
155
+
156
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
157
+
158
+ OUTPUT:
159
+
160
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the required graph if it exists,
161
+ and ``None`` otherwise.
162
+
163
+ EXAMPLES::
164
+
165
+ sage: # needs sage.libs.pari
166
+ sage: from sage.graphs.strongly_regular_db import is_muzychuk_S6
167
+ sage: t = is_muzychuk_S6(378, 116, 34, 36)
168
+ sage: G = t[0](*t[1:]); G
169
+ Muzychuk S6 graph with parameters (3,3): Graph on 378 vertices
170
+ sage: G.is_strongly_regular(parameters=True)
171
+ (378, 116, 34, 36)
172
+ sage: t = is_muzychuk_S6(5, 5, 5, 5); t
173
+ """
174
+ cdef int n, d
175
+ from sage.rings.integer_ring import ZZ
176
+ n_list = [n for n in range(l - 1) if ZZ(n).is_prime_power()]
177
+ for n in n_list:
178
+ d = 2
179
+ while n**d * ((n**d - 1)//(n - 1) + 1) <= v:
180
+ if (v == n**d * ((n**d - 1)//(n - 1) + 1) and
181
+ k == n**(d - 1)*(n**d - 1)//(n - 1) - 1 and
182
+ l == mu - 2 and
183
+ mu == n**(d - 1) * (n**(d - 1) - 1)//(n - 1)):
184
+ from sage.graphs.generators.families import MuzychukS6Graph
185
+ return (MuzychukS6Graph, n, d)
186
+ d += 1
187
+
188
+
189
+ @cached_function
190
+ def is_orthogonal_array_block_graph(int v, int k, int l, int mu):
191
+ r"""
192
+ Test whether some (pseudo)Orthogonal Array graph is `(v,k,\lambda,\mu)`-strongly regular.
193
+
194
+ We know how to construct graphs with parameters of an Orthogonal Array (`OA(m,n)`),
195
+ also known as Latin squares graphs `L_m(n)`, in several cases where no orthogonal
196
+ array is known, or even in some cases for which they are known not to exist.
197
+
198
+ Such graphs are usually called pseudo-Latin squares graphs. Namely, Sage
199
+ can construct a graph with parameters of an `OA(m,n)`-graph whenever there
200
+ exists a skew-Hadamard matrix of order `n+1`, and `m=(n+1)/2` or
201
+ `m=(n-1)/2`. The construction in the former case is due to Goethals-Seidel
202
+ [BL1984]_, and in the latter case due to Pasechnik [Pas1992]_.
203
+
204
+ INPUT:
205
+
206
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
207
+
208
+ OUTPUT:
209
+
210
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
211
+ exists, and ``None`` otherwise.
212
+
213
+ EXAMPLES::
214
+
215
+ sage: # needs sage.combinat sage.modules
216
+ sage: from sage.graphs.strongly_regular_db import is_orthogonal_array_block_graph
217
+ sage: t = is_orthogonal_array_block_graph(64, 35, 18, 20); t
218
+ (..., 5, 8)
219
+ sage: g = t[0](*t[1:]); g
220
+ OA(5,8): Graph on 64 vertices
221
+ sage: g.is_strongly_regular(parameters=True)
222
+ (64, 35, 18, 20)
223
+ sage: t = is_orthogonal_array_block_graph(225,98,43,42); t
224
+ (..., 4)
225
+ sage: g = t[0](*t[1:]); g
226
+ Pasechnik Graph_4: Graph on 225 vertices
227
+ sage: g.is_strongly_regular(parameters=True)
228
+ (225, 98, 43, 42)
229
+ sage: t = is_orthogonal_array_block_graph(225,112,55,56); t
230
+ (..., 4)
231
+ sage: g = t[0](*t[1:]); g
232
+ skewhad^2_4: Graph on 225 vertices
233
+ sage: g.is_strongly_regular(parameters=True)
234
+ (225, 112, 55, 56)
235
+
236
+ sage: t = is_orthogonal_array_block_graph(5,5,5,5); t # needs sage.combinat sage.modules
237
+ """
238
+ # notations from
239
+ # https://www.win.tue.nl/~aeb/graphs/OA.html
240
+ from sage.combinat.matrices.hadamard_matrix import skew_hadamard_matrix
241
+ try:
242
+ m, n = latin_squares_graph_parameters(v, k, l, mu)
243
+ except Exception:
244
+ return
245
+ if orthogonal_array(m, n, existence=True) is True:
246
+ from sage.graphs.generators.intersection import OrthogonalArrayBlockGraph
247
+ return (lambda m, n: OrthogonalArrayBlockGraph(m, n), m, n)
248
+
249
+ elif n > 2 and skew_hadamard_matrix(n+1, existence=True) is True:
250
+ if m == (n + 1)/2:
251
+ from sage.graphs.generators.families import SquaredSkewHadamardMatrixGraph as G
252
+ elif m == (n - 1)//2:
253
+ from sage.graphs.generators.families import PasechnikGraph as G
254
+ else:
255
+ return
256
+ return (G, (n+1)//4)
257
+
258
+
259
+ @cached_function
260
+ def is_johnson(int v, int k, int l, int mu):
261
+ r"""
262
+ Test whether some Johnson graph is `(v,k,\lambda,\mu)`-strongly regular.
263
+
264
+ INPUT:
265
+
266
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
267
+
268
+ OUTPUT:
269
+
270
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
271
+ exists, and ``None`` otherwise.
272
+
273
+ EXAMPLES::
274
+
275
+ sage: from sage.graphs.strongly_regular_db import is_johnson
276
+ sage: t = is_johnson(10,6,3,4); t
277
+ (..., 5)
278
+ sage: g = t[0](*t[1:]); g
279
+ Johnson graph with parameters 5,2: Graph on 10 vertices
280
+ sage: g.is_strongly_regular(parameters=True)
281
+ (10, 6, 3, 4)
282
+
283
+ sage: t = is_johnson(5,5,5,5); t
284
+ """
285
+ # Using notations of https://www.win.tue.nl/~aeb/graphs/Johnson.html
286
+ #
287
+ # J(n,m) has parameters v = m(m – 1)/2, k = 2(m – 2), λ = m – 2, μ = 4.
288
+ m = l + 2
289
+ if (mu == 4 and
290
+ k == 2*(m - 2) and
291
+ v == m*(m - 1)//2):
292
+ from sage.graphs.generators.families import JohnsonGraph
293
+ return (lambda m: JohnsonGraph(m, 2), m)
294
+
295
+
296
+ @cached_function
297
+ def is_steiner(int v, int k, int l, int mu):
298
+ r"""
299
+ Test whether some Steiner graph is `(v,k,\lambda,\mu)`-strongly regular.
300
+
301
+ A Steiner graph is the intersection graph of a Steiner set system. For more
302
+ information, see https://www.win.tue.nl/~aeb/graphs/S.html.
303
+
304
+ INPUT:
305
+
306
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
307
+
308
+ OUTPUT:
309
+
310
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
311
+ exists, and ``None`` otherwise.
312
+
313
+ EXAMPLES::
314
+
315
+ sage: from sage.graphs.strongly_regular_db import is_steiner
316
+ sage: t = is_steiner(26,15,8,9); t
317
+ (..., 13, 3)
318
+ sage: g = t[0](*t[1:]); g
319
+ Intersection Graph: Graph on 26 vertices
320
+ sage: g.is_strongly_regular(parameters=True)
321
+ (26, 15, 8, 9)
322
+
323
+ sage: t = is_steiner(5,5,5,5); t
324
+ """
325
+ # Using notations from https://www.win.tue.nl/~aeb/graphs/S.html
326
+ #
327
+ # The block graph of a Steiner 2-design S(2,m,n) has parameters:
328
+ # v = n(n-1)/m(m-1), k = m(n-m)/(m-1), λ = (m-1)^2 + (n-1)/(m–1)–2, μ = m^2.
329
+ if mu <= 1 or not is_square(mu):
330
+ return
331
+ m = int(sqrt(mu))
332
+ n = (k*(m - 1))//m + m
333
+
334
+ if (v == (n*(n - 1))/(m*(m - 1)) and
335
+ k == m*(n - m)/(m - 1) and
336
+ l == (m - 1)**2 + (n - 1)/(m - 1) - 2 and
337
+ balanced_incomplete_block_design(n, m, existence=True) is True):
338
+ from sage.graphs.generators.intersection import IntersectionGraph
339
+ return (lambda n, m: IntersectionGraph([frozenset(b) for b in balanced_incomplete_block_design(n, m)]), n, m)
340
+
341
+
342
+ @cached_function
343
+ def is_affine_polar(int v, int k, int l, int mu):
344
+ r"""
345
+ Test whether some Affine Polar graph is `(v,k,\lambda,\mu)`-strongly regular.
346
+
347
+ For more information, see https://www.win.tue.nl/~aeb/graphs/VO.html.
348
+
349
+ INPUT:
350
+
351
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
352
+
353
+ OUTPUT:
354
+
355
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
356
+ exists, and ``None`` otherwise.
357
+
358
+ EXAMPLES::
359
+
360
+ sage: from sage.graphs.strongly_regular_db import is_affine_polar
361
+ sage: t = is_affine_polar(81,32,13,12); t # needs sage.rings.finite_rings
362
+ (..., 4, 3)
363
+ sage: g = t[0](*t[1:]); g # needs sage.libs.gap sage.rings.finite_rings
364
+ Affine Polar Graph VO^+(4,3): Graph on 81 vertices
365
+ sage: g.is_strongly_regular(parameters=True) # needs sage.libs.gap sage.rings.finite_rings
366
+ (81, 32, 13, 12)
367
+
368
+ sage: t = is_affine_polar(5,5,5,5); t
369
+ """
370
+ # Using notations from https://www.win.tue.nl/~aeb/graphs/VO.html
371
+ #
372
+ # VO+(2e,q) has parameters: v = q^(2e), k = (q^(e−1) + 1)(q^e − 1), λ =
373
+ # q(q^(e−2) + 1)(q^(e−1) − 1) + q − 2, μ = q^(e−1)(q^(e−1) + 1)
374
+ #
375
+ # VO−(2e,q) has parameters v = q^(2e), k = (q^(e−1) - 1)(q^e + 1), λ =
376
+ # q(q^(e−2) - 1)(q^(e−1) + 1) + q − 2, μ = q^(e−1)(q^(e−1) - 1)
377
+ if not is_square(v) or not is_prime_power(v):
378
+ return
379
+ prime, power = is_prime_power(v, get_data=True)
380
+ if power % 2:
381
+ return
382
+ for e in divisors(power/2):
383
+ q = prime**(power//(2*e))
384
+ assert v == q**(2*e)
385
+ if (k == (q**(e - 1) + 1)*(q**e - 1) and
386
+ l == q*(q**(e - 2) + 1)*(q**(e - 1) - 1) + q - 2 and
387
+ mu == q**(e - 1)*(q**(e - 1) + 1)):
388
+ from sage.graphs.generators.classical_geometries import AffineOrthogonalPolarGraph
389
+ return (lambda d, q: AffineOrthogonalPolarGraph(d, q, sign='+'), 2*e, q)
390
+ if (k == (q**(e - 1) - 1)*(q**e + 1) and
391
+ l == q*(q**(e - 2) - 1)*(q**(e - 1) + 1) + q - 2 and
392
+ mu == q**(e - 1)*(q**(e - 1) - 1)):
393
+ from sage.graphs.generators.classical_geometries import AffineOrthogonalPolarGraph
394
+ return (lambda d, q: AffineOrthogonalPolarGraph(d, q, sign='-'), 2*e, q)
395
+
396
+
397
+ @cached_function
398
+ def is_orthogonal_polar(int v, int k, int l, int mu):
399
+ r"""
400
+ Test whether some Orthogonal Polar graph is `(v,k,\lambda,\mu)`-strongly regular.
401
+
402
+ For more information, see https://www.win.tue.nl/~aeb/graphs/srghub.html.
403
+
404
+ INPUT:
405
+
406
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
407
+
408
+ OUTPUT:
409
+
410
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
411
+ exists, and ``None`` otherwise.
412
+
413
+ EXAMPLES::
414
+
415
+ sage: from sage.graphs.strongly_regular_db import is_orthogonal_polar
416
+ sage: t = is_orthogonal_polar(85, 20, 3, 5); t
417
+ (<function OrthogonalPolarGraph at ...>, 5, 4, '')
418
+ sage: g = t[0](*t[1:]); g # needs sage.libs.gap sage.rings.finite_rings
419
+ Orthogonal Polar Graph O(5, 4): Graph on 85 vertices
420
+ sage: g.is_strongly_regular(parameters=True) # needs sage.libs.gap sage.rings.finite_rings
421
+ (85, 20, 3, 5)
422
+
423
+ sage: t = is_orthogonal_polar(5,5,5,5); t # needs sage.rings.finite_rings
424
+
425
+ TESTS:
426
+
427
+ All of ``O(2m+1,q)``, ``O^+(2m,q)`` and ``O^-(2m,q)`` appear::
428
+
429
+ sage: is_orthogonal_polar(85, 20, 3, 5)
430
+ (<function OrthogonalPolarGraph at ...>, 5, 4, '')
431
+ sage: is_orthogonal_polar(119,54,21,27)
432
+ (<function OrthogonalPolarGraph at ...>, 8, 2, '-')
433
+ sage: is_orthogonal_polar(130,48,20,16) # needs sage.rings.finite_rings
434
+ (<function OrthogonalPolarGraph at ...>, 6, 3, '+')
435
+ """
436
+ r, s = eigenvalues(v, k, l, mu)
437
+ if r is None:
438
+ return
439
+ q_pow_m_minus_one = -s-1 if abs(s) > r else r+1
440
+
441
+ if is_prime_power(q_pow_m_minus_one):
442
+ prime, power = is_prime_power(q_pow_m_minus_one, get_data=True)
443
+ for d in divisors(power):
444
+ q = prime**d
445
+ m = (power//d) + 1
446
+
447
+ # O(2m+1,q)
448
+ if (v == (q**(2*m) - 1)//(q - 1) and
449
+ k == q*(q**(2*m - 2) - 1)//(q - 1) and
450
+ l == q**2*(q**(2*m - 4) - 1)//(q - 1) + q - 1 and
451
+ mu == (q**(2*m - 2) - 1)//(q - 1)):
452
+ from sage.graphs.generators.classical_geometries import OrthogonalPolarGraph
453
+ return (OrthogonalPolarGraph, 2*m+1, q, "")
454
+
455
+ # O^+(2m,q)
456
+ if (v == (q**(2*m - 1) - 1)//(q - 1) + q**(m - 1) and
457
+ k == q*(q**(2*m - 3) - 1)//(q - 1) + q**(m - 1) and
458
+ k == q**(2*m - 3) + l + 1 and
459
+ mu == k//q):
460
+ from sage.graphs.generators.classical_geometries import OrthogonalPolarGraph
461
+ return (OrthogonalPolarGraph, 2*m, q, "+")
462
+
463
+ # O^+(2m+1,q)
464
+ if (v == (q**(2*m - 1) - 1)//(q - 1) - q**(m - 1) and
465
+ k == q*(q**(2*m - 3) - 1)//(q - 1) - q**(m - 1) and
466
+ k == q**(2*m - 3) + l + 1 and
467
+ mu == k//q):
468
+ from sage.graphs.generators.classical_geometries import OrthogonalPolarGraph
469
+ return (OrthogonalPolarGraph, 2*m, q, "-")
470
+
471
+
472
+ @cached_function
473
+ def is_goethals_seidel(int v, int k, int l, int mu):
474
+ r"""
475
+ Test whether some
476
+ :func:`~sage.graphs.graph_generators.GraphGenerators.GoethalsSeidelGraph` graph is
477
+ `(v,k,\lambda,\mu)`-strongly regular.
478
+
479
+ INPUT:
480
+
481
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
482
+
483
+ OUTPUT:
484
+
485
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
486
+ exists, and ``None`` otherwise.
487
+
488
+ EXAMPLES::
489
+
490
+ sage: from sage.graphs.strongly_regular_db import is_goethals_seidel
491
+ sage: t = is_goethals_seidel(28, 15, 6, 10); t # needs sage.combinat sage.modules
492
+ [<function GoethalsSeidelGraph at ...>, 3, 3]
493
+ sage: g = t[0](*t[1:]); g # needs sage.combinat sage.modules
494
+ Graph on 28 vertices
495
+ sage: g.is_strongly_regular(parameters=True) # needs sage.combinat sage.modules
496
+ (28, 15, 6, 10)
497
+
498
+ sage: t = is_goethals_seidel(256, 135, 70, 72); t # needs sage.combinat sage.modules
499
+ [<function GoethalsSeidelGraph at ...>, 2, 15]
500
+ sage: g = t[0](*t[1:]); g # needs sage.combinat sage.modules
501
+ Graph on 256 vertices
502
+ sage: g.is_strongly_regular(parameters=True) # needs sage.combinat sage.modules
503
+ (256, 135, 70, 72)
504
+
505
+ sage: t = is_goethals_seidel(5,5,5,5); t # needs sage.combinat sage.modules
506
+
507
+ TESTS::
508
+
509
+ sage: for p in [(16, 9, 4, 6), (28, 15, 6, 10), # needs sage.combinat sage.modules
510
+ ....: (64, 35, 18, 20), (120, 63, 30, 36),
511
+ ....: (144, 77, 40, 42), (256, 135, 70, 72), (400, 209, 108, 110),
512
+ ....: (496, 255, 126, 136), (540, 275, 130, 150), (576, 299, 154, 156),
513
+ ....: (780, 399, 198, 210), (784, 405, 208, 210), (976, 495, 238, 264)]:
514
+ ....: print(is_goethals_seidel(*p))
515
+ [<function GoethalsSeidelGraph at ...>, 2, 3]
516
+ [<function GoethalsSeidelGraph at ...>, 3, 3]
517
+ [<function GoethalsSeidelGraph at ...>, 2, 7]
518
+ [<function GoethalsSeidelGraph at ...>, 3, 7]
519
+ [<function GoethalsSeidelGraph at ...>, 2, 11]
520
+ [<function GoethalsSeidelGraph at ...>, 2, 15]
521
+ [<function GoethalsSeidelGraph at ...>, 2, 19]
522
+ [<function GoethalsSeidelGraph at ...>, 3, 15]
523
+ [<function GoethalsSeidelGraph at ...>, 5, 11]
524
+ [<function GoethalsSeidelGraph at ...>, 2, 23]
525
+ [<function GoethalsSeidelGraph at ...>, 3, 19]
526
+ [<function GoethalsSeidelGraph at ...>, 2, 27]
527
+ [<function GoethalsSeidelGraph at ...>, 5, 15]
528
+ """
529
+ from sage.combinat.designs.bibd import balanced_incomplete_block_design
530
+ from sage.combinat.matrices.hadamard_matrix import hadamard_matrix
531
+
532
+ # here we guess the parameters v_bibd,k_bibd and r_bibd of the block design
533
+ #
534
+ # - the number of vertices v is equal to v_bibd*(r_bibd+1)
535
+ # - the degree k of the graph is equal to k=(v+r_bibd-1)/2
536
+
537
+ r_bibd = k - (v - 1 - k)
538
+ v_bibd = v//(r_bibd + 1)
539
+ k_bibd = (v_bibd - 1)//r_bibd + 1 if r_bibd > 0 else -1
540
+
541
+ if (v == v_bibd*(r_bibd + 1) and
542
+ 2*k == v + r_bibd - 1 and
543
+ 4*l == -2*v + 6*k - v_bibd - k_bibd and
544
+ hadamard_matrix(r_bibd + 1, existence=True) is True and
545
+ balanced_incomplete_block_design(v_bibd, k_bibd, existence=True) is True):
546
+ from sage.graphs.generators.families import GoethalsSeidelGraph
547
+ return [GoethalsSeidelGraph, k_bibd, r_bibd]
548
+
549
+
550
+ @cached_function
551
+ def is_NOodd(int v, int k, int l, int mu):
552
+ r"""
553
+ Test whether some NO^e(2n+1,q) graph is `(v,k,\lambda,\mu)`-strongly regular.
554
+
555
+ Here `q>2`, for in the case `q=2` this graph is complete. For more
556
+ information, see
557
+ :func:`sage.graphs.graph_generators.GraphGenerators.NonisotropicOrthogonalPolarGraph`
558
+ and Sect. 7.C of [BL1984]_.
559
+
560
+ INPUT:
561
+
562
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
563
+
564
+ OUTPUT:
565
+
566
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
567
+ exists, and ``None`` otherwise.
568
+
569
+ EXAMPLES::
570
+
571
+ sage: from sage.graphs.strongly_regular_db import is_NOodd
572
+ sage: t = is_NOodd(120, 51, 18, 24); t # needs sage.libs.pari
573
+ (<function NonisotropicOrthogonalPolarGraph at ...>, 5, 4, '-')
574
+ sage: g = t[0](*t[1:]); g # needs sage.libs.gap sage.libs.pari
575
+ NO^-(5, 4): Graph on 120 vertices
576
+ sage: g.is_strongly_regular(parameters=True) # needs sage.libs.gap sage.libs.pari
577
+ (120, 51, 18, 24)
578
+
579
+ TESTS:
580
+
581
+ All of ``NO^+(2m+1,q)`` and ``NO^-(2m+1,q)`` appear::
582
+
583
+ sage: # needs sage.libs.pari
584
+ sage: t = is_NOodd(120, 51, 18, 24); t
585
+ (<function NonisotropicOrthogonalPolarGraph at ...>, 5, 4, '-')
586
+ sage: t = is_NOodd(136, 75, 42, 40); t
587
+ (<function NonisotropicOrthogonalPolarGraph at ...>, 5, 4, '+')
588
+ sage: t = is_NOodd(378, 260, 178, 180); t
589
+ (<function NonisotropicOrthogonalPolarGraph at ...>, 7, 3, '+')
590
+ sage: t = is_NOodd(45, 32, 22, 24); t
591
+ (<function NonisotropicOrthogonalPolarGraph at ...>, 5, 3, '+')
592
+ sage: t = is_NOodd(351, 224, 142, 144); t
593
+ (<function NonisotropicOrthogonalPolarGraph at ...>, 7, 3, '-')
594
+ sage: t = is_NOodd(325, 144, 68, 60); t
595
+ (<function NonisotropicOrthogonalPolarGraph at ...>, 5, 5, '+')
596
+ sage: t = is_NOodd(300, 104, 28, 40); t
597
+ (<function NonisotropicOrthogonalPolarGraph at ...>, 5, 5, '-')
598
+ sage: t = is_NOodd(5,5,5,5); t
599
+ """
600
+ cdef int n, q
601
+ r, s = eigenvalues(v, k, l, mu) # -eq^(n-1)-1 and eq^(n-1)(q-2)-1; q=3 is special case
602
+ if r is None:
603
+ return
604
+ r += 1
605
+ s += 1
606
+ if abs(r) > abs(s):
607
+ (r, s) = (s, r) # r=-eq^(n-1) s= eq^(n-1)(q-2)
608
+ q = 2 - s//r
609
+ p, t = is_prime_power(q, get_data=True)
610
+ pp, kk = is_prime_power(abs(r), get_data=True)
611
+ if p == pp and t:
612
+ n = kk//t + 1
613
+ e = 1 if v == (q**n)*(q**n + 1)//2 else -1
614
+ if (v == (q**n)*(q**n + e)//2 and
615
+ k == (q**n - e)*(q**(n - 1) + e) and
616
+ l == 2*(q**(2*n - 2) - 1) + e*q**(n - 1)*(q - 1) and
617
+ mu == 2*q**(n - 1)*(q**(n - 1) + e)):
618
+ from sage.graphs.generators.classical_geometries import NonisotropicOrthogonalPolarGraph
619
+ return (NonisotropicOrthogonalPolarGraph, 2*n + 1, q, '+' if e == 1 else '-')
620
+
621
+
622
+ @cached_function
623
+ def is_NOperp_F5(int v, int k, int l, int mu):
624
+ r"""
625
+ Test whether some NO^e,perp(2n+1,5) graph is `(v,k,\lambda,\mu)`-strongly regular.
626
+
627
+ For more information, see
628
+ :func:`sage.graphs.graph_generators.GraphGenerators.NonisotropicOrthogonalPolarGraph`
629
+ and Sect. 7.D of [BL1984]_.
630
+
631
+ INPUT:
632
+
633
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
634
+
635
+ OUTPUT:
636
+
637
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
638
+ exists, and ``None`` otherwise.
639
+
640
+ EXAMPLES::
641
+
642
+ sage: from sage.graphs.strongly_regular_db import is_NOperp_F5
643
+ sage: t = is_NOperp_F5(10, 3, 0, 1); t # needs sage.libs.pari
644
+ (<function NonisotropicOrthogonalPolarGraph at ...>, 3, 5, '-', 1)
645
+ sage: g = t[0](*t[1:]); g # needs sage.libs.gap sage.libs.pari
646
+ NO^-,perp(3, 5): Graph on 10 vertices
647
+ sage: g.is_strongly_regular(parameters=True) # needs sage.libs.gap sage.libs.pari
648
+ (10, 3, 0, 1)
649
+
650
+ TESTS:
651
+
652
+ All of ``NO^+,perp(2m+1,5)`` and ``NO^-,perp(2m+1,5)`` appear::
653
+
654
+ sage: t = is_NOperp_F5(325, 60, 15, 10); t # needs sage.libs.pari
655
+ (<function NonisotropicOrthogonalPolarGraph at ...>, 5, 5, '+', 1)
656
+ sage: t = is_NOperp_F5(300, 65, 10, 15); t # needs sage.libs.pari
657
+ (<function NonisotropicOrthogonalPolarGraph at ...>, 5, 5, '-', 1)
658
+ sage: t = is_NOperp_F5(5,5,5,5); t # needs sage.libs.pari
659
+ """
660
+ cdef int n
661
+ r, s = eigenvalues(v, k, l, mu) # 2*e*5**(n-1), -e*5**(n-1); note exceptional case n=1
662
+ if r is None:
663
+ return
664
+ if abs(r) < abs(s):
665
+ (r, s) = (s, r)
666
+ e = 1 if s < 0 else -1
667
+ p, n = is_prime_power(abs(s), get_data=True)
668
+ if (5 == p and n) or (abs(r) == 2 and abs(s) == 1):
669
+ n += 1
670
+ if (v == (5**n)*(5**n + e)//2 and
671
+ k == (5**n - e)*5**(n - 1)//2 and
672
+ l == 5**(n - 1)*(5**(n - 1) + e)//2 and
673
+ mu == 5**(n - 1)*(5**(n - 1) - e)//2):
674
+ from sage.graphs.generators.classical_geometries import NonisotropicOrthogonalPolarGraph
675
+ return (NonisotropicOrthogonalPolarGraph, 2*n + 1, 5, '+' if e == 1 else '-', 1)
676
+
677
+
678
+ @cached_function
679
+ def is_NO_F2(int v, int k, int l, int mu):
680
+ r"""
681
+ Test whether some NO^e,perp(2n,2) graph is `(v,k,\lambda,\mu)`-strongly regular.
682
+
683
+ For more information, see
684
+ :func:`sage.graphs.graph_generators.GraphGenerators.NonisotropicOrthogonalPolarGraph`.
685
+
686
+ INPUT:
687
+
688
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
689
+
690
+ OUTPUT:
691
+
692
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
693
+ exists, and ``None`` otherwise.
694
+
695
+ EXAMPLES::
696
+
697
+ sage: from sage.graphs.strongly_regular_db import is_NO_F2
698
+ sage: t = is_NO_F2(10, 3, 0, 1); t # needs sage.libs.pari
699
+ (<function NonisotropicOrthogonalPolarGraph at ...>, 4, 2, '-')
700
+ sage: g = t[0](*t[1:]); g # needs sage.libs.gap sage.libs.pari
701
+ NO^-(4, 2): Graph on 10 vertices
702
+ sage: g.is_strongly_regular(parameters=True) # needs sage.libs.gap sage.libs.pari
703
+ (10, 3, 0, 1)
704
+
705
+ TESTS:
706
+
707
+ All of ``NO^+(2m,2)`` and ``NO^-(2m,2)`` appear::
708
+
709
+ sage: t = is_NO_F2(36, 15, 6, 6); t # needs sage.libs.pari
710
+ (<function NonisotropicOrthogonalPolarGraph at ...>, 6, 2, '-')
711
+ sage: t = is_NO_F2(28, 15, 6, 10); t # needs sage.libs.pari
712
+ (<function NonisotropicOrthogonalPolarGraph at ...>, 6, 2, '+')
713
+ sage: t = is_NO_F2(5,5,5,5); t # needs sage.libs.pari
714
+ """
715
+ cdef int n, e, p
716
+ p, n = is_prime_power(k+1, get_data=True) # k+1==2**(2*n-2)
717
+ if 2 == p and n and not n % 2:
718
+ n = (n+2)//2
719
+ e = (2**(2*n-1)-v)//2**(n-1)
720
+ if (abs(e) == 1 and
721
+ v == 2**(2*n - 1) - e*2**(n - 1) and
722
+ k == 2**(2*n - 2) - 1 and
723
+ l == 2**(2*n - 3) - 2 and
724
+ mu == 2**(2*n - 3) + e*2**(n - 2)):
725
+ from sage.graphs.generators.classical_geometries import NonisotropicOrthogonalPolarGraph
726
+ return (NonisotropicOrthogonalPolarGraph, 2*n, 2, '+' if e == 1 else '-')
727
+
728
+
729
+ @cached_function
730
+ def is_NO_F3(int v, int k, int l, int mu):
731
+ r"""
732
+ Test whether some NO^e,perp(2n,3) graph is `(v,k,\lambda,\mu)`-strongly regular.
733
+
734
+ For more information, see
735
+ :func:`sage.graphs.graph_generators.GraphGenerators.NonisotropicOrthogonalPolarGraph`.
736
+
737
+ INPUT:
738
+
739
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
740
+
741
+ OUTPUT:
742
+
743
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
744
+ exists, and ``None`` otherwise.
745
+
746
+ EXAMPLES::
747
+
748
+ sage: from sage.graphs.strongly_regular_db import is_NO_F3
749
+ sage: t = is_NO_F3(15, 6, 1, 3); t # needs sage.libs.pari
750
+ (<function NonisotropicOrthogonalPolarGraph at ...>, 4, 3, '-')
751
+ sage: g = t[0](*t[1:]); g # needs sage.libs.gap sage.libs.pari
752
+ NO^-(4, 3): Graph on 15 vertices
753
+ sage: g.is_strongly_regular(parameters=True) # needs sage.libs.gap sage.libs.pari
754
+ (15, 6, 1, 3)
755
+
756
+ TESTS:
757
+
758
+ All of ``NO^+(2m,3)`` and ``NO^-(2m,3)`` appear::
759
+
760
+ sage: t = is_NO_F3(126, 45, 12, 18); t # needs sage.libs.pari
761
+ (<function NonisotropicOrthogonalPolarGraph at ...>, 6, 3, '-')
762
+ sage: t = is_NO_F3(117, 36, 15, 9); t # needs sage.libs.pari
763
+ (<function NonisotropicOrthogonalPolarGraph at ...>, 6, 3, '+')
764
+ sage: t = is_NO_F3(5,5,5,5); t # needs sage.libs.pari
765
+ """
766
+ cdef int n, e, p
767
+ r, s = eigenvalues(v, k, l, mu) # e*3**(n-1), -e*3**(n-2)
768
+ if r is None:
769
+ return
770
+ if abs(r) < abs(s):
771
+ (r, s) = (s, r)
772
+ e = 1 if r > 0 else -1
773
+ p, n = is_prime_power(abs(r), get_data=True)
774
+ if 3 == p and n:
775
+ n += 1
776
+ if (v == 3**(n - 1)*(3**n - e)//2 and
777
+ k == 3**(n - 1)*(3**(n - 1) - e)//2 and
778
+ l == 3**(n - 2)*(3**(n - 1) + e)//2 and
779
+ mu == 3**(n - 1)*(3**(n - 2) - e)//2):
780
+ from sage.graphs.generators.classical_geometries import NonisotropicOrthogonalPolarGraph
781
+ return (NonisotropicOrthogonalPolarGraph, 2*n, 3, '+' if e == 1 else '-')
782
+
783
+
784
+ @cached_function
785
+ def is_NU(int v, int k, int l, int mu):
786
+ r"""
787
+ Test whether some NU(n,q)-graph, is `(v,k,\lambda,\mu)`-strongly regular.
788
+
789
+ Note that n>2; for n=2 there is no s.r.g. For more information, see
790
+ :func:`sage.graphs.graph_generators.GraphGenerators.NonisotropicUnitaryPolarGraph`
791
+ and series C14 in [Hub1975]_.
792
+
793
+ INPUT:
794
+
795
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
796
+
797
+ OUTPUT:
798
+
799
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
800
+ exists, and ``None`` otherwise.
801
+
802
+ EXAMPLES::
803
+
804
+ sage: from sage.graphs.strongly_regular_db import is_NU
805
+ sage: t = is_NU(40, 27, 18, 18); t # needs sage.libs.pari
806
+ (<function NonisotropicUnitaryPolarGraph at ...>, 4, 2)
807
+ sage: g = t[0](*t[1:]); g # needs sage.libs.gap sage.libs.pari
808
+ NU(4, 2): Graph on 40 vertices
809
+ sage: g.is_strongly_regular(parameters=True) # needs sage.libs.gap sage.libs.pari
810
+ (40, 27, 18, 18)
811
+
812
+ TESTS::
813
+
814
+ sage: # needs sage.libs.pari
815
+ sage: t = is_NU(176, 135, 102, 108); t
816
+ (<function NonisotropicUnitaryPolarGraph at ...>, 5, 2)
817
+ sage: t = is_NU(540, 224, 88, 96); t
818
+ (<function NonisotropicUnitaryPolarGraph at ...>, 4, 3)
819
+ sage: t = is_NU(208, 75, 30, 25); t
820
+ (<function NonisotropicUnitaryPolarGraph at ...>, 3, 4)
821
+ sage: t = is_NU(5,5,5,5); t
822
+ """
823
+ cdef int n, q, e # special cases: n=3 or q=2
824
+ r, s = eigenvalues(v, k, l, mu) # r,s = eq^{n-2} - 1, -e(q^2-q-1)q^{n-3} - 1, e=(-1)^n
825
+ if r is None:
826
+ return
827
+ r += 1
828
+ s += 1
829
+ if abs(r) > abs(s):
830
+ (r, s) = (s, r)
831
+ p, t = is_prime_power(abs(r), get_data=True)
832
+ if p == 2: # it can be that q=2, then we'd have r>s now
833
+ pp, kk = is_prime_power(abs(s), get_data=True)
834
+ if pp == 2 and kk > 0:
835
+ (r, s) = (s, r)
836
+ p, t = is_prime_power(abs(r), get_data=True)
837
+ if r == 1:
838
+ return
839
+ kr = k//(r-1) # eq^{n-1}+1
840
+ e = 1 if kr > 0 else -1
841
+ q = (kr-1)//r
842
+ pp, kk = is_prime_power(q, get_data=True)
843
+ if p == pp and kk:
844
+ n = t//kk + 2
845
+ if (v == q**(n - 1)*(q**n - e)//(q + 1) and
846
+ k == (q**(n - 1) + e)*(q**(n - 2) - e) and
847
+ l == q**(2*n - 5)*(q + 1) - e*q**(n - 2)*(q - 1) - 2 and
848
+ mu == q**(n - 3)*(q + 1)*(q**(n - 2) - e)):
849
+ from sage.graphs.generators.classical_geometries import NonisotropicUnitaryPolarGraph
850
+ return (NonisotropicUnitaryPolarGraph, n, q)
851
+
852
+
853
+ @cached_function
854
+ def is_haemers(int v, int k, int l, int mu):
855
+ r"""
856
+ Test whether some HaemersGraph graph is `(v,k,\lambda,\mu)`-strongly regular.
857
+
858
+ For more information, see
859
+ :func:`~sage.graphs.graph_generators.GraphGenerators.HaemersGraph`.
860
+
861
+ INPUT:
862
+
863
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
864
+
865
+ OUTPUT:
866
+
867
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
868
+ exists, and ``None`` otherwise.
869
+
870
+ EXAMPLES::
871
+
872
+ sage: from sage.graphs.strongly_regular_db import is_haemers
873
+ sage: t = is_haemers(96, 19, 2, 4); t # needs sage.libs.pari
874
+ (<function HaemersGraph at ...>, 4)
875
+ sage: g = t[0](*t[1:]); g # needs sage.libs.pari
876
+ Haemers(4): Graph on 96 vertices
877
+ sage: g.is_strongly_regular(parameters=True) # needs sage.libs.pari
878
+ (96, 19, 2, 4)
879
+
880
+ TESTS::
881
+
882
+ sage: t = is_haemers(5,5,5,5); t # needs sage.libs.pari
883
+ """
884
+ cdef int q, n, p
885
+ p, n = is_prime_power(mu, get_data=True)
886
+ q = mu
887
+ if 2 == p and n:
888
+ if (v == q**2*(q + 2) and
889
+ k == q*(q + 1) - 1 and
890
+ l == q - 2):
891
+ from sage.graphs.generators.classical_geometries import HaemersGraph
892
+ return (HaemersGraph, q)
893
+
894
+
895
+ @cached_function
896
+ def is_cossidente_penttila(int v, int k, int l, int mu):
897
+ r"""
898
+ Test whether some CossidentePenttilaGraph graph is `(v,k,\lambda,\mu)`-strongly regular.
899
+
900
+ For more information, see
901
+ :func:`~sage.graphs.graph_generators.GraphGenerators.CossidentePenttilaGraph`.
902
+
903
+ INPUT:
904
+
905
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
906
+
907
+ OUTPUT:
908
+
909
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
910
+ exists, and ``None`` otherwise.
911
+
912
+ EXAMPLES::
913
+
914
+ sage: from sage.graphs.strongly_regular_db import is_cossidente_penttila
915
+ sage: t = is_cossidente_penttila(378, 52, 1, 8); t # needs sage.libs.pari
916
+ (<function CossidentePenttilaGraph at ...>, 5)
917
+ sage: g = t[0](*t[1:]); g # optional - gap_package_design, needs sage.libs.pari
918
+ CossidentePenttila(5): Graph on 378 vertices
919
+ sage: g.is_strongly_regular(parameters=True) # optional - gap_package_design, needs sage.libs.pari
920
+ (378, 52, 1, 8)
921
+
922
+ TESTS::
923
+
924
+ sage: t = is_cossidente_penttila(56,10,0,2); t # needs sage.libs.pari
925
+ (<function CossidentePenttilaGraph at ...>, 3)
926
+ sage: t = is_cossidente_penttila(1376,150,2,18); t # needs sage.libs.pari
927
+ (<function CossidentePenttilaGraph at ...>, 7)
928
+ sage: t = is_cossidente_penttila(5,5,5,5); t # needs sage.libs.pari
929
+ """
930
+ cdef int q, n, p
931
+ q = 2*l + 3
932
+ p, n = is_prime_power(q, get_data=True)
933
+ if 2 < p and n:
934
+ if (v == (q**3 + 1)*(q + 1)//2 and
935
+ k == (q**2 + 1)*(q - 1)//2 and
936
+ mu == (q - 1)**2//2):
937
+ from sage.graphs.generators.classical_geometries import CossidentePenttilaGraph
938
+ return (CossidentePenttilaGraph, q)
939
+
940
+
941
+ @cached_function
942
+ def is_complete_multipartite(int v, int k, int l, int mu):
943
+ r"""
944
+ Test whether some complete multipartite graph is `(v,k,\lambda,\mu)`-strongly regular.
945
+
946
+ Any complete multipartite graph with parts of the same size is strongly regular.
947
+
948
+ INPUT:
949
+
950
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
951
+
952
+ OUTPUT:
953
+
954
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
955
+ exists, and ``None`` otherwise.
956
+
957
+ EXAMPLES::
958
+
959
+ sage: from sage.graphs.strongly_regular_db import is_complete_multipartite
960
+ sage: t = is_complete_multipartite(12,8,4,8); t
961
+ (<...CompleteMultipartiteSRG...>,
962
+ 3,
963
+ 4)
964
+ sage: g = t[0](*t[1:]); g
965
+ Multipartite Graph with set sizes [4, 4, 4]: Graph on 12 vertices
966
+ sage: g.is_strongly_regular(parameters=True)
967
+ (12, 8, 4, 8)
968
+
969
+ TESTS::
970
+
971
+ sage: t = is_complete_multipartite(5,5,5,5); t
972
+ sage: t = is_complete_multipartite(11,8,4,8); t
973
+ sage: t = is_complete_multipartite(20,16,12,16)
974
+ sage: g = t[0](*t[1:]); g
975
+ Multipartite Graph with set sizes [4, 4, 4, 4, 4]: Graph on 20 vertices
976
+ sage: g.is_strongly_regular(parameters=True)
977
+ (20, 16, 12, 16)
978
+ """
979
+ if v > k:
980
+ r = v//(v - k) # number of parts (of size v-k each)
981
+ if l == (v - k)*(r - 2) and k == mu and v == r*(v - k):
982
+ from sage.graphs.generators.basic import CompleteMultipartiteGraph
983
+
984
+ def CompleteMultipartiteSRG(nparts, partsize):
985
+ return CompleteMultipartiteGraph([partsize] * nparts)
986
+ return (CompleteMultipartiteSRG, r, v - k)
987
+
988
+
989
+ @cached_function
990
+ def is_polhill(int v, int k, int l, int mu):
991
+ r"""
992
+ Test whether some graph from [Pol2009]_ is `(1024,k,\lambda,\mu)`-strongly
993
+ regular.
994
+
995
+ .. NOTE::
996
+
997
+ This function does not actually explore *all* strongly regular graphs
998
+ produced in [Pol2009]_, but only those on 1024 vertices.
999
+
1000
+ John Polhill offered his help if we attempt to write a code to guess,
1001
+ given `(v,k,\lambda,\mu)`, which of his construction must be applied to
1002
+ find the graph.
1003
+
1004
+ INPUT:
1005
+
1006
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
1007
+
1008
+ OUTPUT:
1009
+
1010
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if the
1011
+ parameters match, and ``None`` otherwise.
1012
+
1013
+ EXAMPLES::
1014
+
1015
+ sage: # needs sage.rings.finite_rings
1016
+ sage: from sage.graphs.strongly_regular_db import is_polhill
1017
+ sage: t = is_polhill(1024, 231, 38, 56); t
1018
+ [<cyfunction is_polhill.<locals>.<lambda> at ...>]
1019
+ sage: g = t[0](*t[1:]); g # not tested (too long)
1020
+ Graph on 1024 vertices
1021
+ sage: g.is_strongly_regular(parameters=True) # not tested (too long)
1022
+ (1024, 231, 38, 56)
1023
+ sage: t = is_polhill(1024, 264, 56, 72); t
1024
+ [<cyfunction is_polhill.<locals>.<lambda> at ...>]
1025
+ sage: t = is_polhill(1024, 297, 76, 90); t
1026
+ [<cyfunction is_polhill.<locals>.<lambda> at ...>]
1027
+ sage: t = is_polhill(1024, 330, 98, 110); t
1028
+ [<cyfunction is_polhill.<locals>.<lambda> at ...>]
1029
+ sage: t = is_polhill(1024, 462, 206, 210); t
1030
+ [<cyfunction is_polhill.<locals>.<lambda> at ...>]
1031
+ """
1032
+ if (v, k, l, mu) not in [(1024, 231, 38, 56),
1033
+ (1024, 264, 56, 72),
1034
+ (1024, 297, 76, 90),
1035
+ (1024, 330, 98, 110),
1036
+ (1024, 462, 206, 210)]:
1037
+ return
1038
+
1039
+ from itertools import product
1040
+ from sage.categories.cartesian_product import cartesian_product
1041
+ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing
1042
+ from copy import copy
1043
+
1044
+ def additive_cayley(vertices):
1045
+ g = Graph()
1046
+ g.add_vertices(vertices[0].parent())
1047
+ edges = [(x, x + vv)
1048
+ for vv in set(vertices)
1049
+ for x in g]
1050
+ g.add_edges(edges)
1051
+ g.relabel()
1052
+ return g
1053
+
1054
+ # D is a Partial Difference Set of (Z4)^2, see section 2.
1055
+ G = cartesian_product([IntegerModRing(4), IntegerModRing(4)])
1056
+ D = [[(2, 0), (0, 1), (0, 3), (1, 1), (3, 3)],
1057
+ [(1, 0), (3, 0), (0, 2), (1, 3), (3, 1)],
1058
+ [(1, 2), (3, 2), (2, 1), (2, 3), (2, 2)]]
1059
+ D = [[G(e) for e in x] for x in D]
1060
+
1061
+ # The K_i are hyperplanes partitioning the nonzero elements of
1062
+ # GF(2^s)^2. See section 6.
1063
+ s = 3
1064
+ G1 = GF(2**s,'x')
1065
+ Gp = cartesian_product([G1, G1])
1066
+ K = [Gp((x, 1)) for x in G1] + [Gp((1, 0))]
1067
+ K = [[x for x in Gp if x[0]*uu + x[1]*vv == 0] for (uu, vv) in K]
1068
+
1069
+ # We now define the P_{i,j}. see section 6.
1070
+
1071
+ P = {}
1072
+ P[0, 1] = list(range((-1) + 1, 2**(s-2)+1))
1073
+ P[1, 1] = list(range((-1) + 2**(s-2)+2, 2**(s-1)+1))
1074
+ P[2, 1] = list(range((-1) + 2**(s-1)+2, 2**(s-1)+2**(s-2)+1))
1075
+ P[3, 1] = list(range((-1) + 2**(s-1)+2**(s-2)+2, 2**(s)+1))
1076
+
1077
+ P[0, 2] = list(range((-1) + 2**(s-2)+2, 2**(s-1)+2))
1078
+ P[1, 2] = list(range((-1) + 2**(s-1)+3, 2**(s-1)+2**(s-2)+2))
1079
+ P[2, 2] = list(range((-1) + 2**(s-1)+2**(s-2)+3, 2**(s)+1)) + [0]
1080
+ P[3, 2] = list(range((-1) + 2, 2**(s-2)+1))
1081
+
1082
+ P[0, 3] = list(range((-1) + 2**(s-1)+3, 2**(s-1)+2**(s-2)+3))
1083
+ P[1, 3] = list(range((-1) + 2**(s-1)+2**(s-2)+4, 2**(s)+1)) + [0,1]
1084
+ P[2, 3] = list(range((-1) + 3, 2**(s-2)+2))
1085
+ P[3, 3] = list(range((-1) + 2**(s-2)+3, 2**(s-1)+2))
1086
+
1087
+ P[0, 4] = list(range((-1) + 2**(s-1)+2**(s-2)+4, 2**(s)+1))
1088
+ P[1, 4] = list(range((-1) + 3, 2**(s-2)+1)) + [2**(s-1)+1,2**(s-1)+2**(s-2)+2]
1089
+ P[2, 4] = list(range((-1) + 2**(s-2)+3, 2**(s-1)+1)) + [2**(s-1)+2**(s-2)+1,1]
1090
+ P[3, 4] = list(range((-1) + 2**(s-1)+3, 2**(s-1)+2**(s-2)+1)) + [2**(s-2)+1,0]
1091
+
1092
+ R = {x: copy(P[x]) for x in P}
1093
+
1094
+ for x in P:
1095
+ P[x] = [K[i] for i in P[x]]
1096
+ P[x] = set(sum(P[x], [])).difference([Gp((0, 0))])
1097
+
1098
+ P[1, 4].add(Gp((0, 0)))
1099
+ P[2, 4].add(Gp((0, 0)))
1100
+ P[3, 4].add(Gp((0, 0)))
1101
+
1102
+ # We now define the R_{i,j}. see *end* of section 6.
1103
+
1104
+ R[0, 3] = list(range((-1) + 2**(s-1)+3, 2**(s-1)+2**(s-2)+2))
1105
+ R[1, 3] = list(range((-1) + 2**(s-1)+2**(s-2)+4, 2**(s)+1)) + [0,1,2**(s-1)+2**(s-2)+2]
1106
+ R[0, 4] = list(range((-1) + 2**(s-1)+2**(s-2)+4, 2**(s)+1)) + [2**(s-1)+2**(s-2)+2]
1107
+ R[1, 4] = list(range((-1) + 3, 2**(s-2)+1)) + [2**(s-1)+1]
1108
+
1109
+ for x in R:
1110
+ R[x] = [K[i] for i in R[x]]
1111
+ R[x] = set(sum(R[x], [])).difference([Gp((0, 0))])
1112
+
1113
+ R[1, 3].add(Gp((0, 0)))
1114
+ R[2, 4].add(Gp((0, 0)))
1115
+ R[3, 4].add(Gp((0, 0)))
1116
+
1117
+ # Dabcd = Da, Db, Dc, Dd (cf. p273)
1118
+ # D1234 = D1, D2, D3, D4 (cf. p276)
1119
+ Dabcd = []
1120
+ D1234 = []
1121
+
1122
+ Gprod = cartesian_product([G, Gp])
1123
+ for DD,PQ in [(Dabcd, P), (D1234, R)]:
1124
+ for i in range(1, 5):
1125
+ Dtmp = [product([G.zero()], PQ[0, i]),
1126
+ product(D[0], PQ[1, i]),
1127
+ product(D[1], PQ[2, i]),
1128
+ product(D[2], PQ[3, i])]
1129
+ Dtmp = map(set, Dtmp)
1130
+ Dtmp = [Gprod(e) for e in sum(map(list, Dtmp), [])]
1131
+ DD.append(Dtmp)
1132
+
1133
+ # Now that we have the data, we can return the graphs.
1134
+ if k == 231:
1135
+ return [lambda: additive_cayley(Dabcd[0])]
1136
+ if k == 264:
1137
+ return [lambda: additive_cayley(D1234[2])]
1138
+ if k == 297:
1139
+ return [lambda: additive_cayley(D1234[0] + D1234[1] + D1234[2]).complement()]
1140
+ if k == 330:
1141
+ return [lambda: additive_cayley(Dabcd[0] + Dabcd[1] + Dabcd[2]).complement()]
1142
+ if k == 462:
1143
+ return [lambda: additive_cayley(Dabcd[0] + Dabcd[1])]
1144
+
1145
+
1146
+ def is_RSHCD(int v, int k, int l, int mu):
1147
+ r"""
1148
+ Test whether some RSHCD graph is `(v,k,\lambda,\mu)`-strongly regular.
1149
+
1150
+ For more information, see :func:`SRG_from_RSHCD`.
1151
+
1152
+ INPUT:
1153
+
1154
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
1155
+
1156
+ OUTPUT:
1157
+
1158
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
1159
+ exists, and ``None`` otherwise.
1160
+
1161
+ EXAMPLES::
1162
+
1163
+ sage: from sage.graphs.strongly_regular_db import is_RSHCD
1164
+ sage: t = is_RSHCD(64,27,10,12); t # needs sage.combinat sage.modules
1165
+ [<built-in function SRG_from_RSHCD>, 64, 27, 10, 12]
1166
+ sage: g = t[0](*t[1:]); g # needs sage.combinat sage.modules
1167
+ Graph on 64 vertices
1168
+ sage: g.is_strongly_regular(parameters=True) # needs sage.combinat sage.modules
1169
+ (64, 27, 10, 12)
1170
+ """
1171
+ if SRG_from_RSHCD(v, k, l, mu, existence=True) is True:
1172
+ return [SRG_from_RSHCD, v, k, l, mu]
1173
+
1174
+
1175
+ def SRG_from_RSHCD(v, k, l, mu, existence=False, check=True):
1176
+ r"""
1177
+ Return a `(v,k,l,mu)`-strongly regular graph from a RSHCD.
1178
+
1179
+ This construction appears in 8.D of [BL1984]_. For more information, see
1180
+ :func:`~sage.combinat.matrices.hadamard_matrix.regular_symmetric_hadamard_matrix_with_constant_diagonal`.
1181
+
1182
+ INPUT:
1183
+
1184
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
1185
+
1186
+ - ``existence`` -- boolean; whether to return a graph or to test if Sage
1187
+ can build such a graph
1188
+
1189
+ - ``check`` -- boolean (default: ``True``); whether to check that output is
1190
+ correct before returning it. As this is expected to be useless, you may
1191
+ want to disable it whenever you want speed.
1192
+
1193
+ EXAMPLES:
1194
+
1195
+ some graphs ::
1196
+
1197
+ sage: from sage.graphs.strongly_regular_db import SRG_from_RSHCD
1198
+ sage: SRG_from_RSHCD(784, 0, 14, 38, existence=True) # needs sage.combinat sage.modules
1199
+ False
1200
+ sage: SRG_from_RSHCD(784, 377, 180, 182, existence=True) # needs sage.combinat sage.modules
1201
+ True
1202
+ sage: SRG_from_RSHCD(144, 65, 28, 30) # needs sage.combinat sage.modules
1203
+ Graph on 144 vertices
1204
+
1205
+ an example with vertex-transitive automorphism group, found during the
1206
+ implementation of the case `v=324` ::
1207
+
1208
+ sage: # long time, needs sage.combinat sage.modules
1209
+ sage: G = SRG_from_RSHCD(324,152,70,72)
1210
+ sage: a = G.automorphism_group()
1211
+ sage: a.order()
1212
+ 2592
1213
+ sage: len(a.orbits())
1214
+ 1
1215
+
1216
+ TESTS::
1217
+
1218
+ sage: SRG_from_RSHCD(784, 0, 14, 38) # needs sage.combinat sage.modules
1219
+ Traceback (most recent call last):
1220
+ ...
1221
+ ValueError: I do not know how to build a (784, 0, 14, 38)-SRG from a RSHCD
1222
+ """
1223
+ from sage.combinat.matrices.hadamard_matrix import regular_symmetric_hadamard_matrix_with_constant_diagonal
1224
+
1225
+ def sgn(x):
1226
+ return 1 if x >= 0 else -1
1227
+ n = v
1228
+ a = (n-4*mu)//2
1229
+ e = 2*k - n + 1 + a
1230
+
1231
+ if (e**2 == 1 and
1232
+ k == (n-1-a+e)/2 and
1233
+ l == (n-2*a)/4 - (1-e) and
1234
+ mu == (n-2*a)/4 and
1235
+ regular_symmetric_hadamard_matrix_with_constant_diagonal(n, sgn(a)*e, existence=True) is True):
1236
+ if existence:
1237
+ return True
1238
+ from sage.matrix.constructor import identity_matrix as I
1239
+ from sage.matrix.constructor import ones_matrix as J
1240
+
1241
+ H = regular_symmetric_hadamard_matrix_with_constant_diagonal(n, sgn(a)*e)
1242
+ if list(H.column(0)[1:]).count(1) == k:
1243
+ H = -H
1244
+ G = Graph((J(n) - I(n) - H + H[0, 0]*I(n)) / 2,
1245
+ loops=False, multiedges=False, format='adjacency_matrix')
1246
+ if check:
1247
+ assert G.is_strongly_regular(parameters=True) == (v, k, l, mu)
1248
+ return G
1249
+
1250
+ if existence:
1251
+ return False
1252
+ raise ValueError("I do not know how to build a {}-SRG from a RSHCD".format((v, k, l, mu)))
1253
+
1254
+
1255
+ @cached_function
1256
+ def is_unitary_polar(int v, int k, int l, int mu):
1257
+ r"""
1258
+ Test whether some Unitary Polar graph is `(v,k,\lambda,\mu)`-strongly regular.
1259
+
1260
+ For more information, see https://www.win.tue.nl/~aeb/graphs/srghub.html.
1261
+
1262
+ INPUT:
1263
+
1264
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
1265
+
1266
+ OUTPUT:
1267
+
1268
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
1269
+ exists, and ``None`` otherwise.
1270
+
1271
+ EXAMPLES::
1272
+
1273
+ sage: from sage.graphs.strongly_regular_db import is_unitary_polar
1274
+ sage: t = is_unitary_polar(45, 12, 3, 3); t # needs sage.libs.pari
1275
+ (<function UnitaryPolarGraph at ...>, 4, 2)
1276
+ sage: g = t[0](*t[1:]); g # needs sage.libs.gap sage.libs.pari
1277
+ Unitary Polar Graph U(4, 2); GQ(4, 2): Graph on 45 vertices
1278
+ sage: g.is_strongly_regular(parameters=True) # needs sage.libs.gap sage.libs.pari
1279
+ (45, 12, 3, 3)
1280
+
1281
+ sage: t = is_unitary_polar(5,5,5,5); t # needs sage.libs.pari
1282
+
1283
+ TESTS:
1284
+
1285
+ All the ``U(n,q)`` appear::
1286
+
1287
+ sage: # needs sage.libs.pari
1288
+ sage: t = is_unitary_polar(45, 12, 3, 3); t
1289
+ (<function UnitaryPolarGraph at ...>, 4, 2)
1290
+ sage: t = is_unitary_polar(165, 36, 3, 9); t
1291
+ (<function UnitaryPolarGraph at ...>, 5, 2)
1292
+ sage: t = is_unitary_polar(693, 180, 51, 45); t
1293
+ (<function UnitaryPolarGraph at ...>, 6, 2)
1294
+ sage: t = is_unitary_polar(1105, 80, 15, 5); t
1295
+ (<function UnitaryPolarGraph at ...>, 4, 4)
1296
+ """
1297
+ r, s = eigenvalues(v, k, l, mu)
1298
+ if r is None:
1299
+ return
1300
+ q = k//mu
1301
+ if q*mu != k or q < 2:
1302
+ return
1303
+ p, t = is_prime_power(q, get_data=True)
1304
+ if p**t != q or t % 2:
1305
+ return
1306
+ # at this point we know that we should have U(n,q) for some n and q=p^t, t even
1307
+ if r > 0:
1308
+ q_pow_d_minus_one = r+1
1309
+ else:
1310
+ q_pow_d_minus_one = -s-1
1311
+ ppp, ttt = is_prime_power(q_pow_d_minus_one, get_data=True)
1312
+ d = ttt//t + 1
1313
+ if ppp != p or (d-1)*t != ttt:
1314
+ return
1315
+ t //= 2
1316
+ # U(2d+1,q); write q^(1/2) as p^t
1317
+ if (v == (q**d - 1)*((q**d)*p**t + 1)//(q - 1) and
1318
+ k == q*(q**(d-1) - 1)*((q**d)//(p**t) + 1)//(q - 1) and
1319
+ l == q*q*(q**(d-2)-1)*((q**(d-1))//(p**t) + 1)//(q - 1) + q - 1):
1320
+ from sage.graphs.generators.classical_geometries import UnitaryPolarGraph
1321
+ return (UnitaryPolarGraph, 2*d+1, p**t)
1322
+
1323
+ # U(2d,q);
1324
+ if (v == (q**d - 1)*((q**d)//(p**t) + 1)//(q - 1) and
1325
+ k == q*(q**(d-1) - 1)*((q**(d-1))//(p**t) + 1)//(q - 1) and
1326
+ l == q*q*(q**(d-2)-1)*((q**(d-2))//(p**t) + 1)//(q - 1) + q - 1):
1327
+ from sage.graphs.generators.classical_geometries import UnitaryPolarGraph
1328
+ return (UnitaryPolarGraph, 2*d, p**t)
1329
+
1330
+
1331
+ @cached_function
1332
+ def is_unitary_dual_polar(int v, int k, int l, int mu):
1333
+ r"""
1334
+ Test whether some Unitary Dual Polar graph is `(v,k,\lambda,\mu)`-strongly regular.
1335
+
1336
+ This must be the U_5(q) on totally isotropic lines.
1337
+ For more information, see https://www.win.tue.nl/~aeb/graphs/srghub.html.
1338
+
1339
+ INPUT:
1340
+
1341
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
1342
+
1343
+ OUTPUT:
1344
+
1345
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
1346
+ exists, and ``None`` otherwise.
1347
+
1348
+ EXAMPLES::
1349
+
1350
+ sage: # needs sage.libs.pari
1351
+ sage: from sage.graphs.strongly_regular_db import is_unitary_dual_polar
1352
+ sage: t = is_unitary_dual_polar(297, 40, 7, 5); t
1353
+ (<function UnitaryDualPolarGraph at ...>, 5, 2)
1354
+ sage: g = t[0](*t[1:]); g # needs sage.libs.gap
1355
+ Unitary Dual Polar Graph DU(5, 2); GQ(8, 4): Graph on 297 vertices
1356
+ sage: g.is_strongly_regular(parameters=True) # needs sage.libs.gap
1357
+ (297, 40, 7, 5)
1358
+ sage: t = is_unitary_dual_polar(5,5,5,5); t
1359
+
1360
+ TESTS::
1361
+
1362
+ sage: is_unitary_dual_polar(6832, 270, 26, 10) # needs sage.libs.pari
1363
+ (<function UnitaryDualPolarGraph at ...>, 5, 3)
1364
+ """
1365
+ r, s = eigenvalues(v, k, l, mu)
1366
+ if r is None:
1367
+ return
1368
+ q = mu - 1
1369
+ if q < 2:
1370
+ return
1371
+ p, t = is_prime_power(q, get_data=True)
1372
+ if p**t != q or t % 2:
1373
+ return
1374
+ if (r < 0 and q != -r - 1) or (s < 0 and q != -s - 1):
1375
+ return
1376
+ t //= 2
1377
+ # we have correct mu, negative eigenvalue, and q=p^(2t)
1378
+ if (v == (q**2*p**t + 1)*(q*p**t + 1) and
1379
+ k == q*p**t*(q + 1) and
1380
+ l == k - 1 - q**2*p**t):
1381
+ from sage.graphs.generators.classical_geometries import UnitaryDualPolarGraph
1382
+ return (UnitaryDualPolarGraph, 5, p**t)
1383
+
1384
+
1385
+ @cached_function
1386
+ def is_GQqmqp(int v, int k, int l, int mu):
1387
+ r"""
1388
+ Test whether some `GQ(q-1,q+1)` or `GQ(q+1,q-1)`-graph is `(v,k,\lambda,\mu)`-srg.
1389
+
1390
+ INPUT:
1391
+
1392
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
1393
+
1394
+ OUTPUT:
1395
+
1396
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
1397
+ exists, and ``None`` otherwise.
1398
+
1399
+ EXAMPLES::
1400
+
1401
+ sage: # needs sage.libs.pari
1402
+ sage: from sage.graphs.strongly_regular_db import is_GQqmqp
1403
+ sage: t = is_GQqmqp(27,10,1,5); t
1404
+ (<function AhrensSzekeresGeneralizedQuadrangleGraph at ...>, 3, False)
1405
+ sage: g = t[0](*t[1:]); g
1406
+ AS(3); GQ(2, 4): Graph on 27 vertices
1407
+ sage: t = is_GQqmqp(45,12,3,3); t
1408
+ (<function AhrensSzekeresGeneralizedQuadrangleGraph at ...>, 3, True)
1409
+ sage: g = t[0](*t[1:]); g
1410
+ AS(3)*; GQ(4, 2): Graph on 45 vertices
1411
+ sage: g.is_strongly_regular(parameters=True)
1412
+ (45, 12, 3, 3)
1413
+ sage: t = is_GQqmqp(16,6,2,2); t
1414
+ (<function T2starGeneralizedQuadrangleGraph at ...>, 2, True)
1415
+ sage: g = t[0](*t[1:]); g
1416
+ T2*(O,2)*; GQ(3, 1): Graph on 16 vertices
1417
+ sage: g.is_strongly_regular(parameters=True)
1418
+ (16, 6, 2, 2)
1419
+ sage: t = is_GQqmqp(64,18,2,6); t
1420
+ (<function T2starGeneralizedQuadrangleGraph at ...>, 4, False)
1421
+ sage: g = t[0](*t[1:]); g
1422
+ T2*(O,4); GQ(3, 5): Graph on 64 vertices
1423
+ sage: g.is_strongly_regular(parameters=True)
1424
+ (64, 18, 2, 6)
1425
+
1426
+ TESTS::
1427
+
1428
+ sage: # needs sage.libs.pari
1429
+ sage: (S,T) = (127,129)
1430
+ sage: t = is_GQqmqp((S+1)*(S*T+1), S*(T+1), S-1, T+1); t
1431
+ (<function T2starGeneralizedQuadrangleGraph at ...>, 128, False)
1432
+ sage: (S,T) = (129,127)
1433
+ sage: t = is_GQqmqp((S+1)*(S*T+1), S*(T+1), S-1, T+1); t
1434
+ (<function T2starGeneralizedQuadrangleGraph at ...>, 128, True)
1435
+ sage: (S,T) = (124,126)
1436
+ sage: t = is_GQqmqp((S+1)*(S*T+1), S*(T+1), S-1, T+1); t
1437
+ (<function AhrensSzekeresGeneralizedQuadrangleGraph at ...>, 125, False)
1438
+ sage: (S,T) = (126,124)
1439
+ sage: t = is_GQqmqp((S+1)*(S*T+1), S*(T+1), S-1, T+1); t
1440
+ (<function AhrensSzekeresGeneralizedQuadrangleGraph at ...>, 125, True)
1441
+ sage: t = is_GQqmqp(5,5,5,5); t
1442
+ """
1443
+ # do we have GQ(s,t)? we must have mu=t+1, s=l+1,
1444
+ # v=(s+1)(st+1), k=s(t+1)
1445
+ S = l + 1
1446
+ T = mu - 1
1447
+ q = (S+T)//2
1448
+ p, w = is_prime_power(q, get_data=True)
1449
+ if (v == (S+1)*(S*T+1) and
1450
+ k == S*(T+1) and
1451
+ q == p**w and
1452
+ (S+T)//2 == q):
1453
+ if p % 2 == 0:
1454
+ from sage.graphs.generators.classical_geometries\
1455
+ import T2starGeneralizedQuadrangleGraph as F
1456
+ else:
1457
+ from sage.graphs.generators.classical_geometries\
1458
+ import AhrensSzekeresGeneralizedQuadrangleGraph as F
1459
+ if (S, T) == (q-1, q+1):
1460
+ return (F, q, False)
1461
+ elif (S, T) == (q+1, q-1):
1462
+ return (F, q, True)
1463
+
1464
+
1465
+ @cached_function
1466
+ def is_twograph_descendant_of_srg(int v, int k0, int l, int mu):
1467
+ r"""
1468
+ Test whether some descendant graph of a s.r.g. is `(v,k_0,\lambda,\mu)`-s.r.g.
1469
+
1470
+ We check whether there can exist `(v+1,k,\lambda^*,\mu^*)`-s.r.g. `G` so
1471
+ that ``self`` is a descendant graph of the regular two-graph specified
1472
+ by `G`.
1473
+ Specifically, we must have that `v+1=2(2k-\lambda^*-\mu^*)`, and
1474
+ `k_0=2(k-\mu^*)`, `\lambda=k+\lambda^*-2\mu^*`, `\mu=k-\mu^*`, which give 2
1475
+ independent linear conditions, say `k-\mu^*=\mu` and
1476
+ `\lambda^*-\mu^*=\lambda-\mu`. Further, there is a quadratic relation
1477
+ `2 k^2-(v+1+4 \mu) k+ 2 v \mu=0`.
1478
+
1479
+ If we can construct such `G` then we return a function to build a
1480
+ `(v,k_0,\lambda,\mu)`-s.r.g. For more information,
1481
+ see 10.3 in https://www.win.tue.nl/~aeb/2WF02/spectra.pdf
1482
+
1483
+ INPUT:
1484
+
1485
+ - ``v``, ``k0``, ``l``, ``mu`` -- integers
1486
+
1487
+ OUTPUT:
1488
+
1489
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one
1490
+ exists and is known, and ``None`` otherwise.
1491
+
1492
+ EXAMPLES::
1493
+
1494
+ sage: from sage.graphs.strongly_regular_db import is_twograph_descendant_of_srg
1495
+ sage: t = is_twograph_descendant_of_srg(27, 10, 1, 5); t # needs database_graphs sage.rings.finite_rings
1496
+ (<...is_twograph_descendant_of_srg...>, (8,))
1497
+ sage: g = t[0](*t[1:]); g # needs database_graphs sage.rings.finite_rings
1498
+ descendant of complement(Johnson graph with parameters 8,2) at {0, 1}: Graph on 27 vertices
1499
+ sage: g.is_strongly_regular(parameters=True) # needs database_graphs sage.rings.finite_rings
1500
+ (27, 10, 1, 5)
1501
+ sage: t = is_twograph_descendant_of_srg(5,5,5,5); t
1502
+
1503
+ TESTS::
1504
+
1505
+ sage: graphs.strongly_regular_graph(279, 150, 85, 75, existence=True) # needs sage.combinat
1506
+ True
1507
+ sage: graphs.strongly_regular_graph(279, 150, 85, 75).is_strongly_regular(parameters=True) # optional - gap_package_design internet
1508
+ (279, 150, 85, 75)
1509
+ """
1510
+ cdef int b, k
1511
+ if k0 != 2*mu or not v % 2:
1512
+ return
1513
+ b = v+1+4*mu
1514
+ D = sqrt(b**2-16*v*mu)
1515
+ if int(D) == D:
1516
+ for kf in [(-D+b)//4, (D+b)//4]:
1517
+ k = int(kf)
1518
+ if (k == kf and
1519
+ strongly_regular_graph(v+1, k, l - 2*mu + k, k - mu, existence=True) is True):
1520
+ try:
1521
+ g = strongly_regular_graph_lazy(v+1, k, l - 2*mu + k) # Sage might not know how to build g
1522
+
1523
+ def la(*gr):
1524
+ from sage.combinat.designs.twographs import twograph_descendant
1525
+ gg = g[0](*gr)
1526
+ if (gg.name() is None) or (gg.name() == ''):
1527
+ gg = Graph(gg, name=str((v+1, k, l - 2*mu + k, k - mu))+"-strongly regular graph")
1528
+ return twograph_descendant(gg, next(gg.vertex_iterator()),
1529
+ name=True)
1530
+ return (la, *g[1:])
1531
+ except RuntimeError:
1532
+ pass
1533
+ return
1534
+
1535
+
1536
+ @cached_function
1537
+ def is_taylor_twograph_srg(int v, int k, int l, int mu):
1538
+ r"""
1539
+ Test whether some Taylor two-graph SRG is `(v,k,\lambda,\mu)`-strongly regular.
1540
+
1541
+ For more information, see §7E of [BL1984]_.
1542
+
1543
+ INPUT:
1544
+
1545
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
1546
+
1547
+ OUTPUT:
1548
+
1549
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph
1550
+ :func:`TaylorTwographSRG
1551
+ <sage.graphs.graph_generators.GraphGenerators.TaylorTwographSRG>` if the
1552
+ parameters match, and ``None`` otherwise.
1553
+
1554
+ EXAMPLES::
1555
+
1556
+ sage: # needs sage.libs.pari
1557
+ sage: from sage.graphs.strongly_regular_db import is_taylor_twograph_srg
1558
+ sage: t = is_taylor_twograph_srg(28, 15, 6, 10); t
1559
+ (<function TaylorTwographSRG at ...>, 3)
1560
+ sage: g = t[0](*t[1:]); g
1561
+ Taylor two-graph SRG: Graph on 28 vertices
1562
+ sage: g.is_strongly_regular(parameters=True)
1563
+ (28, 15, 6, 10)
1564
+ sage: t = is_taylor_twograph_srg(5,5,5,5); t
1565
+
1566
+ TESTS::
1567
+
1568
+ sage: is_taylor_twograph_srg(730, 369, 168, 205) # needs sage.libs.pari
1569
+ (<function TaylorTwographSRG at ...>, 9)
1570
+ """
1571
+ r, _ = eigenvalues(v, k, l, mu)
1572
+ if r is None:
1573
+ return
1574
+ p, t = is_prime_power(v-1, get_data=True)
1575
+ if p**t+1 != v or t % 3 != 0 or p % 2 == 0:
1576
+ return
1577
+ q = p**(t//3)
1578
+ if (k, l, mu) == (q*(q**2+1)//2, (q**2+3)*(q-1)//4, (q**2+1)*(q+1)//4):
1579
+ from sage.graphs.generators.classical_geometries import TaylorTwographSRG
1580
+ return (TaylorTwographSRG, q)
1581
+ return
1582
+
1583
+
1584
+ def is_switch_skewhad(int v, int k, int l, int mu):
1585
+ r"""
1586
+ Test whether some ``switch skewhad^2+*`` is `(v,k,\lambda,\mu)`-strongly regular.
1587
+
1588
+ The ``switch skewhad^2+*`` graphs appear on `Andries Brouwer's database
1589
+ <https://www.win.tue.nl/~aeb/graphs/srg/srgtab.html>`__ and are built by
1590
+ adding an isolated vertex to the complement of
1591
+ :func:`~sage.graphs.graph_generators.GraphGenerators.SquaredSkewHadamardMatrixGraph`,
1592
+ and a :meth:`Seidel switching <Graph.seidel_switching>` a set of disjoint
1593
+ `n`-cocliques.
1594
+
1595
+ INPUT:
1596
+
1597
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
1598
+
1599
+ OUTPUT:
1600
+
1601
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if the
1602
+ parameters match, and ``None`` otherwise.
1603
+
1604
+ EXAMPLES::
1605
+
1606
+ sage: graphs.strongly_regular_graph(226, 105, 48, 49) # needs sage.combinat sage.modules
1607
+ switch skewhad^2+*_4: Graph on 226 vertices
1608
+
1609
+ TESTS::
1610
+
1611
+ sage: from sage.graphs.strongly_regular_db import is_switch_skewhad
1612
+ sage: t = is_switch_skewhad(5,5,5,5); t # needs sage.combinat sage.modules
1613
+ """
1614
+ from sage.combinat.matrices.hadamard_matrix import skew_hadamard_matrix
1615
+ from sage.graphs.generators.families import SwitchedSquaredSkewHadamardMatrixGraph
1616
+ cdef int n
1617
+ r, s = eigenvalues(v, k, l, mu)
1618
+ if r is None:
1619
+ return
1620
+ if r < s:
1621
+ r, s = s, r
1622
+ n = -s // 2
1623
+ if (int(r) == 2*n-1 and
1624
+ v == (4*n-1)**2 + 1 and
1625
+ k == (4*n-1)*(2*n-1) and
1626
+ skew_hadamard_matrix(4*n, existence=True) is True):
1627
+ return (SwitchedSquaredSkewHadamardMatrixGraph, n)
1628
+
1629
+
1630
+ def is_switch_OA_srg(int v, int k, int l, int mu):
1631
+ r"""
1632
+ Test whether some *switch* `OA(k,n)+*` is `(v,k,\lambda,\mu)`-strongly regular.
1633
+
1634
+ The "switch* `OA(k,n)+*` graphs appear on `Andries Brouwer's database
1635
+ <https://www.win.tue.nl/~aeb/graphs/srg/srgtab.html>`__ and are built by
1636
+ adding an isolated vertex to a
1637
+ :meth:`~sage.graphs.graph_generators.GraphGenerators.OrthogonalArrayBlockGraph`,
1638
+ and a :meth:`Seidel switching <Graph.seidel_switching>` a set of disjoint
1639
+ `n`-cocliques.
1640
+
1641
+ INPUT:
1642
+
1643
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
1644
+
1645
+ OUTPUT:
1646
+
1647
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if the
1648
+ parameters match, and ``None`` otherwise.
1649
+
1650
+ EXAMPLES::
1651
+
1652
+ sage: graphs.strongly_regular_graph(170, 78, 35, 36) # indirect doctest # needs sage.combinat sage.modules
1653
+ Graph on 170 vertices
1654
+
1655
+ TESTS::
1656
+
1657
+ sage: from sage.graphs.strongly_regular_db import is_switch_OA_srg
1658
+ sage: t = is_switch_OA_srg(5,5,5,5); t
1659
+ sage: t = is_switch_OA_srg(170, 78, 35, 36) # needs sage.schemes
1660
+ sage: t[0](*t[1:]).is_strongly_regular(parameters=True) # needs sage.schemes
1661
+ (170, 78, 35, 36)
1662
+ sage: t = is_switch_OA_srg(290, 136, 63, 64) # needs sage.schemes
1663
+ sage: t[0](*t[1:]).is_strongly_regular(parameters=True) # needs sage.schemes
1664
+ (290, 136, 63, 64)
1665
+ sage: is_switch_OA_srg(626, 300, 143, 144) # needs sage.schemes
1666
+ (<...switch_OA_srg..., 12, 25)
1667
+ sage: is_switch_OA_srg(842, 406, 195, 196) # needs sage.schemes
1668
+ (<...switch_OA_srg..., 14, 29)
1669
+ """
1670
+ cdef int n_2_p_1 = v
1671
+ cdef int n = <int> floor(sqrt(n_2_p_1 - 1))
1672
+
1673
+ if n*n != n_2_p_1 - 1: # is it a square?
1674
+ return None
1675
+
1676
+ cdef int c = k//n
1677
+ if (k % n or l != c*c-1 or k != 1+(c-1)*(c+1)+(n-c)*(n-c-1) or
1678
+ orthogonal_array(c+1, n, existence=True, resolvable=True) is not True):
1679
+ return None
1680
+
1681
+ def switch_OA_srg(c, n):
1682
+ OA = [tuple(x) for x in orthogonal_array(c+1, n, resolvable=True)]
1683
+ g = Graph([OA, lambda x, y: any(xx == yy for xx, yy in zip(x, y))],
1684
+ loops=False)
1685
+ g.add_vertex(0)
1686
+ g.seidel_switching(OA[:c*n])
1687
+ return g
1688
+
1689
+ return (switch_OA_srg, c, n)
1690
+
1691
+
1692
+ def is_nowhere0_twoweight(int v, int k, int l, int mu):
1693
+ r"""
1694
+ Test whether some graph of nowhere 0 words is `(v,k,\lambda,\mu)`-strongly regular.
1695
+
1696
+ Test whether a :meth:`~sage.graphs.graph_generators.GraphGenerators.Nowhere0WordsTwoWeightCodeGraph`
1697
+ is `(v,k,\lambda,\mu)`-strongly regular.
1698
+
1699
+ INPUT:
1700
+
1701
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
1702
+
1703
+ OUTPUT:
1704
+
1705
+ A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if the
1706
+ parameters match, and ``None`` otherwise.
1707
+
1708
+ EXAMPLES::
1709
+
1710
+ sage: graphs.strongly_regular_graph(196, 60, 14, 20) # needs sage.combinat sage.modules
1711
+ Nowhere0WordsTwoWeightCodeGraph(8): Graph on 196 vertices
1712
+
1713
+ TESTS::
1714
+
1715
+ sage: from sage.graphs.strongly_regular_db import is_nowhere0_twoweight
1716
+ sage: t = is_nowhere0_twoweight(1800, 728, 268, 312); t # needs sage.libs.pari
1717
+ (<function Nowhere0WordsTwoWeightCodeGraph at ...>, 16)
1718
+ sage: t = is_nowhere0_twoweight(5,5,5,5); t # needs sage.libs.pari
1719
+ """
1720
+ from sage.graphs.generators.classical_geometries import Nowhere0WordsTwoWeightCodeGraph
1721
+ cdef int q
1722
+ r, s = eigenvalues(v, k, l, mu)
1723
+ if r is None:
1724
+ return
1725
+ if r < s:
1726
+ r, s = s, r
1727
+ q = r*2
1728
+ if (q > 4 and is_prime_power(q) and not r % 2 and
1729
+ v == r*(q-1)**2 and
1730
+ 4*k == q*(q-2)*(q-3) and
1731
+ 8*mu == q*(q-3)*(q-4)):
1732
+ return (Nowhere0WordsTwoWeightCodeGraph, q)
1733
+
1734
+
1735
+ cdef eigenvalues(int v, int k, int l, int mu):
1736
+ r"""
1737
+ Return the eigenvalues of a (v,k,l,mu)-strongly regular graph.
1738
+
1739
+ If the set of parameters is not feasible, or if they correspond to a
1740
+ conference graph, the function returns ``(None,None)``. Otherwise
1741
+ it returns the pair [r,s] of eigenvalues, satisfying r>s.
1742
+
1743
+ INPUT:
1744
+
1745
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
1746
+ """
1747
+ # See 1.3.1 of [Distance-regular graphs]
1748
+ b = (mu-l)
1749
+ c = (mu-k)
1750
+ D = b**2-4*c
1751
+ if not is_square(D):
1752
+ return [None, None]
1753
+ return [(-b+sqrt(D))/2.0,
1754
+ (-b-sqrt(D))/2.0]
1755
+
1756
+
1757
+ def eigenmatrix(int v, int k, int l, int mu):
1758
+ r"""
1759
+ Return the first eigenmatrix of a `(v,k,l,mu)`-strongly regular graph.
1760
+
1761
+ The adjacency matrix `A` of an s.r.g. commutes with the adjacency matrix
1762
+ `A'=J-A-I` of its complement (here `J` is all-1 matrix, and `I` the identity
1763
+ matrix). Thus, they can be simultaneously diagonalized and so `A` and `A'`
1764
+ share eigenspaces.
1765
+
1766
+ The eigenvalues of `J` are `v` with multiplicity 1, and 0 with multiplicity
1767
+ `v-1`. Thus the eigenvalue of `A'` corresponding to the 1-dimension
1768
+ `k`-eigenspace of `A` is `v-k-1`. Respectively, the eigenvalues of `A'`
1769
+ corresponding to `t`-eigenspace of `A`, with `t` unequal to `k`, equals
1770
+ `-t-1`. The 1st eigenmatrix `P` of the C-algebra `C[A]` generated by `A`
1771
+ encodes this eigenvalue information in its three columns;
1772
+ the 2nd (resp. 3rd)
1773
+ column contains distinct eigenvalues of `A` (resp. of `A'`), and the 1st
1774
+ column contains the corresponding eigenvalues of `I`. The matrix `vP^{-1}`
1775
+ is called the 2nd eigenvalue matrix of `C[A]`.
1776
+
1777
+ The most interesting feature of `vP^{-1}` is that it is the 1st eigenmatrix
1778
+ of the dual of `C[A]` if the dual is generated by the adjacency matrix of a
1779
+ strongly regular graph. See [BH2012]_ and [BI1984]_ for details.
1780
+
1781
+ If the set of parameters is not feasible, or if they correspond to a
1782
+ conference graph, the function returns ``None``. Its output is stable, assuming
1783
+ that the eigenvalues r,s used satisfy r>s; this holds for the current
1784
+ implementation of eigenvalues().
1785
+
1786
+ INPUT:
1787
+
1788
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
1789
+
1790
+ EXAMPLES:
1791
+
1792
+ Petersen's graph's C-algebra does not have a dual coming from an s.r.g.::
1793
+
1794
+ sage: from sage.graphs.strongly_regular_db import eigenmatrix
1795
+ sage: P = eigenmatrix(10,3,0,1); P # needs sage.modules
1796
+ [ 1 3 6]
1797
+ [ 1 1 -2]
1798
+ [ 1 -2 1]
1799
+ sage: 10*P^-1 # needs sage.modules
1800
+ [ 1 5 4]
1801
+ [ 1 5/3 -8/3]
1802
+ [ 1 -5/3 2/3]
1803
+
1804
+ The line graph of `K_{3,3}` is self-dual::
1805
+
1806
+ sage: P = eigenmatrix(9,4,1,2); P # needs sage.modules
1807
+ [ 1 4 4]
1808
+ [ 1 1 -2]
1809
+ [ 1 -2 1]
1810
+ sage: 9*P^-1 # needs sage.modules
1811
+ [ 1 4 4]
1812
+ [ 1 1 -2]
1813
+ [ 1 -2 1]
1814
+
1815
+ A strongly regular graph with a non-isomorphic dual coming from another
1816
+ strongly regular graph::
1817
+
1818
+ sage: # needs sage.modules
1819
+ sage: graphs.strongly_regular_graph(243,220,199,200, existence=True) # needs sage.combinat
1820
+ True
1821
+ sage: graphs.strongly_regular_graph(243,110,37,60, existence=True) # needs sage.combinat
1822
+ True
1823
+ sage: P = eigenmatrix(243,220,199,200); P
1824
+ [ 1 220 22]
1825
+ [ 1 4 -5]
1826
+ [ 1 -5 4]
1827
+ sage: 243*P^-1
1828
+ [ 1 110 132]
1829
+ [ 1 2 -3]
1830
+ [ 1 -25 24]
1831
+ sage: 243*P^-1==eigenmatrix(243,110,37,60)
1832
+ True
1833
+
1834
+ TESTS::
1835
+
1836
+ sage: eigenmatrix(5,5,5,-5)
1837
+ """
1838
+ from sage.rings.integer_ring import ZZ
1839
+ r, s = eigenvalues(v, k, l, mu)
1840
+ if r is not None:
1841
+ return Matrix(ZZ, [[1, k, v-k-1], [1, r, -r-1], [1, s, -s-1]])
1842
+
1843
+
1844
+ cpdef latin_squares_graph_parameters(int v, int k, int l, int mu):
1845
+ r"""
1846
+ Check whether (v,k,l,mu)-strongly regular graph has parameters of an `L_g(n)` s.r.g.
1847
+
1848
+ Also known as pseudo-OA(n,g) case, i.e. s.r.g. with parameters of an OA(n,g)-graph.
1849
+ Return g and n, if they exist. See Sect. 9.1 of [BH2012]_ for details.
1850
+
1851
+ INPUT:
1852
+
1853
+ - ``v``, ``k``, ``l``, ``mu`` -- - (integrs) parameters of the graph
1854
+
1855
+ OUTPUT:
1856
+
1857
+ - ``(g, n)`` -- parameters of an `L_g(n)` graph, or ``None``
1858
+
1859
+ TESTS::
1860
+
1861
+ sage: from sage.graphs.strongly_regular_db import latin_squares_graph_parameters
1862
+ sage: latin_squares_graph_parameters(9,4,1,2)
1863
+ (2, 3)
1864
+ sage: latin_squares_graph_parameters(5,4,1,2)
1865
+ """
1866
+ cdef int g, n
1867
+ r, s = eigenvalues(v, k, l, mu)
1868
+ if r is None:
1869
+ return
1870
+ if r < s:
1871
+ r, s = s, r
1872
+ g = -s
1873
+ n = r+g
1874
+ if v == n**2 and k == g*(n-1) and l == (g-1)*(g-2)+n-2 and mu == g*(g-1):
1875
+ return g, n
1876
+ return
1877
+
1878
+
1879
+ def _H_3_cayley_graph(L):
1880
+ r"""
1881
+ Return the `L`-Cayley graph of the group `H_3` from Prop. 12 in [JK2003]_.
1882
+
1883
+ INPUT:
1884
+
1885
+ - the list of words for the generating set in the format ["abc",...,"xyz"] for
1886
+ a,b,...,z being integers between 0 and 4.
1887
+
1888
+ TESTS::
1889
+
1890
+ sage: from sage.graphs.strongly_regular_db import _H_3_cayley_graph
1891
+ sage: _H_3_cayley_graph(["100","110","130","140","200","230","240","300"]) # needs sage.groups
1892
+ Graph on 100 vertices
1893
+ """
1894
+ from sage.groups.free_group import FreeGroup
1895
+ from sage.groups.finitely_presented import FinitelyPresentedGroup
1896
+ G = FreeGroup('x,y,z')
1897
+ x, y, z = G.gens()
1898
+ rels = (x**5, y**5, z**4, x*y*x**(-1)*y**(-1), z*x*z**(-1)*x**(-2), z*y*z**(-1)*y**(-2))
1899
+ G = FinitelyPresentedGroup(G, rels)
1900
+ x, y, z = G.gens()
1901
+ H = G.as_permutation_group()
1902
+ L = [[int(u) for u in x] for x in L]
1903
+ x, y, z = (H.gen(0), H.gen(1), H.gen(2))
1904
+ L = [H(x**xx*y**yy*z**zz) for xx, yy, zz in L]
1905
+ return Graph(H.cayley_graph(generators=L, simple=True))
1906
+
1907
+
1908
+ def SRG_100_44_18_20():
1909
+ r"""
1910
+ Return a `(100, 44, 18, 20)`-strongly regular graph.
1911
+
1912
+ This graph is built as a Cayley graph, using the construction for `\Delta_1`
1913
+ with group `H_3` presented in Table 8.1 of [JK2003]_
1914
+
1915
+ EXAMPLES::
1916
+
1917
+ sage: from sage.graphs.strongly_regular_db import SRG_100_44_18_20
1918
+ sage: G = SRG_100_44_18_20() # long time # needs sage.groups
1919
+ sage: G.is_strongly_regular(parameters=True) # long time # needs sage.groups
1920
+ (100, 44, 18, 20)
1921
+ """
1922
+ L = ['100', '110', '130', '140', '200', '230', '240', '300', '310', '320',
1923
+ '400', '410', '420', '440', '041', '111', '221', '231', '241', '321',
1924
+ '331', '401', '421', '441', '002', '042', '112', '122', '142', '212',
1925
+ '232', '242', '322', '342', '033', '113', '143', '223', '303', '333',
1926
+ '343', '413', '433', '443']
1927
+ return _H_3_cayley_graph(L)
1928
+
1929
+
1930
+ def SRG_100_45_20_20():
1931
+ r"""
1932
+ Return a `(100, 45, 20, 20)`-strongly regular graph.
1933
+
1934
+ This graph is built as a Cayley graph, using the construction for `\Gamma_3`
1935
+ with group `H_3` presented in Table 8.1 of [JK2003]_.
1936
+
1937
+ EXAMPLES::
1938
+
1939
+ sage: from sage.graphs.strongly_regular_db import SRG_100_45_20_20
1940
+ sage: G = SRG_100_45_20_20() # long time # needs sage.groups
1941
+ sage: G.is_strongly_regular(parameters=True) # long time # needs sage.groups
1942
+ (100, 45, 20, 20)
1943
+ """
1944
+ L = ['120', '140', '200', '210', '201', '401', '411', '321', '002', '012',
1945
+ '022', '042', '303', '403', '013', '413', '240', '031', '102', '323',
1946
+ '300', '231', '132', '133', '310', '141', '142', '233', '340', '241',
1947
+ '202', '333', '410', '341', '222', '433', '430', '441', '242', '302',
1948
+ '312', '322', '332', '442', '143']
1949
+ return _H_3_cayley_graph(L)
1950
+
1951
+
1952
+ def SRG_105_32_4_12():
1953
+ r"""
1954
+ Return a `(105, 32, 4, 12)`-strongly regular graph.
1955
+
1956
+ The vertices are the flags of the projective plane of order 4. Two flags
1957
+ `(a,A)` and `(b,B)` are adjacent if the point `a` is on the line `B` or
1958
+ the point `b` is on the line `A`, and `a \neq b`, `A \neq B`. See
1959
+ Theorem 2.7 in [GS1970]_, and [Coo2006]_.
1960
+
1961
+ EXAMPLES::
1962
+
1963
+ sage: from sage.graphs.strongly_regular_db import SRG_105_32_4_12
1964
+ sage: G = SRG_105_32_4_12(); G # needs sage.groups sage.rings.finite_rings
1965
+ Aut L(3,4) on flags: Graph on 105 vertices
1966
+ sage: G.is_strongly_regular(parameters=True) # needs sage.groups sage.rings.finite_rings
1967
+ (105, 32, 4, 12)
1968
+ """
1969
+ from sage.combinat.designs.block_design import ProjectiveGeometryDesign
1970
+ P = ProjectiveGeometryDesign(2, 1, GF(4, 'a'))
1971
+ IG = P.incidence_graph().line_graph()
1972
+ a = IG.automorphism_group()
1973
+ h = a.stabilizer(a.domain()[0])
1974
+ o = next(x for x in h.orbits() if len(x) == 32)[0]
1975
+ e = a.orbit((a.domain()[0], o), action='OnSets')
1976
+ G = Graph()
1977
+ G.add_edges(e)
1978
+ G.name('Aut L(3,4) on flags')
1979
+ return G
1980
+
1981
+
1982
+ def SRG_120_77_52_44():
1983
+ r"""
1984
+ Return a `(120,77,52,44)`-strongly regular graph.
1985
+
1986
+ To build this graph, we first build a `2-(21,7,12)` design, by removing two
1987
+ points from the :func:`~sage.combinat.designs.block_design.WittDesign` on 23
1988
+ points. We then build the intersection graph of blocks with intersection
1989
+ size 3.
1990
+
1991
+ EXAMPLES::
1992
+
1993
+ sage: from sage.graphs.strongly_regular_db import SRG_120_77_52_44
1994
+ sage: G = SRG_120_77_52_44() # optional - gap_package_design
1995
+ sage: G.is_strongly_regular(parameters=True) # optional - gap_package_design
1996
+ (120, 77, 52, 44)
1997
+ """
1998
+ from sage.combinat.designs.block_design import WittDesign
1999
+ from sage.combinat.designs.incidence_structures import IncidenceStructure
2000
+ W = WittDesign(23)
2001
+ H = IncidenceStructure([x for x in W if 22 not in x and 21 not in x])
2002
+ g = H.intersection_graph(3)
2003
+ g.name('PG(2,2)s in PG(2,4)')
2004
+ return g
2005
+
2006
+
2007
+ def SRG_144_39_6_12():
2008
+ r"""
2009
+ Return a `(144,39,6,12)`-strongly regular graph.
2010
+
2011
+ This graph is obtained as an orbit of length 2808 on sets of cardinality 2
2012
+ (among 2 such orbits) of the group `PGL_3(3)` acting on the (right) cosets of
2013
+ a subgroup of order 39.
2014
+
2015
+ EXAMPLES::
2016
+
2017
+ sage: from sage.graphs.strongly_regular_db import SRG_144_39_6_12
2018
+ sage: G = SRG_144_39_6_12() # needs sage.libs.gap
2019
+ sage: G.is_strongly_regular(parameters=True) # needs sage.libs.gap
2020
+ (144, 39, 6, 12)
2021
+ """
2022
+ from sage.libs.gap.libgap import libgap
2023
+ g = libgap.ProjectiveGeneralLinearGroup(3, 3)
2024
+ ns = g.Normalizer(g.SylowSubgroup(13))
2025
+ G = g.Action(g.RightCosets(ns), libgap.OnRight)
2026
+ H = G.Stabilizer(1)
2027
+ for o in H.Orbits():
2028
+ if len(o) != 39:
2029
+ continue
2030
+ h = Graph()
2031
+ h.add_edges(G.Orbit([1, o[0]], libgap.OnSets))
2032
+ if h.is_strongly_regular():
2033
+ h.relabel()
2034
+ h.name('PGL_3(3) on cosets of 13:3')
2035
+ return h
2036
+
2037
+
2038
+ def SRG_176_49_12_14():
2039
+ r"""
2040
+ Return a `(176,49,12,14)`-strongly regular graph.
2041
+
2042
+ This graph is built from the symmetric Higman-Sims design. In
2043
+ [Bro1982]_, it is explained that there exists an involution
2044
+ `\sigma` exchanging the points and blocks of the Higman-Sims design, such
2045
+ that each point is mapped on a block that contains it (i.e. `\sigma` is a
2046
+ 'polarity with all universal points'). The graph is then built by making two
2047
+ vertices `u,v` adjacent whenever `v\in \sigma(u)`.
2048
+
2049
+ EXAMPLES::
2050
+
2051
+ sage: from sage.graphs.strongly_regular_db import SRG_176_49_12_14
2052
+ sage: G = SRG_176_49_12_14() # long time, optional - gap_package_design
2053
+ sage: G.is_strongly_regular(parameters=True) # long time, optional - gap_package_design
2054
+ (176, 49, 12, 14)
2055
+ """
2056
+ from sage.combinat.designs.database import HigmanSimsDesign
2057
+ d = HigmanSimsDesign()
2058
+ g = d.incidence_graph(labels=True)
2059
+ ag = g.automorphism_group().conjugacy_classes_representatives()
2060
+
2061
+ # Looking for an involution that maps a point of the design to one of the
2062
+ # blocks that contains it. It is called a polarity with only absolute
2063
+ # points.
2064
+ for aut in ag:
2065
+ try:
2066
+ 0 in aut(0)
2067
+ except TypeError:
2068
+ continue
2069
+ if (aut.order() == 2 and
2070
+ all(i in aut(i) for i in d.ground_set())):
2071
+ g = Graph()
2072
+ g.add_edges(((u, v) for u in d.ground_set() for v in aut(u)), loops=False)
2073
+ g.name('Higman symmetric 2-design')
2074
+ return g
2075
+
2076
+
2077
+ def SRG_176_105_68_54():
2078
+ r"""
2079
+ Return a `(176, 105, 68, 54)`-strongly regular graph.
2080
+
2081
+ To build this graph, we first build a `2-(22,7,16)` design, by removing one
2082
+ point from the :func:`~sage.combinat.designs.block_design.WittDesign` on 23
2083
+ points. We then build the intersection graph of blocks with intersection
2084
+ size 3. Known as S.7 in [Hub1975]_.
2085
+
2086
+ EXAMPLES::
2087
+
2088
+ sage: from sage.graphs.strongly_regular_db import SRG_176_105_68_54
2089
+ sage: G = SRG_176_105_68_54() # optional - gap_package_design
2090
+ sage: G.is_strongly_regular(parameters=True) # optional - gap_package_design
2091
+ (176, 105, 68, 54)
2092
+ """
2093
+ from sage.combinat.designs.block_design import WittDesign
2094
+ from sage.combinat.designs.incidence_structures import IncidenceStructure
2095
+ W = WittDesign(23)
2096
+ H = IncidenceStructure([x for x in W if 22 not in x])
2097
+ g = H.intersection_graph(3)
2098
+ g.name('Witt 3-(22,7,4)')
2099
+ return g
2100
+
2101
+
2102
+ def SRG_210_99_48_45():
2103
+ r"""
2104
+ Return a strongly regular graph with parameters `(210, 99, 48, 45)`.
2105
+
2106
+ This graph is from Example 4.2 in [KPRWZ2010]_. One considers the action of
2107
+ the symmetric group `S_7` on the 210 digraphs isomorphic to the
2108
+ disjoint union of `K_1` and the circulant 6-vertex digraph
2109
+ ``digraphs.Circulant(6,[1,4])``. It has 16 orbitals; the package [FK1991]_
2110
+ found a megring of them, explicitly described in [KPRWZ2010]_, resulting in
2111
+ this graph.
2112
+
2113
+ EXAMPLES::
2114
+
2115
+ sage: from sage.graphs.strongly_regular_db import SRG_210_99_48_45
2116
+ sage: g = SRG_210_99_48_45() # needs sage.libs.gap
2117
+ sage: g.is_strongly_regular(parameters=True) # needs sage.libs.gap
2118
+ (210, 99, 48, 45)
2119
+ """
2120
+ from sage.libs.gap.libgap import libgap
2121
+ from sage.combinat.permutation import Permutation
2122
+
2123
+ def ekg(g0):
2124
+ # return arcs of the Cayley digraph of <g> on {g,g^4}
2125
+ g = Permutation(g0)
2126
+ return libgap.Set([(x, g(x)) for x in range(1, 8)] +
2127
+ [(x, g(g(g(g(x))))) for x in range(1, 8)])
2128
+
2129
+ kd = list(map(ekg,
2130
+ [(7, 1, 2, 3, 4, 5), (7, 1, 3, 4, 5, 6),
2131
+ (7, 3, 4, 5, 6, 2), (7, 1, 4, 3, 5, 6),
2132
+ (7, 3, 1, 4, 5, 6), (7, 2, 4, 3, 5, 6),
2133
+ (7, 3, 2, 4, 5, 1), (7, 2, 4, 3, 5, 1)]))
2134
+ s = libgap.SymmetricGroup(7)
2135
+ O = s.Orbit(kd[0], libgap.OnSetsTuples)
2136
+ sa = s.Action(O, libgap.OnSetsTuples)
2137
+ G = Graph()
2138
+ for g in kd[1:]:
2139
+ G.add_edges(libgap.Orbit(sa, [libgap.Position(O, kd[0]),
2140
+ libgap.Position(O, g)], libgap.OnSets))
2141
+ G.name('merging of S_7 on Circulant(6,[1,4])s')
2142
+ return G
2143
+
2144
+
2145
+ def SRG_243_110_37_60():
2146
+ r"""
2147
+ Return a `(243, 110, 37, 60)`-strongly regular graph.
2148
+
2149
+ Consider the orthogonal complement of the
2150
+ :func:`~sage.coding.code_constructions.TernaryGolayCode`, which has 243
2151
+ words. On them we define a graph, in which two words are adjacent
2152
+ whenever their Hamming distance is 9. This construction appears in
2153
+ [GS1975]_.
2154
+
2155
+ .. NOTE::
2156
+
2157
+ A strongly regular graph with the same parameters is also obtained from
2158
+ the database of 2-weight codes.
2159
+
2160
+ EXAMPLES::
2161
+
2162
+ sage: from sage.graphs.strongly_regular_db import SRG_243_110_37_60
2163
+ sage: G = SRG_243_110_37_60() # needs sage.modules sage.rings.finite_rings
2164
+ sage: G.is_strongly_regular(parameters=True) # needs sage.modules sage.rings.finite_rings
2165
+ (243, 110, 37, 60)
2166
+ """
2167
+ from sage.coding.golay_code import GolayCode
2168
+ M = GolayCode(GF(3), False).generator_matrix()
2169
+ V = list(M.right_kernel())
2170
+ g = Graph([list(range(len(V))), lambda x, y: (V[x] - V[y]).hamming_weight() == 9])
2171
+ g.name('Ternary Golay code')
2172
+ return g
2173
+
2174
+
2175
+ def SRG_253_140_87_65():
2176
+ r"""
2177
+ Return a `(253, 140, 87, 65)`-strongly regular graph.
2178
+
2179
+ To build this graph, we first build the
2180
+ :func:`~sage.combinat.designs.block_design.WittDesign` on 23 points which is
2181
+ a `2-(23,7,21)` design. We then build the intersection graph of blocks with
2182
+ intersection size 3. Known as S.6 in [Hub1975]_.
2183
+
2184
+ EXAMPLES::
2185
+
2186
+ sage: from sage.graphs.strongly_regular_db import SRG_253_140_87_65
2187
+ sage: G = SRG_253_140_87_65() # optional - gap_package_design
2188
+ sage: G.is_strongly_regular(parameters=True) # optional - gap_package_design
2189
+ (253, 140, 87, 65)
2190
+ """
2191
+ from sage.combinat.designs.block_design import WittDesign
2192
+ from sage.combinat.designs.incidence_structures import IncidenceStructure
2193
+ W = WittDesign(23)
2194
+ g = W.intersection_graph(3)
2195
+ g.name('Witt 4-(23,7,1)')
2196
+ return g
2197
+
2198
+
2199
+ def SRG_196_91_42_42():
2200
+ r"""
2201
+ Return a `(196,91,42,42)`-strongly regular graph.
2202
+
2203
+ This strongly regular graph is built following the construction provided in
2204
+ Corollary 8.2.27 of [IS2006]_.
2205
+
2206
+ EXAMPLES::
2207
+
2208
+ sage: from sage.graphs.strongly_regular_db import SRG_196_91_42_42
2209
+ sage: G = SRG_196_91_42_42()
2210
+ sage: G.is_strongly_regular(parameters=True)
2211
+ (196, 91, 42, 42)
2212
+ """
2213
+ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing
2214
+ from sage.graphs.generators.intersection import IntersectionGraph
2215
+ k = 7
2216
+ R = IntegerModRing(91)
2217
+ A = list(map(R, [0, 10, 27, 28, 31, 43, 50]))
2218
+ B = list(map(R, [0, 11, 20, 25, 49, 55, 57]))
2219
+ H = list(map(R, [13 * i for i in range(k)]))
2220
+ U = list(map(frozenset, [[x + z for x in A] for z in R]))
2221
+ V = list(map(frozenset, [[x + z for x in B] for z in R]))
2222
+ W = list(map(frozenset, [[x + z for x in H] for z in R]))
2223
+ G = IntersectionGraph(U + V + W)
2224
+
2225
+ G.seidel_switching(U)
2226
+
2227
+ G.add_edges((-1, x) for x in U)
2228
+ G.relabel(perm={u: i for i, u in enumerate(G)})
2229
+ G.name('RSHCD+')
2230
+ return G
2231
+
2232
+
2233
+ def SRG_220_84_38_28():
2234
+ r"""
2235
+ Return a `(220, 84, 38, 28)`-strongly regular graph.
2236
+
2237
+ This graph is obtained from the
2238
+ :meth:`~IncidenceStructure.intersection_graph` of a
2239
+ :func:`~sage.combinat.designs.database.BIBD_45_9_8`. This construction
2240
+ appears in VII.11.2 from [DesignHandbook]_
2241
+
2242
+ EXAMPLES::
2243
+
2244
+ sage: from sage.graphs.strongly_regular_db import SRG_220_84_38_28
2245
+ sage: g = SRG_220_84_38_28()
2246
+ sage: g.is_strongly_regular(parameters=True)
2247
+ (220, 84, 38, 28)
2248
+ """
2249
+ from sage.combinat.designs.database import BIBD_45_9_8
2250
+ from sage.combinat.designs.incidence_structures import IncidenceStructure
2251
+ G = IncidenceStructure(BIBD_45_9_8()).intersection_graph(3)
2252
+ G.relabel()
2253
+ G.name('Tonchev: quasisymmetric 2-(45,9,8)')
2254
+ return G
2255
+
2256
+
2257
+ def SRG_276_140_58_84():
2258
+ r"""
2259
+ Return a `(276, 140, 58, 84)`-strongly regular graph.
2260
+
2261
+ The graph is built from
2262
+ :meth:`~sage.graphs.graph_generators.GraphGenerators.McLaughlinGraph`, with
2263
+ an added isolated vertex. We then perform a
2264
+ :meth:`~Graph.seidel_switching` on a set of 28 disjoint 5-cliques, which
2265
+ exist by cf. [HT1996]_.
2266
+
2267
+ EXAMPLES::
2268
+
2269
+ sage: from sage.graphs.strongly_regular_db import SRG_276_140_58_84
2270
+ sage: g = SRG_276_140_58_84() # long time, optional - gap_package_design
2271
+ sage: g.is_strongly_regular(parameters=True) # long time, optional - gap_package_design
2272
+ (276, 140, 58, 84)
2273
+ """
2274
+ from sage.graphs.generators.smallgraphs import McLaughlinGraph
2275
+ g = McLaughlinGraph()
2276
+ C = [[ 0, 72, 87, 131, 136], [ 1, 35, 61, 102, 168], [ 2, 32, 97, 125, 197], [ 3, 22, 96, 103, 202],
2277
+ [ 4, 46, 74, 158, 229], [ 5, 83, 93, 242, 261], [ 6, 26, 81, 147, 176], [ 7, 42, 63, 119, 263],
2278
+ [ 8, 49, 64, 165, 227], [ 9, 70, 85, 208, 273], [10, 73, 92, 230, 268], [11, 54, 95, 184, 269],
2279
+ [12, 55, 62, 185, 205], [13, 51, 65, 162, 254], [14, 78, 88, 231, 274], [15, 40, 59, 117, 252],
2280
+ [16, 24, 71, 137, 171], [17, 39, 43, 132, 163], [18, 57, 79, 175, 271], [19, 68, 80, 217, 244],
2281
+ [20, 75, 98, 239, 267], [21, 33, 56, 113, 240], [23, 127, 152, 164, 172], [25, 101, 128, 183, 264],
2282
+ [27, 129, 154, 160, 201], [28, 126, 144, 161, 228], [29, 100, 133, 204, 266], [30, 108, 146, 200, 219]]
2283
+ g.add_vertex(-1)
2284
+ g.seidel_switching(sum(C, []))
2285
+ g.relabel()
2286
+ g.name('Haemers-Tonchev')
2287
+ return g
2288
+
2289
+
2290
+ def SRG_280_135_70_60():
2291
+ r"""
2292
+ Return a strongly regular graph with parameters `(280, 135, 70, 60)`.
2293
+
2294
+ This graph is built from the action of `J_2` on the cosets of a `3.PGL(2,9)`-subgroup.
2295
+
2296
+ EXAMPLES::
2297
+
2298
+ sage: from sage.graphs.strongly_regular_db import SRG_280_135_70_60
2299
+ sage: g=SRG_280_135_70_60() # long time, optional - internet
2300
+ sage: g.is_strongly_regular(parameters=True) # long time, optional - internet
2301
+ (280, 135, 70, 60)
2302
+ """
2303
+ from sage.libs.gap.libgap import libgap
2304
+ from sage.graphs.graph import Graph
2305
+
2306
+ libgap.load_package("AtlasRep")
2307
+ # A representation of J2 acting on a 3.PGL(2,9) it contains.
2308
+ J2 = libgap.AtlasGroup("J2", libgap.NrMovedPoints, 280)
2309
+ edges = J2.Orbit([1, 2], libgap.OnSets)
2310
+ g = Graph()
2311
+ g.add_edges(edges)
2312
+ g.relabel()
2313
+ g.name('J_2 on cosets of 3.PGL(2,9)')
2314
+ return g
2315
+
2316
+
2317
+ def SRG_280_117_44_52():
2318
+ r"""
2319
+ Return a strongly regular graph with parameters `(280, 117, 44, 52)`.
2320
+
2321
+ This graph is built according to a very pretty construction of Mathon and
2322
+ Rosa [MR1985]_:
2323
+
2324
+ The vertices of the graph `G` are all partitions of a set of 9 elements
2325
+ into `\{\{a,b,c\},\{d,e,f\},\{g,h,i\}\}`. The cross-intersection of two
2326
+ such partitions `P=\{P_1,P_2,P_3\}` and `P'=\{P'_1,P'_2,P'_3\}` being
2327
+ defined as `\{P_i \cap P'_j: 1\leq i,j\leq 3\}`, two vertices of `G` are
2328
+ set to be adjacent if the cross-intersection of their respective
2329
+ partitions does not contain exactly 7 nonempty sets.
2330
+
2331
+ EXAMPLES::
2332
+
2333
+ sage: from sage.graphs.strongly_regular_db import SRG_280_117_44_52
2334
+ sage: g=SRG_280_117_44_52()
2335
+ sage: g.is_strongly_regular(parameters=True)
2336
+ (280, 117, 44, 52)
2337
+ """
2338
+ from sage.graphs.hypergraph_generators import hypergraphs
2339
+
2340
+ # V is the set of partitions {{a,b,c},{d,e,f},{g,h,i}} of {0,...,8}
2341
+ H = hypergraphs.CompleteUniform(9, 3)
2342
+ g = H.intersection_graph()
2343
+ V = g.complement().cliques_maximal()
2344
+ V = [frozenset(u) for u in V]
2345
+
2346
+ # G is the graph defined on V in which two vertices are adjacent when they
2347
+ # corresponding partitions cross-intersect on 7 nonempty sets
2348
+ G = Graph([V, lambda x, y:
2349
+ sum(any(xxx in yy for xxx in xx) for xx in x for yy in y) != 7],
2350
+ loops=False)
2351
+ G.name('Mathon-Rosa')
2352
+ return G
2353
+
2354
+
2355
+ def strongly_regular_from_two_weight_code(L):
2356
+ r"""
2357
+ Return a strongly regular graph from a two-weight code.
2358
+
2359
+ A code is said to be a *two-weight* code the weight of its nonzero codewords
2360
+ (i.e. their number of nonzero coordinates) can only be one of two integer
2361
+ values `w_1,w_2`. It is said to be *projective* if the minimum weight of the
2362
+ dual code is `\geq 3`. A strongly regular graph can be built from a
2363
+ two-weight projective code with weights `w_1,w_2` (assuming `w_1<w_2`) by
2364
+ adding an edge between any two codewords whose difference has weight
2365
+ `w_1`. For more information, see [LS1981]_ or [Del1972]_.
2366
+
2367
+ INPUT:
2368
+
2369
+ - ``L`` -- a two-weight linear code, or its generating matrix
2370
+
2371
+ EXAMPLES::
2372
+
2373
+ sage: from sage.graphs.strongly_regular_db import strongly_regular_from_two_weight_code
2374
+ sage: x = ("100022021001111",
2375
+ ....: "010011211122000",
2376
+ ....: "001021112100011",
2377
+ ....: "000110120222220")
2378
+ sage: M = Matrix(GF(3),[list(l) for l in x]) # needs sage.modules sage.rings.finite_rings
2379
+ sage: G = strongly_regular_from_two_weight_code(LinearCode(M)) # needs sage.modules sage.rings.finite_rings
2380
+ sage: G.is_strongly_regular(parameters=True) # needs sage.modules sage.rings.finite_rings
2381
+ (81, 50, 31, 30)
2382
+ """
2383
+ from sage.structure.element import Matrix
2384
+ if isinstance(L, Matrix):
2385
+ L = LinearCode(L)
2386
+ V = [tuple(l) for l in L]
2387
+ w1, _ = sorted(set(sum(map(bool, x)) for x in V).difference([0]))
2388
+ G = Graph([V, lambda u, v: sum(uu != vv for uu, vv in zip(u, v)) == w1])
2389
+ G.relabel()
2390
+ G.name('two-weight code: '+str(L))
2391
+ return G
2392
+
2393
+
2394
+ def SRG_416_100_36_20():
2395
+ r"""
2396
+ Return a `(416,100,36,20)`-strongly regular graph.
2397
+
2398
+ This graph is obtained as an orbit on sets of cardinality 2
2399
+ (among 2 that exists) of the group `G_2(4)`.
2400
+ This graph is isomorphic to the subgraph of the from :meth:`Suzuki Graph
2401
+ <sage.graphs.graph_generators.GraphGenerators.SuzukiGraph>` induced on
2402
+ the neighbors of a vertex. Known as S.14 in [Hub1975]_.
2403
+
2404
+ EXAMPLES::
2405
+
2406
+ sage: from sage.graphs.strongly_regular_db import SRG_416_100_36_20
2407
+ sage: g = SRG_416_100_36_20() # long time, optional - internet, needs sage.libs.gap
2408
+ sage: g.is_strongly_regular(parameters=True) # long time, optional - internet, needs sage.libs.gap
2409
+ (416, 100, 36, 20)
2410
+ """
2411
+ from sage.libs.gap.libgap import libgap
2412
+ libgap.load_package("AtlasRep")
2413
+ g = libgap.AtlasGroup("G2(4)", libgap.NrMovedPoints, 416)
2414
+ h = Graph()
2415
+ h.add_edges(g.Orbit([1, 5],libgap.OnSets))
2416
+ h.relabel()
2417
+ h.name('G_2(4) on cosets of HS')
2418
+ return h
2419
+
2420
+
2421
+ def SRG_560_208_72_80():
2422
+ r"""
2423
+ Return a `(560,208,72,80)`-strongly regular graph.
2424
+
2425
+ This graph is obtained as the union of 4 orbits of sets of cardinality 2
2426
+ (among the 13 that exist) of the group `Sz(8)`.
2427
+
2428
+ EXAMPLES::
2429
+
2430
+ sage: from sage.graphs.strongly_regular_db import SRG_560_208_72_80
2431
+ sage: g = SRG_560_208_72_80() # not tested (~2s) # needs sage.libs.gap
2432
+ sage: g.is_strongly_regular(parameters=True) # not tested (~2s) # needs sage.libs.gap
2433
+ (560, 208, 72, 80)
2434
+ """
2435
+ from sage.libs.gap.libgap import libgap
2436
+ libgap.load_package("AtlasRep")
2437
+ g = libgap.AtlasGroup("Sz8", libgap.NrMovedPoints, 560)
2438
+
2439
+ h = Graph()
2440
+ h.add_edges(g.Orbit([1, 2],libgap.OnSets))
2441
+ h.add_edges(g.Orbit([1, 4],libgap.OnSets))
2442
+ h.add_edges(g.Orbit([1, 8],libgap.OnSets))
2443
+ h.add_edges(g.Orbit([1, 27],libgap.OnSets))
2444
+ h.relabel()
2445
+ h.name('Sz(8)-graph')
2446
+ return h
2447
+
2448
+
2449
+ def strongly_regular_from_two_intersection_set(M):
2450
+ r"""
2451
+ Return a strongly regular graph from a 2-intersection set.
2452
+
2453
+ A set of points in the projective geometry `PG(k,q)` is said to be a
2454
+ 2-intersection set if it intersects every hyperplane in either `h_1` or
2455
+ `h_2` points, where `h_1,h_2\in \\NN`.
2456
+
2457
+ From a 2-intersection set `S` can be defined a strongly-regular graph in the
2458
+ following way:
2459
+
2460
+ - Place the points of `S` on a hyperplane `H` in `PG(k+1,q)`
2461
+
2462
+ - Define the graph `G` on all points of `PG(k+1,q)\backslash H`
2463
+
2464
+ - Make two points of `V(G)=PG(k+1,q)\backslash H` adjacent if the line going
2465
+ through them intersects `S`
2466
+
2467
+ For more information, see e.g. [CD2013]_ where this explanation has been
2468
+ taken from.
2469
+
2470
+ INPUT:
2471
+
2472
+ - ``M`` -- a `|S| \times k` matrix with entries in `F_q` representing the points of
2473
+ the 2-intersection set. We assume that the first nonzero entry of each row is
2474
+ equal to `1`, that is, they give points in homogeneous coordinates.
2475
+
2476
+ The implementation does not check that `S` is actually a 2-intersection set.
2477
+
2478
+ EXAMPLES::
2479
+
2480
+ sage: from sage.graphs.strongly_regular_db import strongly_regular_from_two_intersection_set
2481
+ sage: S = Matrix([(0,0,1),(0,1,0)] + [(1,x^2,x) for x in GF(4,'b')]) # needs sage.modules sage.rings.finite_rings
2482
+ sage: g = strongly_regular_from_two_intersection_set(S); g # needs sage.modules sage.rings.finite_rings
2483
+ two-intersection set in PG(3,4): Graph on 64 vertices
2484
+ sage: g.is_strongly_regular(parameters=True) # needs sage.modules sage.rings.finite_rings
2485
+ (64, 18, 2, 6)
2486
+ """
2487
+ from itertools import product
2488
+ from sage.rings.rational_field import QQ
2489
+ K = M.base_ring()
2490
+ k = M.ncols()
2491
+ g = Graph()
2492
+
2493
+ M = [list(p) for p in M]
2494
+
2495
+ # For every point in F_q^{k+1} not on the hyperplane of M
2496
+ for u in [tuple(x) for x in product(K,repeat=k)]:
2497
+ # For every v point of M
2498
+ for v in M:
2499
+ # u is adjacent with all vertices on a uv line.
2500
+ g.add_edges([[u, tuple([u[i] + qq*v[i] for i in range(k)])]
2501
+ for qq in K if not qq == K.zero()])
2502
+ g.relabel()
2503
+ e = QQ((1,k))
2504
+ qq = g.num_verts()**e
2505
+ g.name('two-intersection set in PG('+str(k)+','+str(qq)+')')
2506
+ return g
2507
+
2508
+
2509
+ def SRG_120_63_30_36():
2510
+ r"""
2511
+ Return a `(120,63,30,36)`-strongly regular graph.
2512
+
2513
+ It is the distance-2 graph of :meth:`JohnsonGraph(10,3)
2514
+ <sage.graphs.graph_generators.GraphGenerators.JohnsonGraph>`.
2515
+
2516
+ EXAMPLES::
2517
+
2518
+ sage: from sage.graphs.strongly_regular_db import SRG_120_63_30_36
2519
+ sage: G = SRG_120_63_30_36()
2520
+ sage: G.is_strongly_regular(parameters=True)
2521
+ (120, 63, 30, 36)
2522
+ """
2523
+ from sage.graphs.generators.families import JohnsonGraph
2524
+ return JohnsonGraph(10, 3).distance_graph([2])
2525
+
2526
+
2527
+ def SRG_126_25_8_4():
2528
+ r"""
2529
+ Return a `(126,25,8,4)`-strongly regular graph.
2530
+
2531
+ It is the distance-(1 or 4) graph of :meth:`JohnsonGraph(9,4)
2532
+ <sage.graphs.graph_generators.GraphGenerators.JohnsonGraph>`.
2533
+
2534
+ EXAMPLES::
2535
+
2536
+ sage: from sage.graphs.strongly_regular_db import SRG_126_25_8_4
2537
+ sage: G = SRG_126_25_8_4()
2538
+ sage: G.is_strongly_regular(parameters=True)
2539
+ (126, 25, 8, 4)
2540
+ """
2541
+ from sage.graphs.generators.families import JohnsonGraph
2542
+ return JohnsonGraph(9, 4).distance_graph([1, 4])
2543
+
2544
+
2545
+ def SRG_175_72_20_36():
2546
+ r"""
2547
+ Return a `(175,72,20,36)`-strongly regular graph.
2548
+
2549
+ This graph is obtained from the line graph of
2550
+ :meth:`~sage.graphs.graph_generators.GraphGenerators.HoffmanSingletonGraph`. Setting
2551
+ two vertices to be adjacent if their distance in the line graph is exactly
2552
+ 2 yields the graph. For more information, see 10.B.(iv) in [BL1984]_ and
2553
+ https://www.win.tue.nl/~aeb/graphs/McL.html.
2554
+
2555
+ EXAMPLES::
2556
+
2557
+ sage: from sage.graphs.strongly_regular_db import SRG_175_72_20_36
2558
+ sage: G = SRG_175_72_20_36()
2559
+ sage: G.is_strongly_regular(parameters=True)
2560
+ (175, 72, 20, 36)
2561
+ """
2562
+ from sage.graphs.generators.smallgraphs import HoffmanSingletonGraph
2563
+ return HoffmanSingletonGraph().line_graph().distance_graph([2])
2564
+
2565
+
2566
+ def SRG_176_90_38_54():
2567
+ r"""
2568
+ Return a `(176,90,38,54)`-strongly regular graph.
2569
+
2570
+ This graph is obtained from
2571
+ :func:`~sage.graphs.strongly_regular_db.SRG_175_72_20_36`
2572
+ by attaching a isolated vertex and doing Seidel switching
2573
+ with respect to disjoint union of 18 maximum cliques, following
2574
+ a construction by W.Haemers given in Sect.10.B.(vi) of [BL1984]_.
2575
+
2576
+ EXAMPLES::
2577
+
2578
+ sage: from sage.graphs.strongly_regular_db import SRG_176_90_38_54
2579
+ sage: G = SRG_176_90_38_54(); G
2580
+ a Seidel switching of Distance graph for distance 2 in : Graph on 176 vertices
2581
+ sage: G.is_strongly_regular(parameters=True)
2582
+ (176, 90, 38, 54)
2583
+ """
2584
+ g = SRG_175_72_20_36()
2585
+ g.relabel(range(175))
2586
+ # c=filter(lambda x: len(x)==5, g.cliques_maximal())
2587
+ # r=flatten(Hypergraph(c).packing()[:18]) # takes 3s, so we put the answer here
2588
+ r = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
2589
+ 20, 21, 22, 23, 24, 25, 28, 29, 32, 38, 39, 41, 42, 43, 47, 49, 50, 51,
2590
+ 52, 53, 55, 57, 61, 63, 65, 67, 69, 72, 75, 77, 79, 81, 84, 87, 88, 89,
2591
+ 92, 95, 96, 97, 99, 101, 102, 104, 105, 107, 112, 114, 117, 118, 123,
2592
+ 125, 129, 132, 139, 140, 141, 144, 146, 147, 153, 154, 162, 165, 166,
2593
+ 167, 170, 172, 173, 174]
2594
+ g.add_vertex()
2595
+ g.seidel_switching(r)
2596
+ g.name('a Seidel switching of ' + g.name())
2597
+ return g
2598
+
2599
+
2600
+ def SRG_630_85_20_10():
2601
+ r"""
2602
+ Return a `(630,85,20,10)`-strongly regular graph.
2603
+
2604
+ This graph is the line graph of `pg(5,18,2)`; its point graph is
2605
+ :func:`~sage.graphs.strongly_regular_db.SRG_175_72_20_36`.
2606
+ One selects a subset of 630 maximum cliques in the latter following
2607
+ a construction by W.Haemers given in Sect.10.B.(v) of [BL1984]_.
2608
+
2609
+ EXAMPLES::
2610
+
2611
+ sage: from sage.graphs.strongly_regular_db import SRG_630_85_20_10
2612
+ sage: G = SRG_630_85_20_10() # long time # needs sage.groups
2613
+ sage: G.is_strongly_regular(parameters=True) # long time # needs sage.groups
2614
+ (630, 85, 20, 10)
2615
+ """
2616
+ from sage.graphs.generators.intersection import IntersectionGraph
2617
+ from sage.graphs.generators.smallgraphs import HoffmanSingletonGraph
2618
+ hs = HoffmanSingletonGraph()
2619
+ P = list(range(5)) + list(range(30, 35)) # a Petersen in hs
2620
+ mc = [0, 1, 5, 6, 12, 13, 16, 17, 22, 23, 29, 33, 39, 42, 47]
2621
+ assert hs.subgraph(mc).is_regular(k=0) # a maximum coclique
2622
+ assert hs.subgraph(P).is_regular(k=3)
2623
+ h = hs.automorphism_group().stabilizer(mc, action='OnSets')
2624
+ l = h.orbit(tuple((x[0], x[1]) for x in hs.subgraph(P).matching()),
2625
+ "OnSetsSets")
2626
+ return IntersectionGraph(l)
2627
+
2628
+
2629
+ def SRG_126_50_13_24():
2630
+ r"""
2631
+ Return a `(126,50,13,24)`-strongly regular graph.
2632
+
2633
+ This graph is a subgraph of
2634
+ :meth:`~sage.graphs.strongly_regular_db.SRG_175_72_20_36`.
2635
+ This construction, due to Goethals, is given in §10B.(vii) of [BL1984]_.
2636
+
2637
+ EXAMPLES::
2638
+
2639
+ sage: from sage.graphs.strongly_regular_db import SRG_126_50_13_24
2640
+ sage: G = SRG_126_50_13_24(); G
2641
+ Goethals graph: Graph on 126 vertices
2642
+ sage: G.is_strongly_regular(parameters=True)
2643
+ (126, 50, 13, 24)
2644
+ """
2645
+ from sage.graphs.generators.smallgraphs import HoffmanSingletonGraph
2646
+ hs = HoffmanSingletonGraph()
2647
+ s = set(hs.vertices(sort=False)).difference(hs.neighbors(0) + [0])
2648
+ g = SRG_175_72_20_36().subgraph(hs.edge_boundary(s, s))
2649
+ g.name('Goethals graph')
2650
+ return g
2651
+
2652
+
2653
+ def SRG_1288_792_476_504():
2654
+ r"""
2655
+ Return a `(1288, 792, 476, 504)`-strongly regular graph.
2656
+
2657
+ This graph is built on the words of weight 12 in the
2658
+ :func:`~sage.coding.code_constructions.BinaryGolayCode`. Two of them are
2659
+ then made adjacent if their symmetric difference has weight 12 (cf
2660
+ [BE1992]_).
2661
+
2662
+ .. SEEALSO::
2663
+
2664
+ :func:`strongly_regular_from_two_weight_code` -- build a strongly regular graph from
2665
+ a two-weight code.
2666
+
2667
+ EXAMPLES::
2668
+
2669
+ sage: from sage.graphs.strongly_regular_db import SRG_1288_792_476_504
2670
+ sage: G = SRG_1288_792_476_504() # long time # needs sage.rings.finite_rings
2671
+ sage: G.is_strongly_regular(parameters=True) # long time # needs sage.rings.finite_rings
2672
+ (1288, 792, 476, 504)
2673
+ """
2674
+ from sage.coding.golay_code import GolayCode
2675
+ C = GolayCode(GF(2), False)
2676
+ C = [[i for i,v in enumerate(c) if v]
2677
+ for c in C]
2678
+ C = [s for s in C if len(s) == 12]
2679
+ G = Graph([[frozenset(c) for c in C],
2680
+ lambda x, y: len(x.symmetric_difference(y)) == 12])
2681
+ G.relabel()
2682
+ G.name('binary Golay code')
2683
+ return G
2684
+
2685
+
2686
+ cdef bint seems_feasible(int v, int k, int l, int mu) noexcept:
2687
+ r"""
2688
+ Check if the set of parameters seems feasible.
2689
+
2690
+ INPUT:
2691
+
2692
+ - ``v``, ``k``, ``l``, ``mu`` -- integers
2693
+
2694
+ TESTS:
2695
+
2696
+ :issue:`32306` is fixed::
2697
+
2698
+ sage: from sage.graphs.strongly_regular_db import strongly_regular_graph
2699
+ sage: strongly_regular_graph(16384, 8256, 4160, 4160, existence=True) # needs sage.combinat sage.modules
2700
+ True
2701
+ """
2702
+ cdef uint_fast32_t tmp[2]
2703
+
2704
+ if (v < 0 or k <= 0 or l < 0 or mu < 0 or
2705
+ k >= v - 1 or l >= k or mu > k or
2706
+ v - 2*k + mu - 2 < 0 or # lambda of complement graph >=0
2707
+ v - 2*k + l < 0 or # μ of complement graph >= 0
2708
+ mu*(v - k - 1) != k*(k - l - 1)):
2709
+ return False
2710
+
2711
+ if mu == k: # complete multipartite graph
2712
+ r = v//(v-k) # number of parts (of size v-k each)
2713
+ return (l == (v-k)*(r-2) and v == r*(v-k))
2714
+
2715
+ if mu == 0: # the complement of a complete multipartite graph
2716
+ r = v//(k+1) # number of parts (of size k+1 each)
2717
+ return (l == k-1 and v == r*(k+1))
2718
+
2719
+ # Conference graphs. Only possible if 'v' is a sum of two squares (3.A of
2720
+ # [BL1984]_)
2721
+ if (v-1)*(mu-l)-2*k == 0:
2722
+ return two_squares_c(v, tmp)
2723
+
2724
+ rr, ss = eigenvalues(v, k, l, mu)
2725
+ if rr is None:
2726
+ return False
2727
+ r, s = rr, ss
2728
+
2729
+ # p.87 of [BL1984]_
2730
+ # "Integrality condition"
2731
+ if ((s+1)*(k-s)*k) % (mu*(s-r)) or ((r+1)*(k-r)*k) % (mu*(s-r)):
2732
+ return False
2733
+
2734
+ # Theorem 21.3 of [WilsonACourse] or
2735
+ # 3.B of [BL1984]_
2736
+ # (Krein conditions)
2737
+ if (r+1)*(k+r+2*r*s) > (k+r)*(s+1)**2 or (s+1)*(k+s+2*r*s) > (k+s)*(r+1)**2:
2738
+ return False
2739
+
2740
+ # multiplicity of eigenvalues 'r,s' (f=lambda_r, g=lambda_s)
2741
+ #
2742
+ # They are integers (checked by the 'integrality condition').
2743
+ f = -k*(s+1)*(k-s)//(mu*(r-s))
2744
+ g = k*(r+1)*(k-r)//(mu*(r-s))
2745
+ if 1 + f + g != v: # the only other eigenvalue, k, has multiplicity 1
2746
+ return False
2747
+
2748
+ # 3.C of [BL1984]_
2749
+ # (Absolute bound)
2750
+ if 2*v > f*(f+3) or 2*v > g*(g+3):
2751
+ return False
2752
+
2753
+ # 3.D of [BL1984]_
2754
+ # (Claw bound)
2755
+ if (mu != s**2 and
2756
+ mu != s*(s+1) and
2757
+ 2*(r+1) > s*(s+1)*(mu+1)):
2758
+ return False
2759
+
2760
+ # 3.E of [BL1984]_
2761
+ # (the Case μ=1)
2762
+ if mu == 1:
2763
+ if k % (l+1) or (v*k) % ((l+1)*(l+2)):
2764
+ return False
2765
+
2766
+ # 3.F of [BL1984]_
2767
+ # (the Case μ=2)
2768
+ if mu == 2 and 2*k < l*(l + 3) and k % (l + 1):
2769
+ return False
2770
+
2771
+ return True
2772
+
2773
+
2774
+ def strongly_regular_graph(int v, int k, int l, int mu=-1, bint existence=False, bint check=True):
2775
+ r"""
2776
+ Return a `(v,k,\lambda,\mu)`-strongly regular graph.
2777
+
2778
+ This function relies partly on Andries Brouwer's `database of strongly
2779
+ regular graphs <https://www.win.tue.nl/~aeb/graphs/srg/srgtab.html>`__. See
2780
+ the documentation of :mod:`sage.graphs.strongly_regular_db` for more
2781
+ information.
2782
+
2783
+ INPUT:
2784
+
2785
+ - ``v``, ``k``, ``l``, ``mu`` -- ``integers`` -- note that ``mu``, if unspecified, is
2786
+ automatically determined from ``v``, ``k``, ``l``
2787
+
2788
+ - ``existence`` -- boolean (default: ``False``); instead of building the graph,
2789
+ return:
2790
+
2791
+ - ``True`` -- meaning that a `(v,k,\lambda,\mu)`-strongly regular graph
2792
+ exists
2793
+
2794
+ - ``Unknown`` -- meaning that Sage does not know if such a strongly
2795
+ regular graph exists (see :mod:`sage.misc.unknown`)
2796
+
2797
+ - ``False`` -- meaning that no such strongly regular graph exists
2798
+
2799
+ - ``check`` -- boolean (default: ``True``); whether to check that output is
2800
+ correct before returning it. As this is expected to be useless, you may
2801
+ want to disable it whenever you want speed.
2802
+
2803
+ EXAMPLES:
2804
+
2805
+ Petersen's graph from its set of parameters::
2806
+
2807
+ sage: graphs.strongly_regular_graph(10,3,0,1, existence=True) # needs database_graphs sage.libs.pari
2808
+ True
2809
+ sage: graphs.strongly_regular_graph(10,3,0,1) # needs database_graphs
2810
+ complement(Johnson graph with parameters 5,2): Graph on 10 vertices
2811
+
2812
+ Now without specifying `\mu`::
2813
+
2814
+ sage: graphs.strongly_regular_graph(10,3,0) # needs database_graphs
2815
+ complement(Johnson graph with parameters 5,2): Graph on 10 vertices
2816
+
2817
+ An obviously infeasible set of parameters::
2818
+
2819
+ sage: graphs.strongly_regular_graph(5,5,5,5, existence=True) # needs database_graphs
2820
+ False
2821
+ sage: graphs.strongly_regular_graph(5,5,5,5) # needs database_graphs
2822
+ Traceback (most recent call last):
2823
+ ...
2824
+ ValueError: There exists no (5, 5, 5, 5)-strongly regular graph
2825
+
2826
+ A set of parameters proved in a paper to be infeasible::
2827
+
2828
+ sage: graphs.strongly_regular_graph(324,57,0,12,existence=True) # needs sage.combinat sage.modules
2829
+ False
2830
+ sage: graphs.strongly_regular_graph(324,57,0,12) # needs sage.combinat sage.modules
2831
+ Traceback (most recent call last):
2832
+ ...
2833
+ EmptySetError: Andries Brouwer's database reports that no (324, 57, 0,
2834
+ 12)-strongly regular graph exists. Comments: <a
2835
+ href="srgtabrefs.html#GavrilyukMakhnev05">Gavrilyuk & Makhnev</a> ...
2836
+
2837
+ A set of parameters unknown to be realizable in Andries Brouwer's database::
2838
+
2839
+ sage: graphs.strongly_regular_graph(324,95,22,30,existence=True) # needs sage.combinat
2840
+ Unknown
2841
+ sage: graphs.strongly_regular_graph(324,95,22,30) # needs sage.combinat
2842
+ Traceback (most recent call last):
2843
+ ...
2844
+ RuntimeError: Andries Brouwer's database reports that no
2845
+ (324, 95, 22, 30)-strongly regular graph is known to exist.
2846
+ Comments:
2847
+
2848
+ A large unknown set of parameters (not in Andries Brouwer's database)::
2849
+
2850
+ sage: graphs.strongly_regular_graph(1394,175,0,25,existence=True) # needs sage.combinat
2851
+ Unknown
2852
+ sage: graphs.strongly_regular_graph(1394,175,0,25) # needs sage.combinat
2853
+ Traceback (most recent call last):
2854
+ ...
2855
+ RuntimeError: Sage cannot figure out if a (1394, 175, 0, 25)-strongly
2856
+ regular graph exists.
2857
+
2858
+ Test the Claw bound (see 3.D of [BL1984]_)::
2859
+
2860
+ sage: graphs.strongly_regular_graph(2058, 242, 91, 20, existence=True) # needs database_graphs
2861
+ False
2862
+
2863
+ TESTS:
2864
+
2865
+ Check that :issue:`26513` is fixed::
2866
+
2867
+ sage: graphs.strongly_regular_graph(539, 288, 162, 144) # needs sage.combinat
2868
+ descendant of (540, 264, 138, 120)-strongly regular graph at ... 539 vertices
2869
+ sage: graphs.strongly_regular_graph(539, 250, 105, 125) # needs sage.combinat
2870
+ descendant of (540, 275, 130, 150)-strongly regular graph at ... 539 vertices
2871
+ sage: graphs.strongly_regular_graph(209, 100, 45, 50) # needs database_graphs sage.libs.pari
2872
+ descendant of complement(merging of S_7 on Circulant(6,[1,4])s) at ... 209 vertices
2873
+
2874
+
2875
+ Check that all of our constructions are correct - you will need gap_packages spkg installed::
2876
+
2877
+ sage: from sage.graphs.strongly_regular_db import apparently_feasible_parameters
2878
+ sage: for p in sorted(apparently_feasible_parameters(1300)): # not tested, optional gap_package_design
2879
+ ....: if graphs.strongly_regular_graph(*p,existence=True) is True:
2880
+ ....: try:
2881
+ ....: _ = graphs.strongly_regular_graph(*p)
2882
+ ....: print(p, "built successfully")
2883
+ ....: except RuntimeError as e:
2884
+ ....: if 'Brouwer' not in str(e):
2885
+ ....: raise
2886
+
2887
+ `\mu=0` behaves correctly (:issue:`19712`)::
2888
+
2889
+ sage: # needs database_graphs
2890
+ sage: graphs.strongly_regular_graph(10,2,1)
2891
+ Traceback (most recent call last):
2892
+ ...
2893
+ ValueError: There exists no (10, 2, 1, 0)-strongly regular graph
2894
+ sage: graphs.strongly_regular_graph(12,3,2)
2895
+ complement(Multipartite Graph with set sizes [4, 4, 4]): Graph on 12 vertices
2896
+ sage: graphs.strongly_regular_graph(6,3,0)
2897
+ Multipartite Graph with set sizes [3, 3]: Graph on 6 vertices
2898
+ """
2899
+ if mu == -1:
2900
+ mu = k*(k - l - 1)//(v - k - 1)
2901
+ g = strongly_regular_graph_lazy(v, k, l, mu=mu, existence=existence)
2902
+ if existence is True:
2903
+ return g
2904
+ G = g[0](*g[1:])
2905
+ if check and (v, k, l, mu) != G.is_strongly_regular(parameters=True):
2906
+ params = (v, k, l, mu)
2907
+ raise RuntimeError(f"Sage built an incorrect {params}-SRG.")
2908
+ return G
2909
+
2910
+
2911
+ def strongly_regular_graph_lazy(int v, int k, int l, int mu=-1, bint existence=False):
2912
+ r"""
2913
+ Return a promise to build an `(v,k,l,mu)`-srg.
2914
+
2915
+ Return a promise to build an `(v,k,l,mu)`-srg as a tuple `t`, with `t[0]` a
2916
+ function to evaluate on `*t[1:]`.
2917
+
2918
+ Input as in :func:`~sage.graphs.strongly_regular_graphs_db.strongly_regular_graph`,
2919
+ although without `check`.
2920
+
2921
+ TESTS::
2922
+
2923
+ sage: # needs database_graphs
2924
+ sage: from sage.graphs.strongly_regular_db import strongly_regular_graph_lazy
2925
+ sage: g,p=strongly_regular_graph_lazy(10,6,3); g,p
2926
+ (<...is_johnson...>, 5)
2927
+ sage: g(p)
2928
+ Johnson graph with parameters 5,2: Graph on 10 vertices
2929
+ sage: g,p=strongly_regular_graph_lazy(10,3,0,1); g,p
2930
+ (<...strongly_regular_graph_lazy...>,
2931
+ (5,))
2932
+ sage: g(p)
2933
+ complement(Johnson graph with parameters 5,2): Graph on 10 vertices
2934
+ sage: g,p=strongly_regular_graph_lazy(12,3,2); g,p
2935
+ (<...strongly_regular_graph_lazy...>,
2936
+ (3, 4))
2937
+ sage: g(p)
2938
+ complement(Multipartite Graph with set sizes [4, 4, 4]): Graph on 12 vertices
2939
+ sage: g = strongly_regular_graph_lazy(539,250,105); g # needs sage.combinat sage.modules
2940
+ (<...is_twograph_descendant_of_srg...>,
2941
+ 5,
2942
+ 11)
2943
+ sage: g[0](*g[1:]) # needs sage.combinat sage.modules
2944
+ descendant of (540, 275, 130, 150)-strongly regular graph at 0: Graph on 539 vertices
2945
+ """
2946
+ load_brouwer_database()
2947
+ if mu == -1:
2948
+ mu = k*(k - l - 1)//(v - k - 1)
2949
+
2950
+ params = (v, k, l, mu)
2951
+ params_complement = (v, v - k - 1, v - 2*k + mu - 2, v - 2*k + l)
2952
+
2953
+ if not seems_feasible(v, k, l, mu):
2954
+ if existence:
2955
+ return False
2956
+ raise ValueError(f"There exists no {params}-strongly regular graph")
2957
+
2958
+ if _small_srg_database is None:
2959
+ _build_small_srg_database()
2960
+
2961
+ if params in _small_srg_database:
2962
+ val = _small_srg_database[params]
2963
+ return True if existence else (val[0], *val[1:])
2964
+ if params_complement in _small_srg_database:
2965
+ val = _small_srg_database[params_complement]
2966
+ return True if existence else (lambda *t: val[0](*t).complement(), *val[1:])
2967
+
2968
+ test_functions = [is_complete_multipartite, # must be 1st, to prevent 0-divisions
2969
+ is_paley, is_johnson,
2970
+ is_orthogonal_array_block_graph,
2971
+ is_steiner, is_affine_polar,
2972
+ is_goethals_seidel,
2973
+ is_orthogonal_polar,
2974
+ is_NOodd, is_NOperp_F5, is_NO_F2, is_NO_F3, is_NU,
2975
+ is_unitary_polar, is_unitary_dual_polar, is_GQqmqp,
2976
+ is_RSHCD,
2977
+ is_twograph_descendant_of_srg,
2978
+ is_taylor_twograph_srg,
2979
+ is_switch_OA_srg,
2980
+ is_polhill,
2981
+ is_haemers,
2982
+ is_cossidente_penttila,
2983
+ is_mathon_PC_srg,
2984
+ is_muzychuk_S6,
2985
+ is_nowhere0_twoweight,
2986
+ is_switch_skewhad]
2987
+
2988
+ # Going through all test functions, for the set of parameters and its
2989
+ # complement.
2990
+ for f in test_functions:
2991
+ if f(*params):
2992
+ if existence:
2993
+ return True
2994
+ ans = f(*params)
2995
+ return (ans[0], *ans[1:])
2996
+ if f(*params_complement):
2997
+ if existence:
2998
+ return True
2999
+ ans = f(*params_complement)
3000
+ return (lambda t: ans[0](*t).complement(), ans[1:])
3001
+
3002
+ # From now on, we have no idea how to build the graph.
3003
+ #
3004
+ # We try to return the most appropriate error message.
3005
+
3006
+ global _brouwer_database
3007
+ brouwer_data = _brouwer_database.get(params, None)
3008
+
3009
+ if brouwer_data is not None:
3010
+ comments = brouwer_data['comments']
3011
+ if brouwer_data['status'] == 'impossible':
3012
+ if existence:
3013
+ return False
3014
+ raise EmptySetError(
3015
+ f"Andries Brouwer's database reports that no "
3016
+ f"{params}-strongly regular graph exists. Comments: {comments}")
3017
+
3018
+ if brouwer_data['status'] == 'open':
3019
+ if existence:
3020
+ return Unknown
3021
+ raise RuntimeError(
3022
+ f"Andries Brouwer's database reports that no "
3023
+ f"{params}-strongly regular graph is known to exist.\n"
3024
+ f"Comments: {comments}")
3025
+
3026
+ if brouwer_data['status'] == 'exists':
3027
+ if existence:
3028
+ return True
3029
+ raise RuntimeError(
3030
+ f"Andries Brouwer's database claims that such a "
3031
+ f"{params}-strongly regular graph exists, but Sage does not "
3032
+ f"know how to build it. If *you* do, please get in touch "
3033
+ f"with us on sage-devel!\n"
3034
+ f"Comments: {comments}")
3035
+ if existence:
3036
+ return Unknown
3037
+ raise RuntimeError(
3038
+ f"Sage cannot figure out if a {params}-strongly "
3039
+ f"regular graph exists.")
3040
+
3041
+
3042
+ def apparently_feasible_parameters(int n):
3043
+ r"""
3044
+ Return a list of a priori feasible parameters `(v,k,\lambda,\mu)`, with `0<\mu<k`.
3045
+
3046
+ Note that some of those that it returns may also be infeasible for more
3047
+ involved reasons. The condition `0<\mu<k` makes sure we skip trivial cases of
3048
+ complete multipartite graphs and their complements.
3049
+
3050
+ INPUT:
3051
+
3052
+ - ``n`` -- integer; return all a-priori feasible tuples `(v,k,\lambda,\mu)`
3053
+ for `v<n`
3054
+
3055
+ EXAMPLES:
3056
+
3057
+ All sets of parameters with `v<20` which pass basic arithmetic tests are
3058
+ feasible::
3059
+
3060
+ sage: from sage.graphs.strongly_regular_db import apparently_feasible_parameters
3061
+ sage: small_feasible = apparently_feasible_parameters(20); small_feasible
3062
+ {(5, 2, 0, 1),
3063
+ (9, 4, 1, 2),
3064
+ (10, 3, 0, 1),
3065
+ (10, 6, 3, 4),
3066
+ (13, 6, 2, 3),
3067
+ (15, 6, 1, 3),
3068
+ (15, 8, 4, 4),
3069
+ (16, 5, 0, 2),
3070
+ (16, 6, 2, 2),
3071
+ (16, 9, 4, 6),
3072
+ (16, 10, 6, 6),
3073
+ (17, 8, 3, 4)}
3074
+ sage: all(graphs.strongly_regular_graph(*x,existence=True) is True # needs database_graphs sage.libs.pari
3075
+ ....: for x in small_feasible)
3076
+ True
3077
+
3078
+ But that becomes wrong for `v<60` (because of the non-existence of a
3079
+ `(49,16,3,6)`-strongly regular graph)::
3080
+
3081
+ sage: small_feasible = apparently_feasible_parameters(60)
3082
+ sage: all(graphs.strongly_regular_graph(*x,existence=True) is True # needs database_graphs sage.libs.pari
3083
+ ....: for x in small_feasible)
3084
+ False
3085
+ """
3086
+ cdef int v, k, l, mu
3087
+ feasible = set()
3088
+ for v in range(n):
3089
+ for k in range(1, v - 1):
3090
+ for l in range(k - 1):
3091
+ mu = k*(k - l - 1)//(v - k - 1)
3092
+ if mu > 0 and mu < k and seems_feasible(v, k, l, mu):
3093
+ feasible.add((v, k, l, mu))
3094
+ return feasible
3095
+
3096
+
3097
+ def _build_small_srg_database():
3098
+ r"""
3099
+ Build the database of small strongly regular graphs.
3100
+
3101
+ This data is stored in the module-level variable ``_small_srg_database``.
3102
+ We use formulas from Cor.3.7 of [CK1986]_ to compute parameters of the
3103
+ graph of the projective 2-intersection set associated with a 2-weight code `C`,
3104
+ and the usual theory of duality in association schemes to compute the
3105
+ parameters of the graph of words of `C`. Another relevant reference is
3106
+ Sect.9.8.3 of [BH2012]_.
3107
+
3108
+ EXAMPLES::
3109
+
3110
+ sage: from sage.graphs.strongly_regular_db import _build_small_srg_database
3111
+ sage: _build_small_srg_database() # needs sage.modules sage.rings.finite_rings
3112
+
3113
+ TESTS:
3114
+
3115
+ Make sure that all two-weight codes yield the strongly regular graphs we
3116
+ expect::
3117
+
3118
+ sage: # needs database_graphs
3119
+ sage: graphs.strongly_regular_graph(81, 50, 31, 30) # needs sage.libs.pari
3120
+ complement(two-intersection set in PG(4,3)): Graph on 81 vertices
3121
+ sage: graphs.strongly_regular_graph(243, 220, 199, 200) # long time, needs sage.rings.finite_rings
3122
+ two-weight code: [55, 5] linear code over GF(3): Graph on 243 vertices
3123
+ sage: graphs.strongly_regular_graph(256, 153, 92, 90) # needs sage.combinat
3124
+ complement(two-intersection set in PG(4,4)): Graph on 256 vertices
3125
+ sage: graphs.strongly_regular_graph(256, 170, 114, 110) # needs sage.combinat
3126
+ complement(two-intersection set in PG(8,2)): Graph on 256 vertices
3127
+ sage: graphs.strongly_regular_graph(256, 187, 138, 132) # needs sage.combinat
3128
+ complement(two-intersection set in PG(8,2)): Graph on 256 vertices
3129
+ sage: graphs.strongly_regular_graph(512, 73, 12, 10) # not tested (too long), needs sage.rings.finite_rings
3130
+ two-weight code: [219, 9] linear code over GF(2): Graph on 512 vertices
3131
+ sage: graphs.strongly_regular_graph(512, 219, 106, 84) # long time, needs sage.combinat
3132
+ two-intersection set in PG(9,2): Graph on 512 vertices
3133
+ sage: graphs.strongly_regular_graph(512, 315, 202, 180) # not tested (too long), needs sage.rings.finite_rings
3134
+ two-weight code: [70, 9] linear code over GF(2): Graph on 512 vertices
3135
+ sage: graphs.strongly_regular_graph(625, 364, 213, 210) # long time, needs sage.libs.pari
3136
+ complement(two-intersection set in PG(4,5)): Graph on 625 vertices
3137
+ sage: graphs.strongly_regular_graph(625, 416, 279, 272) # long time, needs sage.libs.pari
3138
+ complement(two-intersection set in PG(4,5)): Graph on 625 vertices
3139
+ sage: graphs.strongly_regular_graph(625, 468, 353, 342) # long time, needs sage.libs.pari
3140
+ complement(two-intersection set in PG(4,5)): Graph on 625 vertices
3141
+ sage: graphs.strongly_regular_graph(729, 336, 153,156) # not tested (too long)
3142
+ two-intersection set in PG(6,3): Graph on 729 vertices
3143
+ sage: graphs.strongly_regular_graph(729, 420, 243, 240) # not tested (too long)
3144
+ complement(two-intersection set in PG(6,3)): Graph on 729 vertices
3145
+ sage: graphs.strongly_regular_graph(729, 448, 277, 272) # not tested (too long)
3146
+ complement(two-intersection set in PG(6,3)): Graph on 729 vertices
3147
+ sage: graphs.strongly_regular_graph(729, 476, 313, 306) # not tested (too long)
3148
+ complement(two-intersection set in PG(6,3)): Graph on 729 vertices
3149
+ sage: graphs.strongly_regular_graph(729, 532, 391, 380) # not tested (too long)
3150
+ complement(two-intersection set in PG(6,3)): Graph on 729 vertices
3151
+ sage: graphs.strongly_regular_graph(729, 560, 433, 420) # not tested (too long)
3152
+ complement(two-intersection set in PG(6,3)): Graph on 729 vertices
3153
+ Graph on 729 vertices
3154
+ sage: graphs.strongly_regular_graph(729, 616, 523, 506) # not tested (too long)
3155
+ complement(two-intersection set in PG(6,3)): Graph on 729 vertices
3156
+ sage: graphs.strongly_regular_graph(1024, 363, 122, 132) # not tested (too long)
3157
+ two-intersection set in PG(5,4): Graph on 1024 vertices
3158
+ sage: graphs.strongly_regular_graph(1024, 396, 148, 156) # not tested (too long)
3159
+ two-intersection set in PG(5,4): Graph on 1024 vertices
3160
+ sage: graphs.strongly_regular_graph(1024, 429, 176, 182) # not tested (too long)
3161
+ two-intersection set in PG(5,4): Graph on 1024 vertices
3162
+ sage: graphs.strongly_regular_graph(1024, 825, 668, 650) # not tested (too long)
3163
+ complement(two-intersection set in PG(10,2)): Graph on 1024 vertices
3164
+ """
3165
+ from sage.graphs.generators.smallgraphs import McLaughlinGraph
3166
+ from sage.graphs.generators.smallgraphs import CameronGraph
3167
+ from sage.graphs.generators.smallgraphs import GritsenkoGraph
3168
+ from sage.graphs.generators.smallgraphs import M22Graph
3169
+ from sage.graphs.generators.smallgraphs import SimsGewirtzGraph
3170
+ from sage.graphs.generators.smallgraphs import HoffmanSingletonGraph
3171
+ from sage.graphs.generators.smallgraphs import HigmanSimsGraph
3172
+ from sage.graphs.generators.smallgraphs import IoninKharaghani765Graph
3173
+ from sage.graphs.generators.smallgraphs import JankoKharaghaniGraph
3174
+ from sage.graphs.generators.smallgraphs import LocalMcLaughlinGraph
3175
+ from sage.graphs.generators.smallgraphs import SuzukiGraph
3176
+ from sage.graphs.generators.smallgraphs import MathonStronglyRegularGraph
3177
+ from sage.graphs.generators.smallgraphs import U42Graph216
3178
+ from sage.graphs.generators.smallgraphs import U42Graph540
3179
+
3180
+ global _small_srg_database
3181
+ _small_srg_database = {
3182
+ ( 36, 14, 4, 6): [Graph, ('c~rLDEOcKTPO`U`HOIj@MWFLQFAaRIT`HIWqPsQQJ'
3183
+ 'DXGLqYM@gRLAWLdkEW@RQYQIErcgesClhKefC_ygS'
3184
+ 'GkZ`OyHETdK[?lWStCapVgKK')],
3185
+ ( 50, 7, 0, 1): [HoffmanSingletonGraph],
3186
+ ( 56, 10, 0, 2): [SimsGewirtzGraph],
3187
+ ( 65, 32, 15, 16): [GritsenkoGraph],
3188
+ ( 77, 16, 0, 4): [M22Graph],
3189
+ (100, 22, 0, 6): [HigmanSimsGraph],
3190
+ (100, 44, 18, 20): [SRG_100_44_18_20],
3191
+ (100, 45, 20, 20): [SRG_100_45_20_20],
3192
+ (105, 32, 4, 12): [SRG_105_32_4_12],
3193
+ (120, 63, 30, 36): [SRG_120_63_30_36],
3194
+ (120, 77, 52, 44): [SRG_120_77_52_44],
3195
+ (126, 25, 8, 4): [SRG_126_25_8_4],
3196
+ (126, 50, 13, 24): [SRG_126_50_13_24],
3197
+ (144, 39, 6, 12): [SRG_144_39_6_12],
3198
+ (162, 56, 10, 24): [LocalMcLaughlinGraph],
3199
+ (175, 72, 20, 36): [SRG_175_72_20_36],
3200
+ (176, 49, 12, 14): [SRG_176_49_12_14],
3201
+ (176, 90, 38, 54): [SRG_176_90_38_54],
3202
+ (176, 105, 68, 54): [SRG_176_105_68_54],
3203
+ (196, 91, 42, 42): [SRG_196_91_42_42],
3204
+ (210, 99, 48, 45): [SRG_210_99_48_45],
3205
+ (216, 40, 4, 8): [U42Graph216],
3206
+ (220, 84, 38, 28): [SRG_220_84_38_28],
3207
+ (231, 30, 9, 3): [CameronGraph],
3208
+ (243, 110, 37, 60): [SRG_243_110_37_60],
3209
+ (253, 140, 87, 65): [SRG_253_140_87_65],
3210
+ (275, 112, 30, 56): [McLaughlinGraph],
3211
+ (276, 140, 58, 84): [SRG_276_140_58_84],
3212
+ (280, 117, 44, 52): [SRG_280_117_44_52],
3213
+ (280, 135, 70, 60): [SRG_280_135_70_60],
3214
+ (416, 100, 36, 20): [SRG_416_100_36_20],
3215
+ (540, 187, 58, 68): [U42Graph540],
3216
+ (560, 208, 72, 80): [SRG_560_208_72_80],
3217
+ (630, 85, 20, 10): [SRG_630_85_20_10],
3218
+ (765, 192, 48, 48): [IoninKharaghani765Graph],
3219
+ (784, 243, 82, 72): [MathonStronglyRegularGraph, 0],
3220
+ (784, 270, 98, 90): [MathonStronglyRegularGraph, 1],
3221
+ (784, 297, 116, 110):[MathonStronglyRegularGraph, 2],
3222
+ (936, 375, 150,150): [JankoKharaghaniGraph, 936],
3223
+ (1288,792, 476,504): [SRG_1288_792_476_504],
3224
+ (1782,416, 100, 96): [SuzukiGraph],
3225
+ (1800,1029,588,588): [JankoKharaghaniGraph, 1800],
3226
+ }
3227
+
3228
+ # Turns the known two-weight codes into SRG constructors
3229
+ #
3230
+ cdef int n, q, k, w1, w2, K, N, l, m, K_O, l_O, m_O
3231
+ import sage.coding.two_weight_db
3232
+ from sage.matrix.constructor import matrix
3233
+ from sage.rings.integer_ring import ZZ
3234
+ cinv = matrix(ZZ, [[1, 0, 0], [0, 0, 1], [0, 1, 0]])
3235
+ for code in sage.coding.two_weight_db.data:
3236
+ n, q, k, w1, w2 = code['n'], code['K'].cardinality(), code['k'], code['w1'], code['w2']
3237
+ N = q**k
3238
+ K_O = n*(q - 1)
3239
+ l_O = K_O**2 + 3*K_O - q*(w1 + w2) - K_O*q*(w1 + w2) + w1*w2*q**2
3240
+ m_O = (w1*w2*q**2)//N
3241
+
3242
+ em = eigenmatrix(N, K_O, l_O, m_O) # 1st eigenmatrix
3243
+ assert((em is not None) and (em.det() != 0))
3244
+ emi = N*em.inverse() # 2nd eigenmatrix
3245
+ # 1st and 2nd eigenmatrices equal up to renumbering graphs?
3246
+ selfdual = em == cinv*emi*cinv
3247
+ _small_srg_database[N, K_O, l_O, m_O] = \
3248
+ [lambda x: strongly_regular_from_two_intersection_set(x.transpose()), code['M']]
3249
+ if not selfdual: # we can build two graphs (not complements to each other!)
3250
+ K, s, r = emi[0, 1], emi[1, 1], emi[2, 1] # by Thm 5.7 in [CK1986]_.
3251
+ l = K + r*s + r + s
3252
+ m = K + r*s
3253
+ _small_srg_database[N, K, l, m] = [strongly_regular_from_two_weight_code, code['M']]
3254
+
3255
+
3256
+ cdef load_brouwer_database():
3257
+ r"""
3258
+ Loads Andries Brouwer's database into _brouwer_database.
3259
+ """
3260
+ global _brouwer_database
3261
+ if _brouwer_database is not None:
3262
+ return
3263
+
3264
+ from sage.features.databases import DatabaseGraphs
3265
+ data_dir = os.path.dirname(DatabaseGraphs().absolute_filename())
3266
+ filename = os.path.join(data_dir, 'brouwer_srg_database.json')
3267
+ with open(filename) as fobj:
3268
+ database = json.load(fobj)
3269
+
3270
+ _brouwer_database = {
3271
+ (v, k, l, mu): {'status': status, 'comments': comments}
3272
+ for (v, k, l, mu, status, comments) in database
3273
+ }
3274
+
3275
+
3276
+ def _check_database():
3277
+ r"""
3278
+ Check the coherence of Andries Brouwer's database with Sage.
3279
+
3280
+ The function also outputs some statistics on the database.
3281
+
3282
+ EXAMPLES::
3283
+
3284
+ sage: from sage.graphs.strongly_regular_db import _check_database
3285
+ sage: _check_database() # long time # needs sage.libs.pari
3286
+ Sage cannot build a (512 133 24 38 ) that exists. Comment ...
3287
+ ...
3288
+ In Andries Brouwer's database:
3289
+ - 462 impossible entries
3290
+ - 2911 undecided entries
3291
+ - 1165 realizable entries (Sage misses ... of them)
3292
+ """
3293
+ global _brouwer_database
3294
+ load_brouwer_database()
3295
+
3296
+ # Check that all parameters detected as infeasible are actually infeasible
3297
+ # in Brouwer's database, for a test that was implemented.
3298
+ for params in set(_brouwer_database).difference(apparently_feasible_parameters(1301)):
3299
+ if _brouwer_database[params]['status'] != "impossible":
3300
+ raise RuntimeError("Brouwer's db does not seem to know that {} in unfeasible".format(params))
3301
+ comment = _brouwer_database[params]['comments']
3302
+ if ('Krein' in comment or
3303
+ 'Absolute' in comment or
3304
+ 'Conf' in comment or
3305
+ 'mu=1' in comment or
3306
+ '&mu;=2' in comment):
3307
+ continue
3308
+ raise RuntimeError("We detected that {} was unfeasible, but maybe we should not have".format(params))
3309
+
3310
+ # We empty the global database, to be sure that strongly_regular_graph does
3311
+ # not use its data to answer.
3312
+ _brouwer_database, saved_database = {}, _brouwer_database
3313
+
3314
+ cdef int missed = 0
3315
+ for params, dic in sorted(saved_database.items()):
3316
+ sage_answer = strongly_regular_graph(*params, existence=True)
3317
+ if dic['status'] == 'open':
3318
+ if sage_answer is True:
3319
+ print("Sage can build a {}, Brouwer's database cannot".format(params))
3320
+ assert sage_answer is not False
3321
+ elif dic['status'] == 'exists':
3322
+ if sage_answer is not True:
3323
+ print(("Sage cannot build a ({:<4} {:<4} {:<4} {:<4}) that exists. " +
3324
+ "Comment from Brouwer's database: ").format(*params)
3325
+ + dic['comments'])
3326
+ missed += 1
3327
+ assert sage_answer is not False
3328
+ elif dic['status'] == 'impossible':
3329
+ assert sage_answer is not True
3330
+ else:
3331
+ assert False # must not happen
3332
+
3333
+ status = [x['status'] for x in saved_database.values()]
3334
+ print("\nIn Andries Brouwer's database:")
3335
+ print("- {} impossible entries".format(status.count('impossible')))
3336
+ print("- {} undecided entries".format(status.count('open')))
3337
+ print("- {} realizable entries (Sage misses {} of them)".format(status.count('exists'), missed))
3338
+
3339
+ # Reassign its value to the global database
3340
+ _brouwer_database = saved_database