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()
Example #2
0
 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)
Example #4
0
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)