maxcut.py 3.5 KB

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