├── Report.pdf ├── celfpp.py ├── diffusion.py ├── greedy.py ├── heuristic.py ├── ivgreedy.py ├── spin.py └── utils.py /Report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prasanna08/SNInfluenceMaximization/d9ad48891421474e2ad758095a516e9a140c51b2/Report.pdf -------------------------------------------------------------------------------- /celfpp.py: -------------------------------------------------------------------------------- 1 | import diffusion 2 | from heapdict import heapdict 3 | 4 | class Node(object): 5 | def __init__(self, node): 6 | self.node = node 7 | self.mg1 = 0 8 | self.prev_best = None 9 | self.mg2 = 0 10 | self.flag = None 11 | self.list_index = 0 12 | 13 | def celfpp(graph, diffuse, k): 14 | S = set() 15 | # Note that heapdict is min heap and hence add negative priorities for 16 | # it to work. 17 | Q = heapdict() 18 | last_seed = None 19 | cur_best = None 20 | node_data_list = [] 21 | 22 | for node in graph.nodes: 23 | node_data = Node(node) 24 | node_data.mg1 = diffuse.diffuse_mc([node]) 25 | node_data.prev_best = cur_best 26 | node_data.mg2 = diffuse.diffuse_mc([node, cur_best.node]) if cur_best else node_data.mg1 27 | node_data.flag = 0 28 | cur_best = cur_best if cur_best and cur_best.mg1 > node_data.mg1 else node_data 29 | graph.nodes[node]['node_data'] = node_data 30 | node_data_list.append(node_data) 31 | node_data.list_index = len(node_data_list) - 1 32 | Q[node_data.list_index] = - node_data.mg1 33 | 34 | while len(S) < k: 35 | node_idx, _ = Q.peekitem() 36 | node_data = node_data_list[node_idx] 37 | if node_data.flag == len(S): 38 | S.add(node_data.node) 39 | del Q[node_idx] 40 | last_seed = node_data 41 | continue 42 | elif node_data.prev_best == last_seed: 43 | node_data.mg1 = node_data.mg2 44 | else: 45 | before = diffuse.diffuse_mc(S) 46 | S.add(node_data.node) 47 | after = diffuse.diffuse_mc(S) 48 | S.remove(node_data.node) 49 | node_data.mg1 = after - before 50 | node_data.prev_best = cur_best 51 | S.add(cur_best.node) 52 | before = diffuse.diffuse_mc(S) 53 | S.add(node_data.node) 54 | after = diffuse.diffuse_mc(S) 55 | S.remove(cur_best.node) 56 | if node_data.node != cur_best.node: S.remove(node_data.node) 57 | node_data.mg2 = after - before 58 | 59 | if cur_best and cur_best.mg1 < node_data.mg1: 60 | cur_best = node_data 61 | 62 | node_data.flag = len(S) 63 | Q[node_idx] = - node_data.mg1 64 | 65 | return S 66 | -------------------------------------------------------------------------------- /diffusion.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import networkx as nx 3 | from tqdm.autonotebook import tqdm 4 | 5 | class TriggeringModel(object): 6 | def __init__(self, graph): 7 | """ 8 | Args: 9 | graph: networkx graph object. Each node should have an attribute 10 | called node_dist which enumerates distribution over its neighbors. 11 | """ 12 | self.graph = graph 13 | self.neighborhood_fn = self.graph.neighbors if isinstance(self.graph, nx.Graph) else self.graph.predecessors 14 | self.node_to_dist = {node: (len(d['node_dist']), [x[1] for x in d['node_dist']]) for node, d in self.graph.nodes.data()} 15 | self.node_to_sampled_dist = dict() 16 | 17 | def diffusion_iter(self): 18 | for node in self.graph.nodes: 19 | if self.graph.nodes[node]['is_active']: 20 | continue 21 | neighbors = self.neighborhood_fn(node) 22 | neighbors = [neighbor for neighbor in neighbors if self.graph.nodes[neighbor]['is_active'] and neighbor in self.graph.nodes[node]['activating_neighbors']] 23 | if len(neighbors) > 0: 24 | self.graph.nodes[node]['is_active'] = True 25 | 26 | def sample_node_dist_for_mc(self, mc): 27 | for node in self.graph.nodes: 28 | selected_nodes = np.random.multinomial(mc, pvals=self.node_to_dist[node][1]) 29 | sampled_dist = [] 30 | for selected_node, times in enumerate(selected_nodes): 31 | sampled_dist.extend([selected_node]*times) 32 | np.random.shuffle(sampled_dist) 33 | self.node_to_sampled_dist[node] = sampled_dist 34 | 35 | def sample_neighbors_for_nodes(self, mc_count): 36 | for node in self.graph.nodes: 37 | node_dist = self.graph.nodes[node]['node_dist'] 38 | # node_dist = [(neighbors, prob) for all possible neighbors of node]. 39 | # Select a set randomly from given distribution. 40 | selected_set = self.node_to_sampled_dist[node][mc_count] 41 | self.graph.nodes[node]['activating_neighbors'] = node_dist[selected_set][0] 42 | 43 | def diffuse(self, act_nodes, mc_count): 44 | self.sample_neighbors_for_nodes(mc_count) 45 | nx.set_node_attributes(self.graph, False, name='is_active') 46 | 47 | for node in act_nodes: 48 | self.graph.nodes[node]['is_active'] = True 49 | 50 | prev_active_nodes = set() 51 | active_nodes = set() 52 | while True: 53 | self.diffusion_iter() 54 | prev_active_nodes = active_nodes 55 | active_nodes = set(i for i, v in self.graph.nodes(data=True) if v['is_active']) 56 | if active_nodes == prev_active_nodes: 57 | break 58 | self.graph.total_activated_nodes.append(len(active_nodes)) 59 | 60 | def diffuse_mc(self, act_nodes, mc=50): 61 | self.sample_node_dist_for_mc(mc) 62 | self.graph.total_activated_nodes = [] 63 | for i in tqdm(range(mc), desc='Monte Carlo', leave=False): 64 | self.diffuse(act_nodes, i) 65 | return sum(self.graph.total_activated_nodes) / float(mc) 66 | 67 | def shapely_iter(self, act_nodes): 68 | nx.set_node_attributes(self.graph, False, name='is_active') 69 | 70 | for node in act_nodes: 71 | self.graph.nodes[node]['is_active'] = True 72 | 73 | self.diffusion_iter() 74 | active_nodes = [n for n, v in self.graph.nodes.data() if v['is_active']] 75 | return active_nodes 76 | 77 | def shapely_diffuse(self, nodes, mc=50): 78 | self.sample_node_dist_for_mc(mc) 79 | 80 | for node in nodes: 81 | self.graph.nodes[node]['tmp'] = 0 82 | 83 | for c in tqdm(range(mc), desc='Shapely Monte Carlo', leave=False): 84 | self.sample_neighbors_for_nodes(c) 85 | active_nodes_with = [] 86 | active_nodes_without = [] 87 | for i in range(len(nodes)): 88 | if i in active_nodes_with: 89 | self.graph.nodes[node]['tmp'] = 0 90 | continue 91 | active_nodes_with = self.shapely_iter(nodes[:i+1]) 92 | active_nodes_without = self.shapely_iter(nodes[:i]) 93 | self.graph.nodes[nodes[i]]['tmp'] += len(active_nodes_with) - len(active_nodes_without) 94 | 95 | for i in range(len(nodes)): 96 | self.graph.nodes[node]['tmp'] /= float(mc) 97 | 98 | class IndependentCascade(object): 99 | def __init__(self, graph): 100 | self.graph = graph 101 | self.sampled_graph = graph.copy() 102 | self.edge_idx = {(u, v): i for i, (u, v) in enumerate(self.graph.edges())} 103 | self.reverse_edge_idx = {i: e for e, i in self.edge_idx.items()} 104 | self.prob_matrix = [self.graph.edges[self.reverse_edge_idx[i][0], self.reverse_edge_idx[i][1]]['prob'] for i in sorted(self.reverse_edge_idx.keys())] 105 | 106 | def sample_live_graph_mc(self, mc): 107 | edge_probs = {(u, v): d['prob'] for u, v, d in self.graph.edges().data()} 108 | probs = np.random.uniform(size=(mc, len(edge_probs))) 109 | self.sampled_graphs = [] 110 | for p in probs: 111 | self.sampled_graphs.append(np.array([p > self.prob_matrix]).astype(np.int8)) 112 | 113 | def sample_live_graph(self, mcount): 114 | removed_edges_idx = np.where(self.sampled_graphs[mcount] == 0)[1].tolist() 115 | removed_edges = [self.reverse_edge_idx[i] for i in removed_edges_idx] 116 | Gp = self.graph.copy() 117 | Gp.remove_edges_from(removed_edges) 118 | self.sampled_graph = Gp 119 | 120 | def diffusion_iter(self, act_nodes): 121 | new_act_nodes = set(act_nodes) 122 | for node in act_nodes: 123 | for node2 in nx.algorithms.bfs_tree(self.sampled_graph, node).nodes(): 124 | new_act_nodes.add(node2) 125 | for node in new_act_nodes: 126 | self.sampled_graph.nodes[node]['is_active'] = True 127 | 128 | def diffuse(self, act_nodes, mcount): 129 | self.sample_live_graph(mcount) 130 | nx.set_node_attributes(self.sampled_graph, False, name='is_active') 131 | 132 | for node in act_nodes: 133 | self.sampled_graph.nodes[node]['is_active'] = True 134 | 135 | self.diffusion_iter(act_nodes) 136 | active_nodes = [n for n, v in self.sampled_graph.nodes.data() if v['is_active']] 137 | self.graph.total_activated_nodes.append(len(active_nodes)) 138 | 139 | def diffuse_mc(self, act_nodes, mc=10): 140 | self.sample_live_graph_mc(mc) 141 | self.graph.total_activated_nodes = [] 142 | for _ in range(mc): 143 | self.diffuse(act_nodes, mcount) 144 | return sum(self.graph.total_activated_nodes) / float(mc) 145 | 146 | def shapely_iter(self, act_nodes): 147 | nx.set_node_attributes(self.sampled_graph, False, name='is_active') 148 | 149 | for node in act_nodes: 150 | self.sampled_graph.nodes[node]['is_active'] = True 151 | 152 | self.diffusion_iter(act_nodes) 153 | active_nodes = [n for n, v in self.sampled_graph.nodes.data() if v['is_active']] 154 | return active_nodes 155 | 156 | def shapely_diffuse(self, nodes, mc=10): 157 | self.sample_live_graph_mc(mc) 158 | for node in nodes: 159 | self.graph.nodes[node]['tmp'] = 0 160 | 161 | for c in tqdm(range(mc), desc='Shapely Monte Carlo', leave=False): 162 | self.sample_live_graph(c) 163 | active_nodes_with = [] 164 | active_nodes_without = [] 165 | for i in tqdm(range(len(nodes)), desc='Shapely Iter', leave=False): 166 | if i in active_nodes_with: 167 | self.graph.nodes[node]['tmp'] = 0 168 | continue 169 | active_nodes_with = self.shapely_iter(nodes[:i+1]) 170 | active_nodes_without = self.shapely_iter(nodes[:i]) 171 | self.graph.nodes[nodes[i]]['tmp'] += len(active_nodes_with) - len(active_nodes_without) 172 | 173 | for i in range(len(nodes)): 174 | self.graph.nodes[node]['tmp'] /= float(mc) 175 | 176 | class LinearThreshold(object): 177 | def __init__(self, graph): 178 | self.graph = graph 179 | self.neighborhood_fn = self.graph.neighbors if isinstance(self.graph, nx.Graph) else self.graph.predecessors 180 | 181 | def sample_node_thresholds_mc(self, mc): 182 | self.sampled_thresholds = np.random.uniform(size=(mc, len(self.graph.nodes))) 183 | 184 | def sample_node_thresholds(self, mcount): 185 | for idx, node in enumerate(self.graph.nodes): 186 | self.graph.nodes[node]['threshold'] = self.sampled_thresholds[mcount][idx] 187 | 188 | def diffusion_iter(self): 189 | for node in self.graph.nodes: 190 | if self.graph.nodes[node]['is_active']: 191 | continue 192 | neighbors = self.neighborhood_fn(node) 193 | weights = sum(self.graph.edges[neighbor, node]['weight'] for neighbor in neighbors) 194 | if weights > self.graph.nodes[node]['threshold']: 195 | self.graph.nodes[node]['is_active'] = True 196 | 197 | def diffuse(self, act_nodes, mcount): 198 | self.sample_node_thresholds(mcount) 199 | nx.set_node_attributes(self.graph, False, name='is_active') 200 | 201 | for node in act_nodes: 202 | self.graph.nodes[node]['is_active'] = True 203 | 204 | prev_active_nodes = set() 205 | active_nodes = set() 206 | while True: 207 | self.diffusion_iter() 208 | prev_active_nodes = active_nodes 209 | active_nodes = set(i for i, v in self.graph.nodes(data=True) if v['is_active']) 210 | if active_nodes == prev_active_nodes: 211 | break 212 | self.graph.total_activated_nodes.append(len(active_nodes)) 213 | 214 | def diffuse_mc(self, act_nodes, mc=50): 215 | self.sample_node_thresholds_mc(mc) 216 | self.graph.total_activated_nodes = [] 217 | for _ in range(mc): 218 | self.diffuse(act_nodes, mcount) 219 | return sum(self.graph.total_activated_nodes) / float(mc) 220 | 221 | def shapely_iter(self, act_nodes): 222 | nx.set_node_attributes(self.graph, False, name='is_active') 223 | 224 | for node in act_nodes: 225 | self.graph.nodes[node]['is_active'] = True 226 | 227 | self.diffusion_iter() 228 | active_nodes = [n for n, v in self.graph.nodes.data() if v['is_active']] 229 | return active_nodes 230 | 231 | def shapely_diffuse(self, nodes, mc=50): 232 | self.sample_node_thresholds_mc(mc) 233 | for node in nodes: 234 | self.graph.nodes[node]['tmp'] = 0 235 | 236 | for c in tqdm(range(mc), desc='Shapely Monte Carlo', leave=False): 237 | self.sample_node_thresholds(c) 238 | active_nodes_with = [] 239 | active_nodes_without = [] 240 | for i in tqdm(range(len(nodes)), desc='Shapely Nodes', leave=False): 241 | if i in active_nodes_with: 242 | self.graph.nodes[node]['tmp'] = 0 243 | continue 244 | active_nodes_with = self.shapely_iter(nodes[:i+1]) 245 | active_nodes_without = self.shapely_iter(nodes[:i]) 246 | self.graph.nodes[nodes[i]]['tmp'] += len(active_nodes_with) - len(active_nodes_without) 247 | 248 | -------------------------------------------------------------------------------- /greedy.py: -------------------------------------------------------------------------------- 1 | from diffusion import TriggeringModel, IndependentCascade 2 | 3 | def greedy(graph, diffuse, k): 4 | S = set() 5 | A = set(graph.nodes) 6 | while len(S) < k: 7 | node_diffusion = {} 8 | for node in A: 9 | S.add(node) 10 | node_diffusion[node] = diffuse.diffuse_mc(S) 11 | S.remove(node) 12 | max_node = max(node_diffusion.items(), key=lambda x: x[1])[0] 13 | S.add(max_node) 14 | A.remove(max_node) 15 | return S 16 | -------------------------------------------------------------------------------- /heuristic.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | 3 | def degree(graph, k): 4 | return [x for x, y in sorted(graph.degree, key=lambda x: x[1], reverse=True)[:k]] 5 | 6 | def single_degree_discount(graph, k): 7 | degree_count = dict(graph.degree) 8 | topk = [] 9 | neighborhood_fn = graph.neighbors if isinstance(graph, nx.Graph) else graph.predecessors 10 | for _ in range(k): 11 | node = max(degree_count.items(), key=lambda x: x[1])[0] 12 | topk.append(node) 13 | for neighbor in neighborhood_fn(node): 14 | degree_count[neighbor] -= 1 15 | return topk 16 | 17 | def top_k(graph, diffuse, k): 18 | expected_spread = [] 19 | for node in graph.nodes: 20 | expected_spread.append([node, diffuse.diffuse_mc([node])]) 21 | return [x for x, y in sorted(expected_spread, key=lambda x: x[1], reverse=True)[:k]] 22 | -------------------------------------------------------------------------------- /ivgreedy.py: -------------------------------------------------------------------------------- 1 | import utils 2 | from collections import defaultdict 3 | 4 | def build_iv_vector(graph, depth_limit=3): 5 | node_vectors = {} 6 | for node in graph.nodes: 7 | node_influence = defaultdict(int) 8 | node_influence[node] = 1 9 | for i in range(depth_limit): 10 | tmp = defaultdict(int) 11 | for node2 in node_influence: 12 | for node3 in graph.neighbors(node2): 13 | if node2 == node3: 14 | continue 15 | tmp[node3] += 1/((i+1)**2) * node_influence[node2] * graph.edges[node2, node3]['prob'] 16 | for node2 in tmp: 17 | node_influence[node2] += tmp[node2] 18 | node_vectors[node] = node_influence 19 | return node_vectors 20 | 21 | def ivgreedy(graph, k, depth_limit=3): 22 | iv_vectors = build_iv_vector(graph, depth_limit=depth_limit) 23 | S = set() 24 | R = [(node, sum(v for k, v in iv_vectors[node].items())) for node in graph.nodes] 25 | max_node = max(R, key=lambda x: x[1])[0] 26 | S.add(max_node) 27 | A = set(graph.nodes) 28 | A.remove(max_node) 29 | AR = iv_vectors[max_node] 30 | 31 | for _ in range(1, k): 32 | g = [] 33 | for node in A: 34 | gn = 0 35 | c = 1 - AR[node] 36 | for nnode in graph.nodes: 37 | p = c * iv_vectors[node][nnode] 38 | q = p + AR[nnode] 39 | p = p if q < 1 else 1 - AR[nnode] 40 | gn += p 41 | g.append((node, gn)) 42 | max_node = max(g, key=lambda x: x[1])[0] 43 | S.add(max_node) 44 | A.remove(max_node) 45 | c = 1 - AR[max_node] 46 | for node in graph.nodes: 47 | AR[node] = min(1, AR[node] + c * iv_vectors[max_node][node]) 48 | return S 49 | -------------------------------------------------------------------------------- /spin.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | def spin(graph, diffuse, k, perms): 4 | for node in graph.nodes: 5 | graph.nodes[node]['shapely'] = 0 6 | 7 | for i in range(perms): 8 | nodes = list(graph.nodes) 9 | np.random.shuffle(nodes) 10 | diffuse.shapely_diffuse(nodes) 11 | for node in nodes: 12 | graph.nodes[node]['shapely'] += diffuse.graph.nodes[node]['tmp'] 13 | 14 | for node in graph.nodes: 15 | graph.nodes[node]['shapely'] /= float(perms) 16 | 17 | rank_list = sorted(((node, data['shapely']) for node, data in graph.nodes.data()), key=lambda x: x[1], reverse=True) 18 | topk_list = get_top_k_from_rank_list(rank_list, graph, k) 19 | return topk_list 20 | 21 | def get_top_k_from_rank_list(rank_list, graph, k): 22 | topk = [] 23 | removed_nodes = set() 24 | for node, shap in rank_list: 25 | if node not in removed_nodes: 26 | topk.append((node, shap)) 27 | removed_nodes.union(set(graph.neighbors(node))) 28 | else: 29 | not_considered_nodes.append((node, shap)) 30 | if len(topk) == k: 31 | return topk 32 | 33 | topk += not_considered_nodes[:k - len(topk)] 34 | return sorted(topk, key=lambda x: x[1], reverse=True) 35 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import random 2 | import networkx as nx 3 | from functools import reduce 4 | 5 | def sample_random_distribution(size): 6 | """ 7 | For more info on this: http://www.cs.cmu.edu/~nasmith/papers/smith+tromble.tr04.pdf 8 | """ 9 | x = [0] + sorted([random.random() for i in range(size-1)]) + [1] 10 | dist = [i - j for i, j in zip(x[1:], x[:-1])] 11 | return dist 12 | 13 | def create_dist_for_graph(graph): 14 | for node in graph.nodes: 15 | neighbors = list(graph.neighbors(node)) 16 | n = len(neighbors) 17 | dist = sample_random_distribution(2**n) 18 | node_dist = [] 19 | for i, d in enumerate(dist): 20 | nset = [neighbors[j] for j, k in enumerate(format(i, '#0%db' % (n+2))[2:]) if k == '1'] 21 | node_dist.append((nset, d)) 22 | graph.nodes[node]['node_dist'] = node_dist 23 | return graph 24 | 25 | def create_dist_for_graph_edges(graph): 26 | probs = [random.random() for _ in range(len(graph.edges))] 27 | for edge, p in zip(graph.edges, probs): 28 | graph.edges[edge]['prob'] = p 29 | return graph 30 | 31 | def create_normalized_weights_for_graph_edges(graph): 32 | graph = create_dist_for_graph_edges(graph) 33 | graph = nx.DiGraph(graph) 34 | for node in graph.nodes: 35 | for edge in graph.in_edges(node): 36 | graph.edges[edge]['weight'] = graph.edges[edge]['prob'] / graph.in_degree(node) 37 | return graph 38 | 39 | def generate_edge_weights_from_dist(graph): 40 | for node in graph.nodes: 41 | for neighbor in graph.neighbors(node): 42 | p = sum(dist[1] for dist in graph.nodes[node]['node_dist'] if neighbor in dist) 43 | graph.edges[neighbor, node]['prob'] = p 44 | return graph 45 | 46 | def generate_union_node_dist_from_graph(graph): 47 | for node in graph.nodes: 48 | neighbors = list(graph.neighbors(node)) 49 | n = len(neighbors) 50 | print("Createing for node %d with %d neighbors" % (node, n)) 51 | node_dist = [] 52 | total_weight = 0.0 53 | for i in range(2**n): 54 | print('Generating iter %d' % i) 55 | nset = [neighbors[j] for j, k in enumerate(format(i, '#0%db' % (n+2))[2:]) if k == '1'] 56 | nnset = set() 57 | if nset: 58 | nnset = reduce(lambda x, y: x.union(y), [set(graph.neighbors(nsetnode)) for nsetnode in nset]) 59 | node_dist.append((nset, len(nnset) if nnset else 0.0)) 60 | total_weight += len(nnset) if nnset else 0.0 61 | if total_weight > 0.0: 62 | node_dist = [(nset, weight/total_weight) for nset, weight in node_dist] 63 | graph.nodes[node]['node_dist'] = node_dist 64 | return graph 65 | 66 | def generate_intersection_node_dist_from_graph(graph): 67 | for node in graph.nodes: 68 | neighbors = list(graph.neighbors(node)) 69 | n = len(neighbors) 70 | node_dist = [] 71 | total_weight = 0.0 72 | for i in range(2**n): 73 | nset = [neighbors[j] for j, k in enumerate(format(i, '#0%db' % (n+2))[2:]) if k == '1'] 74 | nnset = set() 75 | if nset: 76 | nnset = reduce(lambda x, y: x.intersection(y), [set(graph.neighbors(nsetnode)) for nsetnode in nset]) 77 | node_dist.append((nset, len(nnset) if nnset else 0.0)) 78 | total_weight += len(nnset) if nnset else 0.0 79 | if total_weight > 0.0: 80 | node_dist = [(nset, weight/total_weight) for nset, weight in node_dist] 81 | graph.nodes[node]['node_dist'] = node_dist 82 | return graph 83 | 84 | def generate_weighted_union_node_dist_from_graph(graph): 85 | for node in graph.nodes: 86 | neighbors = list(graph.neighbors(node)) 87 | n = len(neighbors) 88 | node_dist = [] 89 | total_weight = 0.0 90 | for i in range(2**n): 91 | nset = [neighbors[j] for j, k in enumerate(format(i, '#0%db' % (n+2))[2:]) if k == '1'] 92 | successor_sum = sum(graph.edges[nsetnode, neighbornset]['weight'] for nsetnode in nset for neighbornset in graph.successor(nsetnode)) 93 | node_sum = sum(graph.edges[nsetnode, node] for nsetnode in nset) 94 | node_dist.append((nset, float(node_sum)/successor_sum)) 95 | total_weight += float(node_sum)/successor_sum 96 | if total_weight > 0.0: 97 | node_dist = [(nset, weight/total_weight) for nset, weight in node_dist] 98 | graph.nodes[node]['node_dist'] = node_dist 99 | return graph 100 | 101 | def generate_weighted_intersection_node_dist_from_graph(graph): 102 | for node in graph.nodes: 103 | neighbors = list(graph.neighbors(node)) 104 | n = len(neighbors) 105 | node_dist = [] 106 | total_weight = 0.0 107 | for i in enumerate(2**n): 108 | nset = [neighbors[j] for j, k in enumerate(format(i, '#0%db' % (n+2))[2:]) if k == '1'] 109 | nnset = reduce(lambda x, y: x.intersection(y), [set(graph.neighbors(nsetnode)) for nsetnode in nset]) 110 | successor_sum = sum(graph.edges[nsetnode, neighbornset]['weight'] for neighbornset in nnset for nsetnode in nset) 111 | node_sum = sum(graph.edges[nsetnode, node] for nsetnode in nset) 112 | node_dist.append((nset, float(node_sum)/successor_sum)) 113 | total_weight += float(node_sum)/successor_sum 114 | if total_weight > 0.0: 115 | node_dist = [(nset, weight/total_weight) for nset, weight in node_dist] 116 | graph.nodes[node]['node_dist'] = node_dist 117 | return graph 118 | 119 | def parse_graph_txt_file(fname, separator='\t'): 120 | edge_list = [] 121 | with open(fname, 'r') as f: 122 | for line in f.readlines(): 123 | if line.startswith('#'): 124 | continue 125 | edge_list.append(list(map(int, line.strip().split(separator)))) 126 | G = nx.Graph() 127 | G.add_edges_from(edge_list) 128 | return G, edge_list --------------------------------------------------------------------------------