def __init__(self, nx_graph=nx.Graph()): self.bipartite_graph = nx.Graph() self.node = self.bipartite_graph.node self.next_edge_index = 0 self.next_hedge_index = 0 self.nodes_count = 0 self.edges_count = 0 self.hedges_count = 0 # ready sets self.reset_nodes_with_more_labels() self.reset_self_loops() self.reset_parallel_hedges_groups() # add nodes for node in nx_graph.nodes_iter(): self.add_node(node, attr_dict=copy.deepcopy(nx_graph.node[node])) # add edges of order 2 if nx_graph.is_directed(): adj_nodes = nxext.get_all_adjacent_nodes(nx_graph) for pair in adj_nodes: edges = nxext.get_edge_labels_and_dirs(nx_graph, pair[0], pair[1]) u = Hypergraph.format_node_id(pair[0]) v = Hypergraph.format_node_id(pair[1]) for edge in edges: dir_code = edge[1] if dir_code == 0: direction = None elif dir_code > 0: direction = set([(u, v)]) else: direction = set([(v, u)]) self.add_edge(set([u, v]), direction=direction, label=copy.deepcopy(edge[0])) else: for edge_endpoints in nx_graph.edges_iter(): u = u"n_{0}".format(edge_endpoints[0]) v = u"n_{0}".format(edge_endpoints[1]) if nx_graph.is_multigraph(): edges = nx_graph[edge_endpoints[0]][edge_endpoints[1]] for i in range(len(edges)): self.add_edge(set([u, v]), label=edges[i]["label"]) else: edge_label = nx_graph.edge[edge_endpoints[0]][ edge_endpoints[1]]["label"] self.add_edge(set([u, v]), label=copy.deepcopy(edge_label)) # Initialize ready sets self.init_parallel_edges_groups() self.init_nodes_with_n_neighbors()
def __init__(self, nx_graph = nx.Graph()): self.bipartite_graph = nx.Graph() self.node = self.bipartite_graph.node self.next_edge_index = 0 self.next_hedge_index = 0 self.nodes_count = 0 self.edges_count = 0 self.hedges_count = 0 # ready sets self.reset_nodes_with_more_labels() self.reset_self_loops() self.reset_parallel_hedges_groups() # add nodes for node in nx_graph.nodes_iter(): self.add_node(node, attr_dict=copy.deepcopy(nx_graph.node[node])) # add edges of order 2 if nx_graph.is_directed(): adj_nodes = nxext.get_all_adjacent_nodes(nx_graph) for pair in adj_nodes: edges = nxext.get_edge_labels_and_dirs(nx_graph, pair[0], pair[1]) u = Hypergraph.format_node_id(pair[0]) v = Hypergraph.format_node_id(pair[1]) for edge in edges: dir_code = edge[1] if dir_code == 0: direction = None elif dir_code > 0: direction = set([(u, v)]) else: direction = set([(v, u)]) self.add_edge(set([u, v]), direction=direction, label=copy.deepcopy(edge[0])) else: for edge_endpoints in nx_graph.edges_iter(): u = u"n_{0}".format(edge_endpoints[0]) v = u"n_{0}".format(edge_endpoints[1]) if nx_graph.is_multigraph(): edges = nx_graph[edge_endpoints[0]][edge_endpoints[1]] for i in range(len(edges)): self.add_edge(set([u, v]), label=edges[i]["label"]) else: edge_label = nx_graph.edge[edge_endpoints[0]][edge_endpoints[1]]["label"] self.add_edge(set([u, v]), label=copy.deepcopy(edge_label)) # Initialize ready sets self.init_parallel_edges_groups() self.init_nodes_with_n_neighbors()
def get_all_parallel_edge_free_groupings(): '''Finds all groups of parallel edges and returns all possible graphs where only one edge per group of parallel edges remains. ''' def process_parallel_edgess(all_adj_nodes, i, new_feature): '''Process parallel edges between nodes u and v recursively through all adjacent pairs of nodes. ''' u, v = all_adj_nodes[i] edges = nxext.get_edge_labels_and_dirs(feature, u, v) edges_count = len(edges) for j, edge in enumerate(edges): if j < edges_count - 1: _new_feature = new_feature.copy() else: # no need to copy when the graph will not be used for other iterations _new_feature = new_feature if edge[1] <= 0: _new_feature.add_edge(v, u, label=edge[0]) if edge[1] >= 0: _new_feature.add_edge(u, v, label=edge[0]) if i < len(all_adj_nodes) - 1: for processed_feature in process_parallel_edgess(all_adj_nodes, i + 1, _new_feature): yield processed_feature else: yield _new_feature all_adj_nodes = list(nxext.get_all_adjacent_nodes(feature)) if len(all_adj_nodes) == 0: return [feature] else: new_feature = nx.MultiDiGraph() new_feature.add_nodes_from(feature.nodes_iter(data=True)) return process_parallel_edgess(all_adj_nodes, 0, new_feature)
def process_raw_feature(raw_feature, hypergraph, max_nodes=6): '''Turns a raw feature to a usable feature or a collection of features, depending on the type of the rule according to which the feature was reduced (fixed, pattern or dynamic). :param raw_feature: A ReducibleFeature extracted from Arnborg & Proskurowski :param hypergraph: A Hypergraph which contains the nodes of the raw feature. :param max_nodes: (default value 6) A number of nodes that a pattern or a dynamic feature can have before being disassembled in subfeatures of size max_nodes. :return A collection containing one or more features depending on the type of the raw feature. ''' assert type(raw_feature) is ReducibleFeature assert type(hypergraph) is Hypergraph assert max_nodes > 3 def get_feature_type(raw_feature): '''Get the type of the raw feature according to the rule it was reduced by. :param raw_feature: A ReducibleFeature. :return Type of the raw feature: 0 for "fixed", 1 for "pattern", 2 for "dynamic". ''' rule = raw_feature.get_full_rule() if rule in ["2.1.0.0", "2.2.0.0", "4.2.0.0", "4.3.0.0", "5.2.3.1"]: return 1 elif rule in ["5.2.2.0", "5.2.3.2", "5.2.4.0"]: return 2 else: return 0 def sliding_window(seq, window_size): # Returns a sliding window (of width window_size) over data from the iterable seq # s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ... it = iter(seq) result = tuple(islice(it, window_size)) if len(result) == window_size: yield result for elem in it: result = result[1:] + (elem,) yield result feature_type = get_feature_type(raw_feature) if feature_type == 1: # pattern nodes_count = raw_feature.number_of_nodes() if nodes_count > max_nodes: rule = raw_feature.get_full_rule() if rule == "2.1.0.0": # chain: extract all subpaths of length max_nodes using a sliding window s = raw_feature.peripheral_nodes[0] t = raw_feature.peripheral_nodes[1] path = [s] + raw_feature.reducible_nodes + [t] for subpath in sliding_window(path, max_nodes): yield hypergraph.subgraph_with_labels(set(subpath)) raise StopIteration elif rule == "2.2.0.0": # ring: extract all subpaths of length max_nodes using a sliding window cycle = raw_feature.peripheral_nodes + raw_feature.reducible_nodes for subpath in sliding_window(cycle + cycle[:max_nodes - 1], max_nodes): yield hypergraph.subgraph_with_labels(set(subpath)) raise StopIteration elif rule == "4.2.0.0": # buddy: If there are more than 3 buddies create a buddy # configuration with 3 buddies for each possible combination assert len(raw_feature.peripheral_nodes) == 3 buddy_nodes = raw_feature.reducible_nodes for buddy_nodes_subgroup in itertools.combinations(buddy_nodes, max_nodes - 3): yield hypergraph.subgraph_with_labels(set(buddy_nodes_subgroup) | set(raw_feature.peripheral_nodes)) raise StopIteration elif rule == "4.3.0.0": # cube: similar approach as for wheel (5.2.3.1) if nodes_count > max_nodes + 1: reducible_neigh = [set(hypergraph.neighbors(node)) for node in raw_feature.reducible_nodes] hub_node = reduce(lambda a, b: a.intersection(b), reducible_neigh) ring_subgraph = hypergraph.subgraph(raw_feature.reducible_nodes | (raw_feature.peripheral_nodes - hub_node)) ring = nx.cycle_basis(ring_subgraph) if len(ring) > 0: ring = ring[0] for subpath in sliding_window(ring + ring[:max_nodes - 1], max_nodes): yield hypergraph.subgraph_with_labels(set(subpath) | hub_node) raise StopIteration else: # if there is no ring in the feature, treat it as a fixed feature # TODO: This can lead to a large number of shingles. Better solution? pass elif rule == "5.2.3.1": # wheel: extract all cake-slice subpaths of length max_node using a sliding window if nodes_count > max_nodes + 1: ring_subgraph = hypergraph.subgraph(raw_feature.reducible_nodes) ring = nx.cycle_basis(ring_subgraph) if len(ring) > 0: ring = ring[0] for subpath in sliding_window(ring + ring[:max_nodes - 1], max_nodes): yield hypergraph.subgraph_with_labels(set(subpath) | set(raw_feature.peripheral_nodes)) raise StopIteration else: # if there is no ring in the feature, treat it as a fixed feature # TODO: This can lead to a large number of shingles. Better solution? pass elif feature_type == 2: # dynamic (the reducible nodes are always of degree 3): # for each pair of adjacent nodes u, v let a new feature be # the subgraph that contains u, v and all neighbors of u and v. nodes_count = raw_feature.number_of_nodes() if nodes_count > max_nodes: nodes = set(raw_feature.reducible_nodes) | set(raw_feature.peripheral_nodes) feature_graph = hypergraph.subgraph(nodes) adj_nodes = nxext.get_all_adjacent_nodes(feature_graph) neighbors = {node: nxext.get_all_neighbors(feature_graph, node) for node in nodes} for u, v in adj_nodes: node_subgroup = set([u, v] + neighbors[u] + neighbors[v]) yield hypergraph.subgraph_with_labels(set(node_subgroup)) raise StopIteration # fixed or pattern/dynamic with <= max_nodes number of nodes yield raw_feature.as_subgraph(hypergraph)