GraphUtil.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. """
  2. altgraph.GraphUtil - Utility classes and functions
  3. ==================================================
  4. """
  5. import random
  6. from collections import deque
  7. from altgraph import Graph, GraphError
  8. def generate_random_graph(node_num, edge_num, self_loops=False, multi_edges=False):
  9. """
  10. Generates and returns a :py:class:`~altgraph.Graph.Graph` instance with
  11. *node_num* nodes randomly connected by *edge_num* edges.
  12. """
  13. g = Graph.Graph()
  14. if not multi_edges:
  15. if self_loops:
  16. max_edges = node_num * node_num
  17. else:
  18. max_edges = node_num * (node_num - 1)
  19. if edge_num > max_edges:
  20. raise GraphError("inconsistent arguments to 'generate_random_graph'")
  21. nodes = range(node_num)
  22. for node in nodes:
  23. g.add_node(node)
  24. while 1:
  25. head = random.choice(nodes)
  26. tail = random.choice(nodes)
  27. # loop defense
  28. if head == tail and not self_loops:
  29. continue
  30. # multiple edge defense
  31. if g.edge_by_node(head, tail) is not None and not multi_edges:
  32. continue
  33. # add the edge
  34. g.add_edge(head, tail)
  35. if g.number_of_edges() >= edge_num:
  36. break
  37. return g
  38. def generate_scale_free_graph(steps, growth_num, self_loops=False, multi_edges=False):
  39. """
  40. Generates and returns a :py:class:`~altgraph.Graph.Graph` instance that
  41. will have *steps* \\* *growth_num* nodes and a scale free (powerlaw)
  42. connectivity. Starting with a fully connected graph with *growth_num*
  43. nodes at every step *growth_num* nodes are added to the graph and are
  44. connected to existing nodes with a probability proportional to the degree
  45. of these existing nodes.
  46. """
  47. # The code doesn't seem to do what the documentation claims.
  48. graph = Graph.Graph()
  49. # initialize the graph
  50. store = []
  51. for i in range(growth_num):
  52. for j in range(i + 1, growth_num):
  53. store.append(i)
  54. store.append(j)
  55. graph.add_edge(i, j)
  56. # generate
  57. for node in range(growth_num, steps * growth_num):
  58. graph.add_node(node)
  59. while graph.out_degree(node) < growth_num:
  60. nbr = random.choice(store)
  61. # loop defense
  62. if node == nbr and not self_loops:
  63. continue
  64. # multi edge defense
  65. if graph.edge_by_node(node, nbr) and not multi_edges:
  66. continue
  67. graph.add_edge(node, nbr)
  68. for nbr in graph.out_nbrs(node):
  69. store.append(node)
  70. store.append(nbr)
  71. return graph
  72. def filter_stack(graph, head, filters):
  73. """
  74. Perform a walk in a depth-first order starting
  75. at *head*.
  76. Returns (visited, removes, orphans).
  77. * visited: the set of visited nodes
  78. * removes: the list of nodes where the node
  79. data does not all *filters*
  80. * orphans: tuples of (last_good, node),
  81. where node is not in removes, is directly
  82. reachable from a node in *removes* and
  83. *last_good* is the closest upstream node that is not
  84. in *removes*.
  85. """
  86. visited, removes, orphans = {head}, set(), set()
  87. stack = deque([(head, head)])
  88. get_data = graph.node_data
  89. get_edges = graph.out_edges
  90. get_tail = graph.tail
  91. while stack:
  92. last_good, node = stack.pop()
  93. data = get_data(node)
  94. if data is not None:
  95. for filtfunc in filters:
  96. if not filtfunc(data):
  97. removes.add(node)
  98. break
  99. else:
  100. last_good = node
  101. for edge in get_edges(node):
  102. tail = get_tail(edge)
  103. if last_good is not node:
  104. orphans.add((last_good, tail))
  105. if tail not in visited:
  106. visited.add(tail)
  107. stack.append((last_good, tail))
  108. orphans = [(lg, tl) for (lg, tl) in orphans if tl not in removes]
  109. return visited, removes, orphans