extendability.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. """Provides a function for computing the extendability of a graph which is
  2. undirected, simple, connected and bipartite and contains at least one perfect matching."""
  3. import networkx as nx
  4. from networkx.utils import not_implemented_for
  5. __all__ = ["maximal_extendability"]
  6. @not_implemented_for("directed")
  7. @not_implemented_for("multigraph")
  8. @nx._dispatchable
  9. def maximal_extendability(G):
  10. """Computes the extendability of a graph.
  11. The extendability of a graph is defined as the maximum $k$ for which `G`
  12. is $k$-extendable. Graph `G` is $k$-extendable if and only if `G` has a
  13. perfect matching and every set of $k$ independent edges can be extended
  14. to a perfect matching in `G`.
  15. Parameters
  16. ----------
  17. G : NetworkX Graph
  18. A fully-connected bipartite graph without self-loops
  19. Returns
  20. -------
  21. extendability : int
  22. Raises
  23. ------
  24. NetworkXError
  25. If the graph `G` is disconnected.
  26. If the graph `G` is not bipartite.
  27. If the graph `G` does not contain a perfect matching.
  28. If the residual graph of `G` is not strongly connected.
  29. Notes
  30. -----
  31. Definition:
  32. Let `G` be a simple, connected, undirected and bipartite graph with a perfect
  33. matching M and bipartition (U,V). The residual graph of `G`, denoted by $G_M$,
  34. is the graph obtained from G by directing the edges of M from V to U and the
  35. edges that do not belong to M from U to V.
  36. Lemma [1]_ :
  37. Let M be a perfect matching of `G`. `G` is $k$-extendable if and only if its residual
  38. graph $G_M$ is strongly connected and there are $k$ vertex-disjoint directed
  39. paths between every vertex of U and every vertex of V.
  40. Assuming that input graph `G` is undirected, simple, connected, bipartite and contains
  41. a perfect matching M, this function constructs the residual graph $G_M$ of G and
  42. returns the minimum value among the maximum vertex-disjoint directed paths between
  43. every vertex of U and every vertex of V in $G_M$. By combining the definitions
  44. and the lemma, this value represents the extendability of the graph `G`.
  45. Time complexity O($n^3$ $m^2$)) where $n$ is the number of vertices
  46. and $m$ is the number of edges.
  47. References
  48. ----------
  49. .. [1] "A polynomial algorithm for the extendability problem in bipartite graphs",
  50. J. Lakhal, L. Litzler, Information Processing Letters, 1998.
  51. .. [2] "On n-extendible graphs", M. D. Plummer, Discrete Mathematics, 31:201–210, 1980
  52. https://doi.org/10.1016/0012-365X(80)90037-0
  53. """
  54. if not nx.is_connected(G):
  55. raise nx.NetworkXError("Graph G is not connected")
  56. if not nx.bipartite.is_bipartite(G):
  57. raise nx.NetworkXError("Graph G is not bipartite")
  58. U, V = nx.bipartite.sets(G)
  59. maximum_matching = nx.bipartite.hopcroft_karp_matching(G)
  60. if not nx.is_perfect_matching(G, maximum_matching):
  61. raise nx.NetworkXError("Graph G does not contain a perfect matching")
  62. # list of edges in perfect matching, directed from V to U
  63. pm = [(node, maximum_matching[node]) for node in V & maximum_matching.keys()]
  64. # Direct all the edges of G, from V to U if in matching, else from U to V
  65. directed_edges = [
  66. (x, y) if (x in V and (x, y) in pm) or (x in U and (y, x) not in pm) else (y, x)
  67. for x, y in G.edges
  68. ]
  69. # Construct the residual graph of G
  70. residual_G = nx.DiGraph()
  71. residual_G.add_nodes_from(G)
  72. residual_G.add_edges_from(directed_edges)
  73. if not nx.is_strongly_connected(residual_G):
  74. raise nx.NetworkXError("The residual graph of G is not strongly connected")
  75. # For node-pairs between V & U, keep min of max number of node-disjoint paths
  76. # Variable $k$ stands for the extendability of graph G
  77. k = float("inf")
  78. for u in U:
  79. for v in V:
  80. num_paths = sum(1 for _ in nx.node_disjoint_paths(residual_G, u, v))
  81. k = k if k < num_paths else num_paths
  82. return k