def _calculate_feather(self, graph: nx.classes.graph.Graph) -> np.ndarray: """ Calculating the characteristic function features of a graph. Arg types: * **graph** *(NetworkX graph)* - A graph to be embedded. Return types: * **features** *(Numpy vector)* - The embedding of a single graph. """ self.n_nodes = graph.number_of_nodes() self.degree_fn = _get_degree_fn(graph) A_tilde = self._get_normalized_adjacency(graph) X = self._create_node_feature_matrix(graph) theta = np.linspace(0.01, self.theta_max, self.eval_points) X = np.outer(X, theta) X = X.reshape(graph.number_of_nodes(), -1) X = np.concatenate([np.cos(X), np.sin(X)], axis=1) feature_blocks = [] for _ in range(self.order): X = A_tilde.dot(X) feature_blocks.append(X) feature_blocks = np.concatenate(feature_blocks, axis=1) feature_blocks = self.pool_fn(feature_blocks) return feature_blocks
def _ensure_integrity( graph: nx.classes.graph.Graph) -> nx.classes.graph.Graph: """Ensure walk traversal conditions.""" edge_list = [(index, index) for index in range(graph.number_of_nodes())] graph.add_edges_from(edge_list) return graph
def get_closest_sensors(G: nx.classes.graph.Graph, central_node: str, n: any, shortest_paths: list) -> list: """ Creates a list of sensors closest to the main node (both pressure and flow) using NetworkX all_pairs_dijkstra_path_length() Arguments: G - NetworkX graph of the water network central_node - name of junction you need sensors positions relative to n - number of closest sensors, int or str ('all' for all sensors) shortest_paths - list(all_pairs_dijkstra_path_length(G)) from NetworkX Returns: list of n closest sensors sorted by distance (ascending), or all sensors if n='all' """ nodes_measured = [] # which junctions have pressure sensors in them? edges_measured = [] # which pipes have flow sensors? # make lists of node/edge objects with sensors in them for node in G.nodes(data=True): if node[1]['measurement'] is True: nodes_measured.append(node) for edge in G.edges(data=True): if edge[2]['measurement'] is True: edges_measured.append(edge) objects_measured = { 'junctions': nodes_measured, 'pipes': edges_measured } # sum it up in one var # find our central_node in weird all_pairs_dijkstra_path_length(G) format for centrum in shortest_paths: if centrum[0] == central_node: shortest_paths_from_central_node = centrum # get a list of pressure sensors with their respective distances from central_node pressure_sensors_with_distance = [] for node in objects_measured['junctions']: pressure_sensors_with_distance.append( (node[0], shortest_paths_from_central_node[1][node[0]])) # do the same for flow sensors flow_sensors_with_distance = [] for edge in objects_measured['pipes']: flow_sensors_with_distance.append( (edge[2]['name'], shortest_paths_from_central_node[1][edge[0]])) sensors = pressure_sensors_with_distance + flow_sensors_with_distance # sort them by distance (ascending) pressure_sensors_sorted_by_distance = sorted( pressure_sensors_with_distance, key=lambda sensor: sensor[1]) flow_sensors_sorted_by_distance = sorted(flow_sensors_with_distance, key=lambda sensor: sensor[1]) sensors_sorted_by_distance = sorted(sensors, key=lambda sensor: sensor[1]) if n == 'all': return sensors_sorted_by_distance # return all sensors else: return sensors_sorted_by_distance[:n] # return only n closest sensors
def plot_tree(fig: Figure, T: nx.classes.graph.Graph, root: Union[str, int], row: int = 1, col: int = 2): """Plot the tree on the figure. This function assumes the type of subplot at the given row and col is of type scatter plot and has both x and y range of [0,1]. Args: fig (Figure): The figure to which the tree should be plotted. T (nx.classes.graph.Graph): Tree to be plotted. root (Union[str,int]): Root node of the tree. row (int, optional): Subplot row of the figure. Defaults to 1. col (int, optional): Subplot col of the figure. Defaults to 2. """ nx.set_node_attributes(T, tree_positions(T, root), 'pos') edge_x = [] edge_y = [] for edge in T.edges(): x0, y0 = T.nodes[edge[0]]['pos'] x1, y1 = T.nodes[edge[1]]['pos'] edge_x += [x0, x1, None] edge_y += [y0, y1, None] edge_trace = plt.Scatter(x=edge_x, y=edge_y, line=dict(width=1, color='black'), hoverinfo='none', showlegend=False, mode='lines') fig.add_trace(trace=edge_trace, row=row, col=col) for node in T.nodes(): if 'text' in T.nodes[node]: text = T.nodes[node]['text'] else: text = node if 'template' in T.nodes[node]: template = T.nodes[node]['template'] else: template = 'unexplored' x, y = T.nodes[node]['pos'] fig.add_annotation(x=x, y=y, visible=True, text=text, templateitemname=template, row=row, col=col)
def fit(self, G: nx.classes.graph.Graph) -> Word2Vec: """Fit and get the embedding algorithm model. Args: G (networkx.classes.graph.Graph): A NetworkX graph object. Returns: (gensim.models.word2vec.Word2Vec): The Word2Vec model. """ self.G = G walks = [] for node in tqdm(G.nodes()): for _ in range(self.num_walks): walk = [] walk.append(str(node)) current_node = node previous_node = None former_neighbors = [] for _ in range(self.walk_length): current_neighbors = np.array( list(G.neighbors(current_node))) if np.size(current_neighbors) == 0: break probability = np.array([1 / self.q] * len(current_neighbors)) probability[current_neighbors == previous_node] = 1 / self.p probability[(np.isin(current_neighbors, former_neighbors))] = 1 next_node = np.random.choice(current_neighbors, 1, p=probability / sum(probability))[0] walk.append(str(next_node)) former_neighbors = current_neighbors previous_node = current_node current_node = next_node walks.append(walk) self.model = Word2Vec(walks, size=self.size, window=self.window, seed=self.seed, workers=self.workers, iter=self.iter) return self.model
def fit(self, graph: nx.classes.graph.Graph): """ Fitting a Symm-NMF clustering model. Arg types: * **graph** *(NetworkX graph)* - The graph to be clustered. """ self._set_seed() self._check_graph(graph) graph.remove_edges_from(nx.selfloop_edges(graph)) A_hat = self._create_base_matrix(graph) self._setup_embeddings(A_hat) for step in range(self.iterations): self._do_admm_update(A_hat)
def fit(self, graph: nx.classes.graph.Graph): """ Fitting a Diff2Vec model. Arg types: * **graph** *(NetworkX graph)* - The graph to be embedded. """ self._set_seed() self._check_graph(graph) diffuser = EulerianDiffuser(self.diffusion_number, self.diffusion_cover) diffuser.do_diffusions(graph) model = Word2Vec(diffuser.diffusions, hs=1, alpha=self.learning_rate, iter=self.epochs, size=self.dimensions, window=self.window_size, min_count=self.min_count, workers=self.workers, seed=self.seed) num_of_nodes = graph.number_of_nodes() self._embedding = [model[str(n)] for n in range(num_of_nodes)]
def fit(self, graph: nx.classes.graph.Graph, X: Union[np.array, coo_matrix]): """ Fitting a SINE model. Arg types: * **graph** *(NetworkX graph)* - The graph to be embedded. * **X** *(Scipy COO array)* - The matrix of node features. """ self._set_seed() self._check_graph(graph) self._walker = RandomWalker(self.walk_length, self.walk_number) self._walker.do_walks(graph) self._features = self._feature_transform(graph, X) self._select_walklets() model = Word2Vec(self._walklets, hs=0, alpha=self.learning_rate, size=self.dimensions, window=1, min_count=self.min_count, workers=self.workers, seed=self.seed, iter=self.epochs) self.embedding = np.array( [model[str(n)] for n in range(graph.number_of_nodes())])
def fit(self, graph: nx.classes.graph.Graph): """ Fitting a Walklets model. Arg types: * **graph** *(NetworkX graph)* - The graph to be embedded. """ self._set_seed() self._check_graph(graph) walker = RandomWalker(self.walk_length, self.walk_number) walker.do_walks(graph) num_of_nodes = graph.number_of_nodes() self._embedding = [] for power in range(1, self.window_size + 1): walklets = self._select_walklets(walker.walks, power) model = Word2Vec(walklets, hs=0, alpha=self.learning_rate, epochs=self.epochs, vector_size=self.dimensions, window=1, min_count=self.min_count, workers=self.workers, seed=self.seed) embedding = np.array( [model.wv[str(n)] for n in range(num_of_nodes)]) self._embedding.append(embedding)
def _recursive_position_for_row( G: nx.classes.graph.Graph, result: dict, two_rows_before: List[Hashable], last_row: List[Hashable], current_height: float, ): new_row = [] for v in last_row: for x in G.neighbors(v): if x not in two_rows_before: new_row.append(x) new_row_length = len(new_row) if new_row_length == 0: return if new_row_length == 1: result[new_row[0]] = np.array([0, current_height, 0]) else: for i in range(new_row_length): result[new_row[i]] = np.array( [-1 + 2 * i / (new_row_length - 1), current_height, 0] ) _recursive_position_for_row( G, result, two_rows_before=last_row, last_row=new_row, current_height=current_height + 1, )
def fit(self, G: nx.classes.graph.Graph) -> Word2Vec: """Fit and get the embedding algorithm model. Args: G (networkx.classes.graph.Graph): A NetworkX graph object. Returns: self.model (gensim.models.word2vec.Word2Vec): The Word2Vec model. """ self.G = G walks = [] for node in tqdm(G.nodes()): for _ in range(self.num_walks): walk = [] walk.append(str(node)) current_node = node for _ in range(self.walk_length): neighbors = list(nx.all_neighbors(G, current_node)) if(len(neighbors) != 0): next_node = np.random.choice(neighbors, 1)[0] walk.append(str(next_node)) current_node = next_node walks.append(walk) self.model = Word2Vec(walks, size=self.size, window=self.window, seed=self.seed, workers=self.workers, iter=self.iter) return self.model
def _get_components(graph: nx.classes.graph.Graph, values, cover: Cover, component_method): ret = [] for ce in cover.get_cover_elements(): filtered = list( filter(lambda v: ce['range'][0] <= values[v] <= ce['range'][1], values)) subg = graph.subgraph(filtered) if subg.number_of_edges() == 0: components = nx.connected_components(subg) elif component_method == 'modularity': components = nx.algorithms.community.greedy_modularity_communities( subg) elif component_method == 'async_label_prop': components = nx.algorithms.community.asyn_lpa_communities(subg) elif component_method == 'label_prop': components = nx.algorithms.community.label_propagation_communities( subg) # elif component_method == 'centrality': # components = nx.algorithms.community.girvan_newman(subg) else: components = nx.connected_components(subg) for comp in components: ret.append({'cover': ce, 'components': comp}) return ret
def fit(self, graph: nx.classes.graph.Graph): """ Fitting a DeepWalk model. Arg types: * **graph** *(NetworkX graph)* - The graph to be embedded. """ self._set_seed() self._check_graph(graph) walker = BiasedRandomWalker(self.walk_length, self.walk_number, self.p, self.q) walker.do_walks(graph) model = Word2Vec(walker.walks, hs=1, alpha=self.learning_rate, iter=self.epochs, size=self.dimensions, window=self.window_size, min_count=self.min_count, workers=self.workers, seed=self.seed) num_of_nodes = graph.number_of_nodes() self._embedding = [model[str(n)] for n in range(num_of_nodes)]
def fit(self, graph: nx.classes.graph.Graph): """ Fitting a GraphWave model. Arg types: * **graph** *(NetworkX graph)* - The graph to be embedded. """ self._set_seed() self._check_graph(graph) graph.remove_edges_from(nx.selfloop_edges(graph)) self._create_evaluation_points() self._check_size(graph) self._G = pygsp.graphs.Graph(nx.adjacency_matrix(graph)) if self.mechanism == "exact": self._exact_structural_wavelet_embedding() elif self.mechanism == "approximate": self._approximate_structural_wavelet_embedding() else: raise NameError("Unknown method.")
def fit(self, graph: nx.classes.graph.Graph): """ Fitting a BigClam clustering model. Arg types: * **graph** *(NetworkX graph)* - The graph to be clustered. """ self._set_seed() self._check_graph(graph) number_of_nodes = graph.number_of_nodes() self._initialize_features(number_of_nodes) nodes = [node for node in graph.nodes()] for i in range(self.iterations): random.shuffle(nodes) for node in nodes: nebs = [neb for neb in graph.neighbors(node)] neb_features = self._embedding[nebs, :] node_feature = self._embedding[node, :] gradient = self._calculate_gradient(node_feature, neb_features) self._do_updates(node, gradient, node_feature)
def fit(self, graph: nx.classes.graph.Graph): """ Fitting a Social Dimensions model. Arg types: * **graph** *(NetworkX graph)* - The graph to be embedded. """ self._set_seed() self._check_graph(graph) number_of_nodes = graph.number_of_nodes() L_tilde = nx.modularity_matrix(graph, nodelist=range(number_of_nodes)) _, self._embedding = sps.linalg.eigsh(L_tilde, k=self.dimensions, which='LM', return_eigenvectors=True)
def plot_population_graph(g: nx.classes.graph.Graph): boys = [i for i, d in g.nodes(data=True) if d['bipartite'] == 0] girls = [j for j, d in g.nodes(data=True) if d['bipartite'] == 1] plt.figure(figsize=(12, 8)) for i in boys: plt.scatter(0, int(i[2:]), c='b', s=100, alpha=0.5) for j in girls: plt.scatter(1, int(j[2:]), c='r', s=100, alpha=0.5) for partnership in g.edges(): coordinates = [(0, int(partnership[0][2:])), (1, int(partnership[1][2:]))] plt.plot(coordinates, c='gray', lw=1) text_y = round(max(len(boys), len(girls)) * 1.05) plt.text(x=0, y=text_y, s='boys', ha='center') plt.text(x=1, y=text_y, s='girls', ha='center') plt.axis('off') plt.xlim([-1, 2]) plt.show()
def fit(self, graph: nx.classes.graph.Graph): """ Fitting a Laplacian EigenMaps model. Arg types: * **graph** *(NetworkX graph)* - The graph to be embedded. """ self._set_seed() graph = self._check_graph(graph) number_of_nodes = graph.number_of_nodes() L_tilde = nx.normalized_laplacian_matrix(graph, nodelist=range(number_of_nodes)) _, self._embedding = sps.linalg.eigsh( L_tilde, k=self.dimensions, which="SM", return_eigenvectors=True )
def get_dgl_graph(self, nx_graph: nx.classes.graph.Graph): graph = dgl.DGLGraph() temp_map = {} for index, node in enumerate(nx_graph.nodes): data = torch.tensor(nx_graph.nodes[node]['feature'], dtype=torch.float32).reshape(1, -1) deg = torch.tensor(nx_graph.degree(node), dtype=torch.float32).reshape(1, -1) graph.add_nodes(1, {'feature': data, 'degree': deg}) temp_map[node] = index for v0, v1 in nx_graph.edges: data = torch.tensor(nx_graph[v0][v1]['feature'], dtype=torch.float32).reshape(1, -1) v0_index, v1_index = temp_map[v0], temp_map[v1] if v0_index != v1_index: graph.add_edge(v0_index, v1_index, {'feature': data}) graph.add_edge(v1_index, v0_index, {'feature': data}) else: graph.add_edge(v0_index, v1_index, {'feature': data}) return graph
def fit(self, graph: nx.classes.graph.Graph, X: Union[np.array, coo_matrix]): """ Fitting a FEATHER-N model. Arg types: * **graph** *(NetworkX graph)* - The graph to be embedded. * **X** *(Scipy COO or Numpy array)* - The matrix of node features. """ self._set_seed() self._check_graph(graph) X = self._create_reduced_features(X) A_tilde = self._create_A_tilde(graph) theta = np.linspace(0.01, self.theta_max, self.eval_points) X = np.outer(X, theta) X = X.reshape(graph.number_of_nodes(), -1) X = np.concatenate([np.cos(X), np.sin(X)], axis=1) self._feature_blocks = [] for _ in range(self.order): X = A_tilde.dot(X) self._feature_blocks.append(X) self._feature_blocks = np.concatenate(self._feature_blocks, axis=1)
def sample(self, graph: nx.classes.graph.Graph) -> nx.classes.graph.Graph: """ Sampling with a shortest path sampler. Arg types: * **graph** *(NetworkX graph)* - The graph to be sampled from. Return types: * **new_graph** *(NetworkX graph)* - The graph of sampled nodes. """ self._check_graph(graph) self._graph = graph self._set_seed_set() while len(self._nodes) < self.number_of_nodes: source, target = self._sample_a_pair() if source != target: path = nx.shortest_path(self._graph, source, target) for node in path: self._nodes.add(node) if len(self._nodes) >= self.number_of_nodes: break new_graph = graph.subgraph(self._nodes) return new_graph
def tree_positions(T: nx.classes.graph.Graph, root: Union[str, int]) -> Dict[int, List[float]]: """Get positions for every node in the tree T with the given root. Args: T (nx.classes.graph.Graph): Tree graph. root (Union[str,int]): Root of the tree graph Returns: Dict[int, List[float]]: Dictionary from nodes in T to positions. """ PAD = 0.1 HORIZONTAL_SPACE = 0.2 position = {} position[root] = (0.5, 1 - PAD) # root position node_to_level = nx.single_source_shortest_path_length(T, root) level_count = max(node_to_level.values()) + 1 levels = {} for l in range(level_count): levels[l] = [i for i in node_to_level if node_to_level[i] == l] level_heights = np.linspace(1.1, -0.1, level_count + 2)[1:-1] for l in range(1, level_count): # If there are more than 5 nodes in level, spread evenly across width; # otherwise, try to put nodes under their parent. if len(levels[l]) <= 4: # get parents of every pair of children in the level children = {} for node in levels[l]: parent = [i for i in list(T.neighbors(node)) if i < node][0] if parent in children: children[parent].append(node) else: children[parent] = [node] # initial attempt at positioning pos = {} for parent in children: x = position[parent][0] d = max((1 / 2)**(l + 1), HORIZONTAL_SPACE / 2) pos[children[parent][0]] = [x - d, level_heights[l]] pos[children[parent][1]] = [x + d, level_heights[l]] # perturb if needed keys = list(pos.keys()) x = [p[0] for p in pos.values()] n = len(x) - 1 while any( [x[i + 1] - x[i] + 0.05 < HORIZONTAL_SPACE for i in range(n)]): for i in range(len(x) - 1): if abs(x[i + 1] - x[i]) < HORIZONTAL_SPACE: shift = (HORIZONTAL_SPACE - abs(x[i + 1] - x[i])) / 2 x[i] -= shift x[i + 1] += shift # shift to be within width x[0] = x[0] + (max(PAD - x[0], 0)) for i in range(1, len(x)): x[i] = x[i] + max(HORIZONTAL_SPACE - (x[i] - x[i - 1]), 0) x[-1] = x[-1] - (max(x[-1] - (1 - PAD), 0)) for i in reversed(range(len(x) - 1)): x[i] = x[i] - max(HORIZONTAL_SPACE - (x[i + 1] - x[i]), 0) # update the position dictionary with new x values for i in range(len(x)): pos[keys[i]][0] = x[i] # set position for node in pos: position[node] = pos[node] else: level_widths = np.linspace(-0.1, 1.1, len(levels[l]) + 2)[1:-1] for j in range(len(levels[l])): position[(levels[l][j])] = (level_widths[j], level_heights[i]) return position
def _determine_graph_layout( nx_graph: nx.classes.graph.Graph, layout: Union[str, dict] = "spring", layout_scale: float = 2, layout_config: Union[dict, None] = None, partitions: Union[List[List[Hashable]], None] = None, root_vertex: Union[Hashable, None] = None, ) -> dict: automatic_layouts = { "circular": nx.layout.circular_layout, "kamada_kawai": nx.layout.kamada_kawai_layout, "planar": nx.layout.planar_layout, "random": nx.layout.random_layout, "shell": nx.layout.shell_layout, "spectral": nx.layout.spectral_layout, "partite": nx.layout.multipartite_layout, "tree": _tree_layout, "spiral": nx.layout.spiral_layout, "spring": nx.layout.spring_layout, } custom_layouts = ["random", "partite", "tree"] if layout_config is None: layout_config = {} if isinstance(layout, dict): return layout elif layout in automatic_layouts and layout not in custom_layouts: auto_layout = automatic_layouts[layout]( nx_graph, scale=layout_scale, **layout_config ) return dict([(k, np.append(v, [0])) for k, v in auto_layout.items()]) elif layout == "tree": return _tree_layout( nx_graph, root_vertex=root_vertex, scale=layout_scale, ) elif layout == "partite": if partitions is None or len(partitions) == 0: raise ValueError( "The partite layout requires the 'partitions' parameter to contain the partition of the vertices" ) partition_count = len(partitions) for i in range(partition_count): for v in partitions[i]: if nx_graph.nodes[v] is None: raise ValueError( "The partition must contain arrays of vertices in the graph" ) nx_graph.nodes[v]["subset"] = i # Add missing vertices to their own side for v in nx_graph.nodes: if "subset" not in nx_graph.nodes[v]: nx_graph.nodes[v]["subset"] = partition_count auto_layout = automatic_layouts["partite"]( nx_graph, scale=layout_scale, **layout_config ) return dict([(k, np.append(v, [0])) for k, v in auto_layout.items()]) elif layout == "random": # the random layout places coordinates in [0, 1) # we need to rescale manually afterwards... auto_layout = automatic_layouts["random"](nx_graph, **layout_config) for k, v in auto_layout.items(): auto_layout[k] = 2 * layout_scale * (v - np.array([0.5, 0.5])) return dict([(k, np.append(v, [0])) for k, v in auto_layout.items()]) else: raise ValueError( f"The layout '{layout}' is neither a recognized automatic layout, " "nor a vertex placement dictionary." )
def create_embedding(args: argparse.Namespace, G: nx.classes.graph.Graph) -> None: """Load the graph from a file in the input directory. Args: args (argparse.Namespace): The provided application arguments. G (networkx.classes.graph.Graph): The NetworkX graph object. """ if args.method == 'node2vec_snap': """Implementation: https://github.com/aditya-grover/node2vec (SNAP - Stanford Network Analysis Project).""" G = node2vec.Graph(G, args.directed, args.p, args.q) G.preprocess_transition_probs() walks = G.simulate_walks(args.num_walks, args.walk_length) walks = [list(map(str, walk)) for walk in walks] model = Word2Vec(walks, size=args.dimensions, window=args.window_size, seed=args.seed, workers=args.workers, iter=args.iter) model.wv.save_word2vec_format(args.output) elif args.method == 'node2vec_eliorc': """Implementation: https://github.com/eliorc/node2vec.""" node2vecTmp = Node2Vec(graph=G, walk_length=args.walk_length, num_walks=args.num_walks, dimensions=args.dimensions, workers=args.workers) model = node2vecTmp.fit(window=args.window_size, seed=args.seed, workers=args.workers, iter=args.iter) model.wv.save_word2vec_format(args.output) elif args.method == 'node2vec_custom': """Custom implementation.""" model = CustomNode2Vec(num_walks=args.num_walks, walk_length=args.walk_length, p=args.p, q=args.q, size=args.dimensions, window=args.window_size, seed=args.seed, workers=args.workers, iter=args.iter) model.fit(G=G) model.save_embedding(args.output) elif args.method == 'deepwalk_phanein': """Implementation: https://github.com/phanein/deepwalk.""" os.system( f"deepwalk --format edgelist --input {args.input} " + f"--number-walks {args.num_walks} --representation-size {args.dimensions} " + f"--walk-length {args.walk_length} --window-size {args.window_size} " + f"--workers {args.workers} --seed {args.seed} --output {args.output}" ) elif args.method == 'deepwalk_custom': """Custom implementation""" model = CustomDeepWalk(num_walks=args.num_walks, walk_length=args.walk_length, size=args.dimensions, window=args.window_size, seed=args.seed, workers=args.workers, iter=args.iter) model.fit(G=G) model.save_embedding(args.output) else: raise ValueError(f'Invalid embedding algorithm: {args.method}')
def _tree_layout( T: nx.classes.graph.Graph, root_vertex: Union[Hashable, None], scale: Union[float, tuple] = 2, orientation: str = "down", ): children = {root_vertex: list(T.neighbors(root_vertex))} if not nx.is_tree(T): raise ValueError("The tree layout must be used with trees") if root_vertex is None: raise ValueError("The tree layout requires the root_vertex parameter") # The following code is SageMath's tree layout implementation, taken from # https://github.com/sagemath/sage/blob/cc60cfebc4576fed8b01f0fc487271bdee3cefed/src/sage/graphs/graph_plot.py#L1447 # Always make a copy of the children because they get eaten stack = [list(children[root_vertex]).copy()] stick = [root_vertex] parent = {u: root_vertex for u in children[root_vertex]} pos = {} obstruction = [0.0] * len(T) if orientation == "down": o = -1 else: o = 1 def slide(v, dx): """ Shift the vertex v and its descendants to the right by dx. Precondition: v and its descendents have already had their positions computed. """ level = [v] while level: nextlevel = [] for u in level: x, y = pos[u] x += dx obstruction[y] = max(x + 1, obstruction[y]) pos[u] = x, y nextlevel += children[u] level = nextlevel while stack: C = stack[-1] if not C: p = stick.pop() stack.pop() cp = children[p] y = o * len(stack) if not cp: x = obstruction[y] pos[p] = x, y else: x = sum(pos[c][0] for c in cp) / float(len(cp)) pos[p] = x, y ox = obstruction[y] if x < ox: slide(p, ox - x) x = ox obstruction[y] = x + 1 continue t = C.pop() pt = parent[t] ct = [u for u in list(T.neighbors(t)) if u != pt] for c in ct: parent[c] = t children[t] = copy(ct) stack.append(ct) stick.append(t) # the resulting layout is then rescaled again to fit on Manim's canvas x_min = min(pos.values(), key=lambda t: t[0])[0] x_max = max(pos.values(), key=lambda t: t[0])[0] y_min = min(pos.values(), key=lambda t: t[1])[1] y_max = max(pos.values(), key=lambda t: t[1])[1] center = np.array([x_min + x_max, y_min + y_max, 0]) / 2 height = y_max - y_min width = x_max - x_min if isinstance(scale, (float, int)): sf = 2 * scale / max(width, height) else: sf = np.array([2 * scale[0] / width, 2 * scale[1] / height, 0]) return { v: (np.array([x, y, 0]) - center) * sf for v, (x, y) in pos.items() }
def merge_nodes(G: nx.classes.graph.Graph, new_inst_type: str, list_of_nodes: list, matched_ports: dict): """ Merges the given nodes in list_of_nodes and returns a reduced graph. Parameters ---------- G : netowrkx graph DESCRIPTION. Bipartite graph of circuit new_inst_type : str DESCRIPTION. name of new subckt to be created, A super node is created in graph havinge a subgraph in its values list_of_nodes : list DESCRIPTION. matched_ports : dict DESCRIPTION. dictionary of {subkt port: connected net} be added in dubckt Returns ------- G : nx.classes.graph.Graph returns updated graph. """ for node in list_of_nodes: if not G.nodes[node]: logger.debug("node not in graph anymore") return G, nx.Graph logger.debug(f"Is input bipartite: {nx.is_bipartite(G)}") assert len(list_of_nodes) > 1 # print("Merging nodes",list_of_nodes) new_node = "" ports = {} subgraph = nx.Graph() max_value = {} for node in list_of_nodes: new_node += '_' + node subgraph.add_node(node, inst_type=G.nodes[node]["inst_type"], real_inst_type=G.nodes[node]["real_inst_type"], ports=G.nodes[node]['ports'], edge_weight=G.nodes[node]['edge_weight'], values=merged_value({}, G.nodes[node]['values'])) if 'ports_match' in G.nodes[node].keys(): subgraph.nodes[node]["ports_match"] = G.nodes[node]['ports_match'] logger.debug(f"removing node {G.nodes[node]}") max_value = merged_value(max_value, G.nodes[node]['values']) nbr = G.neighbors(node) for ele in nbr: if ele not in subgraph.nodes(): if ele in matched_ports.keys(): subgraph.add_node(ele, inst_type=G.nodes[ele]["inst_type"], net_type="external") else: subgraph.add_node(ele, inst_type=G.nodes[ele]["inst_type"], net_type=G.nodes[ele]["net_type"]) #print("adding edge b/w:",node,ele,G[node][ele]["weight"]) subgraph.add_edge(node, ele, weight=G[node][ele]["weight"]) if ele in ports: # had to remove addition as combination of weight for cmc caused gate to be considered source # changed to bitwise and as all connections of CMB were considered as gate ports[ele] = ports[ele] | G[node][ele]["weight"] else: ports[ele] = G[node][ele]["weight"] new_node = new_node[1:] G.add_node(new_node, inst_type=new_inst_type, real_inst_type=new_inst_type, ports=list(matched_ports.keys()), edge_weight=list(ports.values()), ports_match=matched_ports, values=max_value) #logger.debug(f"creating a super node of combination of nodes: {new_inst_type}") for pins in list(ports): if set(G.neighbors(pins)) <= set( list_of_nodes) and G.nodes[pins]["net_type"] == 'internal': del ports[pins] G.remove_node(pins) for node in list_of_nodes: G.remove_node(node) for pins in ports: G.add_edge(new_node, pins, weight=ports[pins]) #logger.debug(f"new ports: {pins},{ports[pins]}") check_nodes(subgraph) return G, subgraph, new_node
def _weighted_directed_degree(node: int, graph: nx.classes.graph.Graph) -> float: out = graph.degree(node, weight="weight") return out
def _check_indexing(self, graph: nx.classes.graph.Graph): """Checking the consecutive numeric indexing.""" numeric_indices = [index for index in range(graph.number_of_nodes())] node_indices = sorted([node for node in graph.nodes()]) assert numeric_indices == node_indices, "The node indexing is wrong."