distance_regular.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. """
  2. =======================
  3. Distance-regular graphs
  4. =======================
  5. """
  6. import networkx as nx
  7. from networkx.utils import not_implemented_for
  8. from .distance_measures import diameter
  9. __all__ = [
  10. "is_distance_regular",
  11. "is_strongly_regular",
  12. "intersection_array",
  13. "global_parameters",
  14. ]
  15. def is_distance_regular(G):
  16. """Returns True if the graph is distance regular, False otherwise.
  17. A connected graph G is distance-regular if for any nodes x,y
  18. and any integers i,j=0,1,...,d (where d is the graph
  19. diameter), the number of vertices at distance i from x and
  20. distance j from y depends only on i,j and the graph distance
  21. between x and y, independently of the choice of x and y.
  22. Parameters
  23. ----------
  24. G: Networkx graph (undirected)
  25. Returns
  26. -------
  27. bool
  28. True if the graph is Distance Regular, False otherwise
  29. Examples
  30. --------
  31. >>> G = nx.hypercube_graph(6)
  32. >>> nx.is_distance_regular(G)
  33. True
  34. See Also
  35. --------
  36. intersection_array, global_parameters
  37. Notes
  38. -----
  39. For undirected and simple graphs only
  40. References
  41. ----------
  42. .. [1] Brouwer, A. E.; Cohen, A. M.; and Neumaier, A.
  43. Distance-Regular Graphs. New York: Springer-Verlag, 1989.
  44. .. [2] Weisstein, Eric W. "Distance-Regular Graph."
  45. http://mathworld.wolfram.com/Distance-RegularGraph.html
  46. """
  47. try:
  48. intersection_array(G)
  49. return True
  50. except nx.NetworkXError:
  51. return False
  52. def global_parameters(b, c):
  53. """Returns global parameters for a given intersection array.
  54. Given a distance-regular graph G with integers b_i, c_i,i = 0,....,d
  55. such that for any 2 vertices x,y in G at a distance i=d(x,y), there
  56. are exactly c_i neighbors of y at a distance of i-1 from x and b_i
  57. neighbors of y at a distance of i+1 from x.
  58. Thus, a distance regular graph has the global parameters,
  59. [[c_0,a_0,b_0],[c_1,a_1,b_1],......,[c_d,a_d,b_d]] for the
  60. intersection array [b_0,b_1,.....b_{d-1};c_1,c_2,.....c_d]
  61. where a_i+b_i+c_i=k , k= degree of every vertex.
  62. Parameters
  63. ----------
  64. b : list
  65. c : list
  66. Returns
  67. -------
  68. iterable
  69. An iterable over three tuples.
  70. Examples
  71. --------
  72. >>> G = nx.dodecahedral_graph()
  73. >>> b, c = nx.intersection_array(G)
  74. >>> list(nx.global_parameters(b, c))
  75. [(0, 0, 3), (1, 0, 2), (1, 1, 1), (1, 1, 1), (2, 0, 1), (3, 0, 0)]
  76. References
  77. ----------
  78. .. [1] Weisstein, Eric W. "Global Parameters."
  79. From MathWorld--A Wolfram Web Resource.
  80. http://mathworld.wolfram.com/GlobalParameters.html
  81. See Also
  82. --------
  83. intersection_array
  84. """
  85. return ((y, b[0] - x - y, x) for x, y in zip(b + [0], [0] + c))
  86. @not_implemented_for("directed", "multigraph")
  87. def intersection_array(G):
  88. """Returns the intersection array of a distance-regular graph.
  89. Given a distance-regular graph G with integers b_i, c_i,i = 0,....,d
  90. such that for any 2 vertices x,y in G at a distance i=d(x,y), there
  91. are exactly c_i neighbors of y at a distance of i-1 from x and b_i
  92. neighbors of y at a distance of i+1 from x.
  93. A distance regular graph's intersection array is given by,
  94. [b_0,b_1,.....b_{d-1};c_1,c_2,.....c_d]
  95. Parameters
  96. ----------
  97. G: Networkx graph (undirected)
  98. Returns
  99. -------
  100. b,c: tuple of lists
  101. Examples
  102. --------
  103. >>> G = nx.icosahedral_graph()
  104. >>> nx.intersection_array(G)
  105. ([5, 2, 1], [1, 2, 5])
  106. References
  107. ----------
  108. .. [1] Weisstein, Eric W. "Intersection Array."
  109. From MathWorld--A Wolfram Web Resource.
  110. http://mathworld.wolfram.com/IntersectionArray.html
  111. See Also
  112. --------
  113. global_parameters
  114. """
  115. # test for regular graph (all degrees must be equal)
  116. degree = iter(G.degree())
  117. (_, k) = next(degree)
  118. for _, knext in degree:
  119. if knext != k:
  120. raise nx.NetworkXError("Graph is not distance regular.")
  121. k = knext
  122. path_length = dict(nx.all_pairs_shortest_path_length(G))
  123. diameter = max(max(path_length[n].values()) for n in path_length)
  124. bint = {} # 'b' intersection array
  125. cint = {} # 'c' intersection array
  126. for u in G:
  127. for v in G:
  128. try:
  129. i = path_length[u][v]
  130. except KeyError as err: # graph must be connected
  131. raise nx.NetworkXError("Graph is not distance regular.") from err
  132. # number of neighbors of v at a distance of i-1 from u
  133. c = len([n for n in G[v] if path_length[n][u] == i - 1])
  134. # number of neighbors of v at a distance of i+1 from u
  135. b = len([n for n in G[v] if path_length[n][u] == i + 1])
  136. # b,c are independent of u and v
  137. if cint.get(i, c) != c or bint.get(i, b) != b:
  138. raise nx.NetworkXError("Graph is not distance regular")
  139. bint[i] = b
  140. cint[i] = c
  141. return (
  142. [bint.get(j, 0) for j in range(diameter)],
  143. [cint.get(j + 1, 0) for j in range(diameter)],
  144. )
  145. # TODO There is a definition for directed strongly regular graphs.
  146. @not_implemented_for("directed", "multigraph")
  147. def is_strongly_regular(G):
  148. """Returns True if and only if the given graph is strongly
  149. regular.
  150. An undirected graph is *strongly regular* if
  151. * it is regular,
  152. * each pair of adjacent vertices has the same number of neighbors in
  153. common,
  154. * each pair of nonadjacent vertices has the same number of neighbors
  155. in common.
  156. Each strongly regular graph is a distance-regular graph.
  157. Conversely, if a distance-regular graph has diameter two, then it is
  158. a strongly regular graph. For more information on distance-regular
  159. graphs, see :func:`is_distance_regular`.
  160. Parameters
  161. ----------
  162. G : NetworkX graph
  163. An undirected graph.
  164. Returns
  165. -------
  166. bool
  167. Whether `G` is strongly regular.
  168. Examples
  169. --------
  170. The cycle graph on five vertices is strongly regular. It is
  171. two-regular, each pair of adjacent vertices has no shared neighbors,
  172. and each pair of nonadjacent vertices has one shared neighbor::
  173. >>> G = nx.cycle_graph(5)
  174. >>> nx.is_strongly_regular(G)
  175. True
  176. """
  177. # Here is an alternate implementation based directly on the
  178. # definition of strongly regular graphs:
  179. #
  180. # return (all_equal(G.degree().values())
  181. # and all_equal(len(common_neighbors(G, u, v))
  182. # for u, v in G.edges())
  183. # and all_equal(len(common_neighbors(G, u, v))
  184. # for u, v in non_edges(G)))
  185. #
  186. # We instead use the fact that a distance-regular graph of diameter
  187. # two is strongly regular.
  188. return is_distance_regular(G) and diameter(G) == 2