maxcut.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import networkx as nx
  2. from networkx.utils.decorators import not_implemented_for, py_random_state
  3. __all__ = ["randomized_partitioning", "one_exchange"]
  4. @not_implemented_for("directed")
  5. @not_implemented_for("multigraph")
  6. @py_random_state(1)
  7. @nx._dispatchable(edge_attrs="weight")
  8. def randomized_partitioning(G, seed=None, p=0.5, weight=None):
  9. """Compute a random partitioning of the graph nodes and its cut value.
  10. A partitioning is calculated by observing each node
  11. and deciding to add it to the partition with probability `p`,
  12. returning a random cut and its corresponding value (the
  13. sum of weights of edges connecting different partitions).
  14. Parameters
  15. ----------
  16. G : NetworkX graph
  17. seed : integer, random_state, or None (default)
  18. Indicator of random number generation state.
  19. See :ref:`Randomness<randomness>`.
  20. p : scalar
  21. Probability for each node to be part of the first partition.
  22. Should be in [0,1]
  23. weight : object
  24. Edge attribute key to use as weight. If not specified, edges
  25. have weight one.
  26. Returns
  27. -------
  28. cut_size : scalar
  29. Value of the minimum cut.
  30. partition : pair of node sets
  31. A partitioning of the nodes that defines a minimum cut.
  32. Examples
  33. --------
  34. >>> G = nx.complete_graph(5)
  35. >>> cut_size, partition = nx.approximation.randomized_partitioning(G, seed=1)
  36. >>> cut_size
  37. 6
  38. >>> partition
  39. ({0, 3, 4}, {1, 2})
  40. Raises
  41. ------
  42. NetworkXNotImplemented
  43. If the graph is directed or is a multigraph.
  44. """
  45. cut = {node for node in G.nodes() if seed.random() < p}
  46. cut_size = nx.algorithms.cut_size(G, cut, weight=weight)
  47. partition = (cut, G.nodes - cut)
  48. return cut_size, partition
  49. def _swap_node_partition(cut, node):
  50. return cut - {node} if node in cut else cut.union({node})
  51. @not_implemented_for("directed")
  52. @not_implemented_for("multigraph")
  53. @py_random_state(2)
  54. @nx._dispatchable(edge_attrs="weight")
  55. def one_exchange(G, initial_cut=None, seed=None, weight=None):
  56. """Compute a partitioning of the graphs nodes and the corresponding cut value.
  57. Use a greedy one exchange strategy to find a locally maximal cut
  58. and its value, it works by finding the best node (one that gives
  59. the highest gain to the cut value) to add to the current cut
  60. and repeats this process until no improvement can be made.
  61. Parameters
  62. ----------
  63. G : networkx Graph
  64. Graph to find a maximum cut for.
  65. initial_cut : set
  66. Cut to use as a starting point. If not supplied the algorithm
  67. starts with an empty cut.
  68. seed : integer, random_state, or None (default)
  69. Indicator of random number generation state.
  70. See :ref:`Randomness<randomness>`.
  71. weight : object
  72. Edge attribute key to use as weight. If not specified, edges
  73. have weight one.
  74. Returns
  75. -------
  76. cut_value : scalar
  77. Value of the maximum cut.
  78. partition : pair of node sets
  79. A partitioning of the nodes that defines a maximum cut.
  80. Examples
  81. --------
  82. >>> G = nx.complete_graph(5)
  83. >>> curr_cut_size, partition = nx.approximation.one_exchange(G, seed=1)
  84. >>> curr_cut_size
  85. 6
  86. >>> partition
  87. ({0, 2}, {1, 3, 4})
  88. Raises
  89. ------
  90. NetworkXNotImplemented
  91. If the graph is directed or is a multigraph.
  92. """
  93. if initial_cut is None:
  94. initial_cut = set()
  95. cut = set(initial_cut)
  96. current_cut_size = nx.algorithms.cut_size(G, cut, weight=weight)
  97. while True:
  98. nodes = list(G.nodes())
  99. # Shuffling the nodes ensures random tie-breaks in the following call to max
  100. seed.shuffle(nodes)
  101. best_node_to_swap = max(
  102. nodes,
  103. key=lambda v: nx.algorithms.cut_size(
  104. G, _swap_node_partition(cut, v), weight=weight
  105. ),
  106. default=None,
  107. )
  108. potential_cut = _swap_node_partition(cut, best_node_to_swap)
  109. potential_cut_size = nx.algorithms.cut_size(G, potential_cut, weight=weight)
  110. if potential_cut_size > current_cut_size:
  111. cut = potential_cut
  112. current_cut_size = potential_cut_size
  113. else:
  114. break
  115. partition = (cut, G.nodes - cut)
  116. return current_cut_size, partition