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