asteroidal.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. """
  2. Algorithms for asteroidal triples and asteroidal numbers in graphs.
  3. An asteroidal triple in a graph G is a set of three non-adjacent vertices
  4. u, v and w such that there exist a path between any two of them that avoids
  5. closed neighborhood of the third. More formally, v_j, v_k belongs to the same
  6. connected component of G - N[v_i], where N[v_i] denotes the closed neighborhood
  7. of v_i. A graph which does not contain any asteroidal triples is called
  8. an AT-free graph. The class of AT-free graphs is a graph class for which
  9. many NP-complete problems are solvable in polynomial time. Amongst them,
  10. independent set and coloring.
  11. """
  12. import networkx as nx
  13. from networkx.utils import not_implemented_for
  14. __all__ = ["is_at_free", "find_asteroidal_triple"]
  15. @not_implemented_for("directed")
  16. @not_implemented_for("multigraph")
  17. def find_asteroidal_triple(G):
  18. r"""Find an asteroidal triple in the given graph.
  19. An asteroidal triple is a triple of non-adjacent vertices such that
  20. there exists a path between any two of them which avoids the closed
  21. neighborhood of the third. It checks all independent triples of vertices
  22. and whether they are an asteroidal triple or not. This is done with the
  23. help of a data structure called a component structure.
  24. A component structure encodes information about which vertices belongs to
  25. the same connected component when the closed neighborhood of a given vertex
  26. is removed from the graph. The algorithm used to check is the trivial
  27. one, outlined in [1]_, which has a runtime of
  28. :math:`O(|V||\overline{E} + |V||E|)`, where the second term is the
  29. creation of the component structure.
  30. Parameters
  31. ----------
  32. G : NetworkX Graph
  33. The graph to check whether is AT-free or not
  34. Returns
  35. -------
  36. list or None
  37. An asteroidal triple is returned as a list of nodes. If no asteroidal
  38. triple exists, i.e. the graph is AT-free, then None is returned.
  39. The returned value depends on the certificate parameter. The default
  40. option is a bool which is True if the graph is AT-free, i.e. the
  41. given graph contains no asteroidal triples, and False otherwise, i.e.
  42. if the graph contains at least one asteroidal triple.
  43. Notes
  44. -----
  45. The component structure and the algorithm is described in [1]_. The current
  46. implementation implements the trivial algorithm for simple graphs.
  47. References
  48. ----------
  49. .. [1] Ekkehard Köhler,
  50. "Recognizing Graphs without asteroidal triples",
  51. Journal of Discrete Algorithms 2, pages 439-452, 2004.
  52. https://www.sciencedirect.com/science/article/pii/S157086670400019X
  53. """
  54. V = set(G.nodes)
  55. if len(V) < 6:
  56. # An asteroidal triple cannot exist in a graph with 5 or less vertices.
  57. return None
  58. component_structure = create_component_structure(G)
  59. E_complement = set(nx.complement(G).edges)
  60. for e in E_complement:
  61. u = e[0]
  62. v = e[1]
  63. u_neighborhood = set(G[u]).union([u])
  64. v_neighborhood = set(G[v]).union([v])
  65. union_of_neighborhoods = u_neighborhood.union(v_neighborhood)
  66. for w in V - union_of_neighborhoods:
  67. # Check for each pair of vertices whether they belong to the
  68. # same connected component when the closed neighborhood of the
  69. # third is removed.
  70. if (
  71. component_structure[u][v] == component_structure[u][w]
  72. and component_structure[v][u] == component_structure[v][w]
  73. and component_structure[w][u] == component_structure[w][v]
  74. ):
  75. return [u, v, w]
  76. return None
  77. @not_implemented_for("directed")
  78. @not_implemented_for("multigraph")
  79. def is_at_free(G):
  80. """Check if a graph is AT-free.
  81. The method uses the `find_asteroidal_triple` method to recognize
  82. an AT-free graph. If no asteroidal triple is found the graph is
  83. AT-free and True is returned. If at least one asteroidal triple is
  84. found the graph is not AT-free and False is returned.
  85. Parameters
  86. ----------
  87. G : NetworkX Graph
  88. The graph to check whether is AT-free or not.
  89. Returns
  90. -------
  91. bool
  92. True if G is AT-free and False otherwise.
  93. Examples
  94. --------
  95. >>> G = nx.Graph([(0, 1), (0, 2), (1, 2), (1, 3), (1, 4), (4, 5)])
  96. >>> nx.is_at_free(G)
  97. True
  98. >>> G = nx.cycle_graph(6)
  99. >>> nx.is_at_free(G)
  100. False
  101. """
  102. return find_asteroidal_triple(G) is None
  103. @not_implemented_for("directed")
  104. @not_implemented_for("multigraph")
  105. def create_component_structure(G):
  106. r"""Create component structure for G.
  107. A *component structure* is an `nxn` array, denoted `c`, where `n` is
  108. the number of vertices, where each row and column corresponds to a vertex.
  109. .. math::
  110. c_{uv} = \begin{cases} 0, if v \in N[u] \\
  111. k, if v \in component k of G \setminus N[u] \end{cases}
  112. Where `k` is an arbitrary label for each component. The structure is used
  113. to simplify the detection of asteroidal triples.
  114. Parameters
  115. ----------
  116. G : NetworkX Graph
  117. Undirected, simple graph.
  118. Returns
  119. -------
  120. component_structure : dictionary
  121. A dictionary of dictionaries, keyed by pairs of vertices.
  122. """
  123. V = set(G.nodes)
  124. component_structure = {}
  125. for v in V:
  126. label = 0
  127. closed_neighborhood = set(G[v]).union({v})
  128. row_dict = {}
  129. for u in closed_neighborhood:
  130. row_dict[u] = 0
  131. G_reduced = G.subgraph(set(G.nodes) - closed_neighborhood)
  132. for cc in nx.connected_components(G_reduced):
  133. label += 1
  134. for u in cc:
  135. row_dict[u] = label
  136. component_structure[v] = row_dict
  137. return component_structure