Beispiel #1
0
def choose_who_to_vaccinate(graph: networkx.Graph) -> List:
    people_to_vaccinate = []
    # T = networkx.algorithms.maximum_spanning_tree(graph)
    node2degree = dict(graph.degree)
    sorted_nodes = sorted(node2degree.items(),
                          key=lambda item: item[1],
                          reverse=True)[:100]
    l1 = [node[0] for node in sorted_nodes]
    g2 = graph.subgraph(l1)
    l2 = networkx.closeness_centrality(g2)
    sorted_by_closness = sorted(l2.items(),
                                key=lambda item: item[1],
                                reverse=True)[:70]
    l3 = [i[0] for i in sorted_by_closness]
    g3 = graph.subgraph(l3)
    contenders = networkx.betweenness_centrality_subset(g3,
                                                        l3,
                                                        l3,
                                                        normalized=True)
    sorted_nodes = sorted(contenders.items(),
                          key=lambda item: item[1],
                          reverse=True)[:50]
    people_to_vaccinate = [i[0] for i in sorted_nodes]
    patientsRand = []
    graph.remove_nodes_from(
        [n for n in graph if n in set(people_to_vaccinate)])
    for i in range(50):
        patientsRand.append(np.random.choice(graph.nodes()))
    # ICM(graph, patientsRand, 6)
    return people_to_vaccinate
Beispiel #2
0
    def canonize_neighborhood(self,
                              graph: nx.Graph,
                              core: Any,
                              shell: int,
                              color_key='atom_type') -> str:
        """Calculate a canonical key for a neighborhood of an atom.

        Given a molecule graph and an atom in that molecule, this \
        function finds the neighborhood of the given atom of the given \
        depth, and returns a string that uniquely identifies that \
        neighborhood.

        A neighborhood comprises the given atom, any atoms at most \
        shell covalent bonds away from it, and any covalent bonds \
        between those atoms.

        Neighborhoods that consist of atoms with the same colors, \
        connected in the same way, will return the same key.

        Args:
            graph: A molecule's atomic graph.
            core: A node in graph, the core of the neighborhood.
            shell: Shell size to use when creating the neighborhood.
            color_key: Attribute key to use to determine atom color.

        Returns:
            A string unique to the neighborhood.
        """
        if shell > 0:
            fragment = graph.subgraph(bfs_nodes(graph, core, max_depth=shell))
        else:
            fragment = graph.subgraph([core])

        result = self.canonize(fragment, color_key=color_key, core=core)
        return result
Beispiel #3
0
    def _propagate(self, graph: Graph, embedded_sub_graph: Graph, embeddings: np.array, node2id, id2node, max_itr: int = 20):
        reachable_nodes = self._get_reachable_subgraph(graph, embedded_sub_graph)

        Z1 = embeddings

        sparse_adj = nx.to_scipy_sparse_matrix(graph)

        while reachable_nodes.order() > 0:
            print("Propagating : current embedding size : {}, reachable nodes : {}, total graph size : {}".format(
                len(embedded_sub_graph),
                len(reachable_nodes),
                len(graph)))
            Z1 = embeddings
            reachable_indexes = [i for i, node in enumerate(graph) if node in reachable_nodes]
            embedded_indexes = [i for i, node in enumerate(graph) if node in embedded_sub_graph]
            A1, A2 = sparse_adj[embedded_indexes, :][:, reachable_indexes], sparse_adj[reachable_indexes, :][:, reachable_indexes]
            norm = sparse.hstack([A1.T, A2]).sum(axis=1)
            A1_norm, A2_norm = sparse.csc_matrix(A1/norm.T), sparse.csc_matrix(A2/norm.T)
            Z2 = sparse.csr_matrix(np.random.uniform(-1, 1, size=(reachable_nodes.order(), self.out_dim_)))
            for itr in range(max_itr):
                Z2 = A1_norm.T@Z1 + A2_norm.T@Z2


            n1 = embedded_sub_graph.order()
            for node in reachable_nodes:
                node2id[node] = n1
                id2node.append(node)
                n1 = n1 + 1
            embeddings = np.concatenate([embeddings, Z2], axis=0)
            embedded_sub_graph = graph.subgraph(list(node2id.keys()))
            reachable_nodes = self._get_reachable_subgraph(graph, embedded_sub_graph)

        unreachable_nodes = graph.subgraph([node for node in graph if node not in embedded_sub_graph])
        if unreachable_nodes:
            unreachable_nodes_embeddings = sparse.csr_matrix(np.zeros((unreachable_nodes.order(), self.out_dim_)))
            embeddings = np.concatenate([embeddings, unreachable_nodes_embeddings.todense()], axis=0)

            n1 = embedded_sub_graph.order()
            for node in unreachable_nodes:
                node2id[node] = n1
                id2node.append(node)
                n1 = n1 + 1

        return {
            "embeddings": np.array(embeddings),
            "node2id": node2id,
            "id2node": id2node,
            "unreachable_nodes": unreachable_nodes
        }
Beispiel #4
0
def swap(clique: list, graph: nx.Graph, node_select: str = "uniform") -> list:
    """If possible, generates a new clique by swapping a node in the input clique with a node
    outside the clique.

    Proceeds by calculating the set :math:`C_1` of nodes in the rest of the graph that are
    connected to all but one of the nodes in the clique. If this set is not empty, this function
    randomly picks a node and swaps it with the corresponding node in the clique that is not
    connected to it. The set :math:`C_1` and corresponding nodes in the clique are provided by the
    :func:`c_1` function.

    Whenever there are multiple nodes within :math:`C_1`, one must choose which node to add to
    the growing clique. This function allows a method of choosing nodes to be set with the
    ``node_select`` argument, with node selection based on uniform randomness and node degree
    supported. Degree-based node selection involves picking the node with the greatest degree,
    with ties settled by uniform random choice.

    **Example usage:**

    >>> graph = nx.wheel_graph(5)
    >>> graph.remove_edge(0, 4)
    >>> clique = [0, 1, 2]
    >>> swap(clique, graph)
    [0, 2, 3]

    Args:
        clique (list[int]): a subgraph specified by a list of nodes; the subgraph must be a clique
        graph (nx.Graph): the input graph
        node_select (str): method of selecting nodes from :math:`C_1` during growth. Can be either
            ``"uniform"`` for uniform random selection or ``"degree"`` for degree-based selection.
            Defaults to ``"uniform"``.

    Returns:
        list[int]: a new clique subgraph of equal size as the input
       """

    if not set(clique).issubset(graph.nodes):
        raise ValueError("Input is not a valid subgraph")

    if not is_clique(graph.subgraph(clique)):
        raise ValueError("Input subgraph is not a clique")

    clique = set(clique)
    _c_1 = c_1(clique, graph)

    if _c_1:
        if node_select == "uniform":
            swap_index = np.random.choice(len(_c_1))
            swap_nodes = _c_1[swap_index]
        elif node_select == "degree":
            degrees = np.array([graph.degree(n[1]) for n in _c_1])
            to_swap_index = np.random.choice(
                np.where(degrees == degrees.max())[0])
            swap_nodes = _c_1[to_swap_index]
        else:
            raise ValueError("Node selection method not recognized")

        clique.remove(swap_nodes[0])
        clique.add(swap_nodes[1])

    return sorted(clique)
def evaluate_conductance(graph: nx.Graph, subgraphs, tau):
    """
    :param graph: the graph being evaluated
    :param subgraphs: K cluster of Subsets of the main graph
    :param tau: tuning parameter, tau = 0 = vanilla conductance
    :return: core_cut, vanilla_conductance
    """

    vanilla_conductances = []
    core_cuts = []
    for _, nodes in subgraphs.items():
        subgraph = graph.subgraph(nodes).copy()
        subgraph_complement = set(graph) - set(subgraph)
        cut = nx.cut_size(graph, subgraph, subgraph_complement)
        volume_subgraph = nx.volume(graph, subgraph)
        volume_subgraph_complement = nx.volume(graph, subgraph_complement)
        volume_div = min(volume_subgraph, volume_subgraph_complement)
        vanilla_conductances.append((cut / volume_div))
        core_cuts.append((cut + ((tau / len(graph)) * len(subgraph) * len(subgraph_complement))) / (
                volume_div + (tau * len(subgraph))))
    vanilla_conductance = min(vanilla_conductances)
    core_cut = min(core_cuts)
    logging.debug('Vanilla graph conductance: %f', vanilla_conductance)
    logging.debug('CoreCut graph conductance: %f', core_cut)

    return core_cut, vanilla_conductance
Beispiel #6
0
def getReplaceable(nodes: np.ndarray, graph: nx.Graph) -> list:
    """
    Function that returns the nodes from the nodes argument, that can be removed from the network
    without generating two disconnected sub-networks.

    Parameters
    ----------
    nodes: np.ndarray
        Nodes composing the sub-graph whose nodes are to be examined.

    graph: nx.Graph
        Graph defining the structure of the network to be explored.

    Returns
    -------
    :list
        Nodes that can be replaced without generating two disconnected sub-networks.
    """
    replaceable_nodes = []
    for node in nodes:
        selected_nodes = [n for n in nodes if n != node]
        subgraph = graph.subgraph(selected_nodes)
        if nx.is_connected(subgraph):
            replaceable_nodes.append(node)

    return replaceable_nodes
Beispiel #7
0
def create_personas(
    G: nx.Graph,
    n: Hashable,
    clustering: Callable[[nx.Graph], Iterable[Sequence[Hashable]]],
) -> Tuple[List[PersonaNode], Dict[Hashable, PersonaNode]]:
    """
    Given a graph, a node in the graph, and a clustering algorithm, generate the personas for the given node.

    :param G: input graph
    :param n: node in the graph
    :param clustering: algorithm to cluster node on a graph, a callable taking a graph to an iterable of hashable
    sequences
    :return: 2-tuple which holds: personalities, so PersonaNode objects, for the given node; and a remap dictionary
    which maps every node in the ego-net of n in G to its corresponding PersonaNode of n
    """
    # TODO this can be optimised
    clusters = clustering(G.subgraph(G.neighbors(n)))
    persona_remap = {}
    personalities = []
    for index, cluster in enumerate(clusters):
        persona = PersonaNode(node=n, index=index)
        personalities.append(persona)
        for other_node in cluster:
            persona_remap[other_node] = persona
    return personalities, persona_remap
Beispiel #8
0
def is_near_cleaner_wasteful(X, graph : nx.Graph, pool : Pool):
    for Y in subsets_upto(vertex_set(graph), 3):
        hole = find_odd_hole(graph.subgraph(vertex_set(graph) - (set(X) - set(Y))), pool)
        if hole:
            return True

    return False
Beispiel #9
0
def get_community(G: nx.Graph, n, hops: int, max, maxCore: bool, visited={}):
    e = nx.ego_graph(G, n['id'], hops)

    # filter here before k-core
    #
    # here we filter out the nodes that doesn't
    # meet the domination requirements
    # currently we select nodes that have dom > 0
    # but this can change to a pruning function
    community = [
        x for x, y in e.nodes(data=True)
        if (y['dom'] > 0 and y['id'] not in visited)
    ]
    community = G.subgraph(community)

    k_core = nx.k_core(community)
    max_k_core = min(k_core.degree(), key=lambda x: x[1])[1]

    if maxCore:
        res = k_core
    else:
        res = community

    # mark nodes as visited
    # res = [x for x,y in res.nodes(data=True) if y['id'] not in visited  ]
    # mod = nx_comm.modularity(G, community)
    data1 = json_graph.node_link_data(res, {"link": "edges"})

    data1['stats'] = get_graph_stats(res, max['dom'], max_k_core, fast=True)
    data1['stats']['init'] = n['id']

    return (data1, res)
def community_keynodes(graph: nx.Graph, community: list, social_position: list, alpha: int = 10) -> list:
    """
    Key nodes detection Algorithm
    :param graph: the graph object to be used
    :param community: a list of nodes in the community
    :param social_position: social position score of each node in the community
    :param alpha: therehold score
    :return: a list of key nodes
    """
    if len(set(social_position)) == 1:  # SP(V1) == SP(V2) == ... == SP(Vn)
        return community
    key, SP = {}, {}
    for node, sp in zip(community, social_position):
        key[node], SP[node] = 0, sp

    subgraph = graph.subgraph(community)
    for u, v in subgraph.edges():
        if SP[u] < SP[v]:
            key[u] -= abs(SP[u]-SP[v])
            key[v] += abs(SP[v]-SP[u])
        else:
            key[u] += abs(SP[u]-SP[v])
            key[v] -= abs(SP[v]-SP[u])
    max_score = max(list(key.values()))
    keynodes =[node for node, score in key.items() if score > 0 and max_score / score < alpha]
    return keynodes
Beispiel #11
0
def CIS_oracles(U: Set, G: nx.Graph, max_size: int):
    if len(U) == 0:
        f_cis = None
        adj_cis = lambda k: {k}
    elif len(U) == 1:
        v = U.__iter__().__next__()
        f_cis = set()

        def adj_cis(k: int):
            if k == v:
                return set()
            else:
                if k in G[v]:
                    return {v, k}
                else:
                    return None
    elif len(U) > 1:
        subgraph = G.subgraph(U)
        articulations = set(articulation_points(subgraph))
        f_cis = U.difference(
            {min(set(subgraph.nodes).difference(articulations))})

        def adj_cis(k: int):
            if k in U:
                if k in articulations:
                    return None
                else:
                    return U.difference({k})
            else:
                if len(U) < max_size and len(set(G[k]).intersection(U)) > 0:
                    return U.union({k})
                else:
                    return None

    return f_cis, adj_cis
Beispiel #12
0
def vertex_branching(graph: nx.Graph, budget: int):
    #print("in branching start:", graph.number_of_nodes(), graph.number_of_edges(), "budget", budget, file=sys.stderr)
    flag, graph, vc, folded_verts = reduction.rrhandler(graph, budget)
    #print("in branching vc:", vc, "fold_verts:", folded_verts, "new graph", graph.number_of_nodes(), file=sys.stderr)
    if not flag:  # NO instance
        return False, set()
    budget -= len(vc) + len(folded_verts)
    num_edges = graph.number_of_edges()
    if budget < 0:
        return False, set()
    if budget <= 0 and num_edges > 0:
        return False, set()
    elif num_edges == 0:
        return True, utils.unfold(vc, folded_verts)
    else:  # budget > 0 and num_edges > 0
        cur_node, max_degree = max(graph.degree_iter(), key=itemgetter(1))
        # print("branching on vertex ", cur_node, "of degree", max_degree, file=sys.stderr)
        nbrs = set(graph.neighbors_iter(cur_node))
        flip = random.random() < 0.5  # flip coin to decide branch order
        if flip:
            branches = [(nbrs, len(nbrs)), (set(), 1)]
        else:
            branches = [(set(), 1), (nbrs, len(nbrs))]
        nodes = set(graph.nodes_iter())
        for branch, budget_change in branches:
            if budget_change > budget: continue
            res, vc_new = vertex_branching(
                graph.subgraph(nodes - {cur_node} - branch),
                budget - budget_change)
            if res:
                final_vc = vc | vc_new | (branch or {cur_node})
                return True, utils.unfold(final_vc, folded_verts)
        return False, set()
def draw(gr: nx.Graph, check_overload=True, circular=False):
    node_color = []
    subgraphs = [gr.subgraph(c) for c in nx.connected_components(gr)]
    for node in gr.nodes():
        if check_overload:
            added = False
            for adjacent in gr.adj[node]:
                if gr[node][adjacent]['a'] > gr[node][adjacent]['c']:
                    node_color.append('purple')
                    added = True
                    break
            if added:
                continue
        if node in subgraphs[0]:
            node_color.append('blue')
        else:
            node_color.append('red')
    if circular:
        pos = nx.circular_layout(gr)
    else:
        pos = nx.nx_pydot.graphviz_layout(gr, prog='neato')
    nx.draw(gr,
            pos=pos,
            node_size=500,
            with_labels=True,
            node_color=node_color)
    plt.show()
Beispiel #14
0
def shortest_path_sets(graph : nx.Graph, A : set, B : set):
    # Find a shortest path between two connected sets of verts

    for a in A:
        for b in B:
            if graph.has_edge(a, b):
                return [a, b]

    F = nx.Graph(graph.subgraph(vertex_set(graph) - (A | B)))

    F.add_nodes_from(("a", "b"))

    for (u, v) in edge_set(graph):
        if u in A:
            F.add_edge("a", v)
        elif v in A:
            F.add_edge(u, "a")

        if u in B:
            F.add_edge("b", v)
        elif v in B:
            F.add_edge(u, "b")

    P = shortest_path(F, "a", "b")

    if P is None:
        return None

    new_a = (A & neighbourhood(graph, P[ 1])).pop()
    new_b = (B & neighbourhood(graph, P[-2])).pop()

    return [new_a] + P[1:-1] + [new_b]
Beispiel #15
0
def path_through(graph : nx.Graph, S, u, v):
    # Returns a path from u to v in G with all its interior vertices in S if 
    # one exists.

    S = set(S) | {u, v}

    return shortest_path(graph.subgraph(S), u, v)
Beispiel #16
0
def subtree(graph: nx.Graph,
            nodes: Iterable[Node],
            predecessors: Mapping[Node, Node] = None) -> nx.Graph:
    """Extract a subtree with the specified nodes.

    Parameters
    ----------
    graph : nx.Graph
        Full tree graph.
    nodes : Iterable[Node]
        Nodes to include.
    predecessors : Mapping[Node, Node], optional
        Mapping of node to parent node, by default None

    Returns
    -------
    nx.Graph
        A proper tree containing at least the specified nodes.
    """
    if predecessors is None:
        (root, ), nodes = _peek(iter(nodes))
        predecessors = dict(nx.bfs_predecessors(graph, root))

    base = set(nodes)
    parent_nodes = (parents(node, predecessors) for node in base)
    result = set(itertools.chain.from_iterable(parent_nodes))
    result.update(base)
    return graph.subgraph(result)
Beispiel #17
0
    def get_path_graph(
        self,
        graph: nx.Graph,
        source: str,
        target: str,
        enforce_directionality: bool = True,
        try_reverse: bool = True,
    ):  # pylint: disable=too-many-arguments
        """Make a new graph with the shortest paths between two nodes"""
        new_graph = graph if enforce_directionality else self._make_undirected(
            graph)
        try:
            nodes = set(
                sum(
                    map(list, nx.all_shortest_paths(new_graph, source,
                                                    target)), []))
            return graph.__class__(graph.subgraph(nodes))

        except (nx.NetworkXError, nx.NetworkXException):
            warn(traceback.format_exc())
            if try_reverse:
                return self.get_path_graph(
                    graph=graph,
                    source=target,
                    target=source,
                    enforce_directionality=enforce_directionality,
                    try_reverse=False,
                )
            return graph.__class__()
Beispiel #18
0
def largest_connected_component(G):
    """
    Input
        G: an n x n matrix or a networkx Graph 
    Output
        The largest connected component of g

    """

    if type(G) == np.ndarray:
        if G.ndim == 2:
            if G.shape[0] == G.shape[1]:  # n x n matrix
                G = Graph(G)
            else:
                raise TypeError("Networkx graphs or n x n numpy arrays only")

    subgraphs = [
        G.subgraph(i).copy() for i in networkx.connected_components(G)
    ]

    G_connected = []
    for i in subgraphs:
        if len(i) > len(G_connected):
            G_connected = i

    return G_connected
Beispiel #19
0
def is_berge_naive(graph : nx.Graph, pool : Pool):
    subsets = it.chain.from_iterable(
        ( it.combinations(graph.nodes, k) 
        for k in range(5, len(graph.nodes), 2) )
    )

    graphs = it.chain.from_iterable(
        ( (graph.subgraph(S), nx.complement(graph.subgraph(S))) 
          for S in subsets )
    )

    for hole_found in pool.imap_unordered(is_cycle, graphs, chunksize=4096):
        if hole_found:
            return False

    return True
Beispiel #20
0
def readjust_tree(root: TreeNode, input_graph: nx.Graph) -> TreeNode:
    new_root = deepcopy(root)
    second_lowest_tnodes = set(tnode.parent for tnode in new_root.leaves)

    # TODO: untested!!! buggy

    # find out the offending tnodes
    not_ok_tnodes: Set[TreeNode] = set()
    for tnode in second_lowest_tnodes:
        leaves = get_leaves(tnode)
        subg = input_graph.subgraph(leaves)
        is_ok = subg.order() > 0 and nx.is_connected(subg)
        if not is_ok:
            not_ok_tnodes.add(tnode)

    # combine all the leaves of each offending tnode siblings together
    for offennding_tnode in not_ok_tnodes:
        parent = offennding_tnode.parent
        if parent is None:  # we have processed this tnode already
            continue

        parent.children = tuple()

        combined_leaves = get_leaves(offennding_tnode)
        for sib in offennding_tnode.siblings:
            combined_leaves |= get_leaves(sib)

        for new_leaf in combined_leaves:
            TreeNode(name=new_leaf, score=None, parent=parent)

    return new_root
Beispiel #21
0
def c_0(clique: list, graph: nx.Graph):
    """Generates the set :math:`C_0` of nodes that are connected to all nodes in the input
    clique subgraph.

    The set :math:`C_0` is defined in :cite:`pullan2006phased` and is used to determine nodes
    that can be added to the current clique to grow it into a larger one.

    **Example usage:**

    >>> graph = nx.complete_graph(10)
    >>> clique = [0, 1, 2, 3, 4]
    >>> c_0(clique, graph)
    [5, 6, 7, 8, 9]

    Args:
        clique (list[int]): a subgraph specified by a list of nodes; the subgraph must be a clique
        graph (nx.Graph): the input graph

    Returns:
        list[int]: a list containing the :math:`C_0` nodes for the clique

    """
    if not is_clique(graph.subgraph(clique)):
        raise ValueError("Input subgraph is not a clique")

    clique = set(clique)
    c_0_nodes = []
    non_clique_nodes = set(graph.nodes) - clique

    for i in non_clique_nodes:
        if clique.issubset(graph.neighbors(i)):
            c_0_nodes.append(i)

    return c_0_nodes
Beispiel #22
0
def degreeone(graph: nx.Graph, k: int = -1):
    """
    Degree 1 vertices (RR2)

    :param graph: graph to reduce
    :param k: parameter k
    :return: 4-tuple (flag, changed, new_graph, vc)
    where flag=false denotes that this is a NO-instance and vice-versa,
    changed indicates if the reduction rule caused any change,
    new_graph is the reduced subgraph,
    vc is the partial vc constructed while applying reduction rules
    """
    deg_one = set()
    for v, deg in graph.degree_iter():
        if deg == 1:
            deg_one.add(v)

    if len(deg_one) == 0:  # no degree 1 vertices found, so no change
        return True, False, graph, set()

    vc = set()
    to_delete = set()
    while deg_one:
        v = deg_one.pop()
        if v in vc:
            continue
        neigh = utils.first(graph.neighbors_iter(v))
        to_delete.add(v)
        vc.add(neigh)
        if len(vc) > k:  # NO-instance
            return False, False, graph, set()
    nodes = set(graph.nodes_iter())
    new_graph = graph.subgraph(nodes - vc - to_delete)
    return True, True, new_graph, vc
Beispiel #23
0
def clique_shrink(subgraph: list, graph: nx.Graph) -> list:
    """Shrinks an input subgraph until it forms a clique.

    Proceeds by removing nodes in the input subgraph one at a time until the result is a clique
    that satisfies :func:`~strawberryfields.apps.graph.utils.is_clique`. Upon each iteration,
    this function selects the node with the lowest degree relative to the subgraph and removes it.

    Args:
        subgraph (list[int]): a subgraph specified by a list of nodes
        graph (nx.Graph): the input graph

    Returns:
        list[int]: a clique of size smaller than or equal to the input subgraph
    """

    if not utils.is_subgraph(subgraph, graph):
        raise ValueError("Input is not a valid subgraph")

    subgraph = graph.subgraph(
        subgraph).copy()  # A copy is required to be able to modify the
    # structure of the subgraph (https://networkx.github.io/documentation/stable/reference/classes/generated/networkx.Graph.subgraph.html)

    while not utils.is_clique(subgraph):
        degrees = list(subgraph.degree())
        np.random.shuffle(
            degrees
        )  # used to make sure selection of node with lowest degree is not
        # deterministic in case of a tie (https://docs.python.org/3/library/functions.html#min)

        to_remove = min(degrees, key=lambda x: x[1])
        subgraph.remove_node(to_remove[0])

    return sorted(subgraph.nodes())
def hcsw(graph: nx.Graph, multiplier_threshold: float = 2) -> nx.Graph:
    """Clusters a connected undirected weighted graph.

    Returns a graph with the same nodes but not necessarily connected
    """

    number_of_nodes = graph.number_of_nodes()

    logger.debug(f'Clustering graph with {number_of_nodes} nodes')

    # singular graphs are already clustered
    if number_of_nodes < 2:
        logger.debug('Graph too small, exiting')
        return graph

    cut_weight, partitions = nx.algorithms.connectivity.stoer_wagner(graph)

    if not highly_connected(graph, cut_weight, multiplier_threshold):
        logger.debug('Graph not dense, performing cut')

        sub_graphs = [graph.subgraph(v).copy() for v in partitions]

        component_1 = hcsw(sub_graphs[0])
        component_2 = hcsw(sub_graphs[1])

        graph = nx.compose(component_1, component_2)
    else:
        logger.debug('Graph is dense, skipping cut')

    return graph
def graph_stats(G: nx.Graph) -> Dict[str, Any]:
    stats = dict()

    n, m = G.number_of_nodes(), G.number_of_edges()

    stats['number_of_vertices'] = n
    stats['number_of_edges'] = m
    stats['complexity'] = n * m
    stats['density'] = 2 * m / (n * (n - 1))

    stats['connected_components'] = []
    for G_hat in (G.subgraph(c) for c in nx.connected_components(G)):
        component_stats = dict()

        component_stats['number_of_vertices'] = G_hat.number_of_nodes()
        component_stats['number_of_edges'] = G_hat.number_of_edges()
        component_stats['diameter'] = nx.diameter(G_hat, usebounds=True)
        component_stats['radius'] = nx.radius(G_hat, usebounds=True)
        component_stats['center_size'] = len(nx.center(G_hat, usebounds=True))
        component_stats['periphery_size'] = len(
            nx.periphery(G_hat, usebounds=True))

        stats['connected_components'] += [component_stats]

    stats['number_of_connected_components'] = len(
        stats['connected_components'])

    stats['average_clustering_coefficient'] = nx.average_clustering(G)

    return stats
Beispiel #26
0
def prune_graph_by_distance(graph: nx.Graph) -> nx.Graph:
    """Get a subgraph with far flung nodes pruned.
    
    Many graphs end up with a few far flung nodes that distort the whole map.  This
    function computes the mean distances from the center of the graph and removes
    any nodes that are more than 2.5x the average distance.
    """
    if len(graph.nodes) == 0:
        return graph

    positions = nx.get_node_attributes(graph, 'position')
    center_x = sum([positions[n][0] for n in graph.nodes()]) / len(graph.nodes())
    center_y = sum([positions[n][1] for n in graph.nodes()]) / len(graph.nodes())

    distance_map = {}
    for node in graph.nodes():
        node_x = positions[node][0]
        node_y = positions[node][1]
        distance = math.sqrt((node_x - center_x) ** 2 + (node_y - center_y) ** 2)
        distance_map[node] = distance

    mean_distance = sorted(distance_map.values())[int(len(distance_map.values()) / 2)]
    max_distance = mean_distance * 2

    include_nodes = []
    for node in graph.nodes():
        if distance_map[node] <= max_distance:
            include_nodes.append(node)

    return graph.subgraph(include_nodes)
def optimize_subgraph_connectivity(graph: nx.Graph,
                                   nodes: set,
                                   utility_function: Callable[[nx.Graph], float],
                                   n_iter: int = 50,
                                   **kwargs) -> Tuple[nx.Graph, float]:
    """
    Optimize graph with the rewire operations restricted to the nodes 
    given as arguments.

    Arguments
    ---
    graph: NetworkX graph
    nodes: Set
        Nodes contained on `graph`
    utility_function: Function
        It takes a graph and returns a real number. Higher values are more optimal.
    """

    best_score = utility_function(graph)
    best_subgraph = graph.subgraph(nodes)

    rewiring_rule: callable = lambda x: subgraph_rewire(x, nodes)

    try:
        # (best_score, best_subgraph) = simulated_annealing_optimize(graph,
        #                                                            utility_function,
        #                                                            rewiring_rule,
        #                                                            n_iter=3)
        (best_score, best_subgraph) = hill_climb_optimize(
            graph, utility_function, rewiring_rule, n_iter=n_iter, **kwargs
        )
    finally:
        return (best_subgraph, best_score)
Beispiel #28
0
def compress(graph: nx.Graph, vc, smart_subsets=True):
    """
    Given a vc tries to find a vc of strictly smaller size
    If unable to find, then provided vc is optimal.

    :param graph: Graph to find vc for
    :param vc: Supplied vc
    :param smart_subsets: generate subsets smartly by branching
    :return: 2-tuple (status, new_vc) where status is True if
    it found a smaller vc in which case new_vc contains this vc.
    Else status is false and new_vc is empty_set()
    """
    vc = set(vc)
    ctr = 0
    subset_func = gen_ind_subsets if smart_subsets else brute_ind_subsets
    for ss in subset_func(graph.subgraph(vc)):
        if ctr % 1000 == 0: print(ctr, "done", file=sys.stderr)
        ctr += 1
        if len(ss) < 1:  # ignore empty subsets
            continue
        nbrs = set()
        for u in ss:  # accumulate neighborhood of subset
            nbrs.update(graph.neighbors_iter(u))
        if len(nbrs) < len(ss):  # found smaller neighborhood
            return True, vc - ss | nbrs
    return False, set()
def subgraph_rewire(G: nx.Graph, nodes: set, seed=None) -> Tuple[nx.Graph, nx.Graph]:
    """
    Rewire a graph with rewiring restricted to a subgraph of it.

    Arguments
    ---
    G: NetworkX graph
    nodes: Set of nodes that makes the subgraph
    seed: random seed
    """
    r = random.Random(seed)

    subgraph = G.subgraph(nodes)

    contributors_set = {
        n for n, attrs in subgraph.nodes(data=True) if attrs["type"] == "contributor"
    }

    grants_set = {
        n for n, attrs in subgraph.nodes(data=True) if attrs["type"] == "grant"
    }

    if len(subgraph.nodes) > 2 and len(subgraph.edges) > 1:
        edge = r.choice(tuple(subgraph.edges))
        edge_data = G.edges[edge]
        node_1 = r.choice(tuple(contributors_set))
        node_2 = r.choice(tuple(grants_set))
        G_new = G.copy()
        G_new.remove_edge(*edge)
        G_new.add_edge(node_1, node_2, **edge_data)
        return G_new
    else:
        raise ValueError("Subgraph must have more than two nodes & one edge")
Beispiel #30
0
def double_star_decomposition(graph : nx.Graph, pool : Pool):
    
    next_ = {graph}

    while not empty(next_):
        F = next_.pop()

        flag = False

        for u, v in vertex_combinations(F, 2):
            path = shortest_path(graph, u, v)

            if path and len(path) > 4:
                flag = True
                break

        if not flag:
            continue

        S = find_double_star_cutset(F, pool)

        if S is None:
            yield F
            continue

        for H in nx.connected_components(F.subgraph(vertex_set(F)-S)):
            next_.add(graph.subgraph(set(H | S)))
Beispiel #31
0
 def subnetwork(self, nbunch, pos=None):
     """ Returns Network instance with nbunch nodes, see subgraph. """
     if not pos:
         pos = self.pos
     H = Graph.subgraph(self, nbunch)
     for node in H:
         H.pos.update({node: pos[node][:2]})
         if len(pos[node]) > 2:
             H.ori.update({node: pos[node][2]})
         else:
             H.ori.update({node: self.ori[node]})
         H.labels.update({node: self.labels[node]})
     H._environment = deepcopy(self._environment)
     assert(isinstance(H, Network))
     return H
    def _get_fbvs(self, graph: Graph):
        # get the list of cycles
        cycles = cycle_basis(graph)

        # if the graph is already acyclic, there's nothing to remove, so return an empty set
        if len(cycles) == 0:
            return set()

        # get the set of nodes that is in at least one cycle
        nodes_in_cycles = set([item for sublist in cycles for item in sublist])

        min_num_to_remove = sys.maxsize
        min_nodes_to_remove = set()

        for node in nodes_in_cycles:
            # make an induced subgraph with the current node removed
            nodes = graph.nodes()
            nodes.remove(node)
            nodes_set = frozenset(nodes)

            if nodes_set in self.cacheDict:
                # if we have previously calculated the min fbvs for this induced subgraph, get that
                # fbvs from the cache dict
                nodes_to_remove = self.cacheDict[nodes_set]
            else:
                # otherwise we have to calculate it
                new_graph = graph.subgraph(nodes)
                nodes_to_remove = self._get_fbvs(new_graph)

                # add the newly calculated fbvs to the cache dict
                self.cacheDict[nodes_set] = nodes_to_remove

            if len(nodes_to_remove) < min_num_to_remove:
                min_num_to_remove = len(nodes_to_remove)
                nodes_to_remove.add(node)
                min_nodes_to_remove = nodes_to_remove

        return min_nodes_to_remove
Beispiel #33
0
class Network(HasTraits):
    """ The implementation of the Connectome Networks """

    implements(INetwork)

    # Network ID, from parsed GraphML the graphid
    networkid = ''

    # Network name
    networkname = Str
    
    # network name as seen in the TreeView
    name = Str
    
    # Is it an hierarchical network?
    hierarchical = CBool(False)

    # TODO: later, also Hypergraph?!
    # see: http://www.ploscompbiol.org/article/info%3Adoi%2F10.1371%2Fjournal.pcbi.1000385
    hypergraph = CBool(False)

    # Directionality of the Network, {True: 'directed', False: 'undirected'}
    directed = CBool(False)

    # metadata for the network
    metadata = Dict

    # NodeKeys from the parsed GraphML
    # These are Dict of Dict, all having strings
    nodekeys = {}
    
    # Edgekeys, from parsed GraphML
    edgekeys = {}

    # A NetworkX AttrGraph containing all the information
    graph = Any
        
    # Surface containers
    surfaces = List(ISurfaceContainer)
    
    # Surface containers loaded
    surfaces_loaded = List(ISurfaceContainer)
    
    # Volume data
    volumes = List(IVolume)
    
    # Track data
    tracks = List(ITrackfile)

    # is this network active, and thus a render manager displayed?
    active = Bool

    # the render manager of this network
    rendermanager = Instance(RenderManager)
    
    # DatasourceManager Instance of this network
    datasourcemanager = Instance(DatasourceManager)
    
    # private traits
    ###########
    
    # parent cfile this networks belongs to
    _parentcfile = Any

    # filezip of cfile
    _filezip = DelegatesTo('_parentcfile')

    # edge parameters for visualization
    _edge_para = Instance(EdgeParameters)

    # View
    traits_view = View(
        Item('networkname', style = 'readonly'),
        Item('hierarchical', style = 'simple'),
        Item('hypergraph', style = 'simple'),
        Item('directed', style = 'simple'),
        Item('active', style = 'simple'),
        title   = 'A network', 
    )

    def __init__(self, name, src = None, directed = '0', pickled_graph = None, \
                 hierarchical ='0', hypergraph = '0', graph = None):
        """ Initializes the network and sets the traits.
        
        Parameters
        ----------
        name : string
            the name of the network
        src : file handle or StringIO object
            the source text of the network to parse
        pickled_graph : NetworkX graph
            reference to a graph object, src should be None
        directed : bool
            Is the network directed?
        hierarchical : bool
            Is the network hierarchical? (default: '0') Not implemented yet.
        hypergraph : bool
            Is the network a hypergraph? (default: '0') Not implemented yet.
        
        """
        
        # initialize the traits
        self.networkname = name
        self.directed = int(directed)
        self.hierarchical = int(hierarchical)
        self.hypergraph = int(hypergraph)
        
        if src is None and not pickled_graph is None:
            self.load_pickled_graphml(pickled_graph)
        else:
            if not src is None:
                # generates NetworkX Graph
                self.graph = self.parse_network_graphml(src)
            elif not graph is None:
                self.graph = graph
            else:
                
                if self.directed:
                    from networkx import DiGraph
                    self.graph = DiGraph()
                    logger.info("Initialize with empty directed Graph")
                else:
                    from networkx import Graph
                    self.graph = Graph()
                    logger.info("Initialize with empty undirected Graph")
                
                
        # initializes the weight key of the graph
        # with the first edgekey
        if len(self.edgekeys) > 0:
            edgk = self.edgekeys.keys()
            if not 'weight' in edgk:
                self.set_weight_key(edgk[0])
        else:
            # try grabbing first edge from the graph
            if self.graph.number_of_edges() > 0:
                it = self.graph.edges_iter(data=True)
                edg = it.next()
                if len(edg[2]) > 0:
                    # if it has a weigth key, just leave it
                    edgk = edg[2].keys()
                    if not 'weight' in edgk:
                        self.set_weight_key(edgk[0])
            else:
                pass
                # logger.error('Cannot set weight key for network : ' + self.networkname)
                
    def _name_default(self):
        return self.networkname

    def _active_default(self):
        return False

    def _active_changed(self , value):
        if value:
            n = self.name
            if ' [Active]' not in n:
                self.name = "%s [Active]" % n
                
            # XXX: do refactor with threaded loading of surfaces
            # and default spring force layout for graph rendering!
            # see also TraitsUI Demos: Multi thread demo
            
            # load the surface containers data
            # make a deep copy of the already loaded surface containers
            import copy
            self.surfaces = copy.deepcopy(self.surfaces_loaded)
            for surfcont in self.surfaces:
                surfcont.load_surface_container()
            
            if self.rendermanager is None:
                self._create_datasourcemanager()
                self._create_renderer()
                # if there are no surfaces, initialize
                # network rendering, but only if dn_positions are given
                if len(self.surfaces) == 0:
                    logger.debug('No surfaces found. Try to render graph view with dn_position information.')
                    self.rendermanager.datasourcemanager._compute_3DLayout(-1, -1)
                    self.rendermanager.visualize_graph()
                else:
                    logger.debug('SurfaceContainer found. Try to render 3D View using %s.' % self.surfaces[0].name)
                    if len(self.surfaces[0].surfaces) == 0:
                        logger.debug('Rendering not possible because SurfaceContainer contains no surfaces.')
                    else:
                        logger.debug('Using first surface for rendering.')
                        self.surfaces[0].surfaces[0]._layout_3DView()
            
            if not self._parentcfile._workbenchwin is None:
                #from enthought.pyface.timer.api import do_later
                from enthought.pyface.api import GUI
                GUI.invoke_later(self._parentcfile._workbenchwin.status_bar_manager.set, message = '')
            
        else:
            self.name = self.name.replace(' [Active]', '')
            logger.debug('Close RenderManager scenes')
            self.rendermanager.close_scenes()
            logger.debug('All scenes closed.')
            # FIXME: what is happening in the following?
            # e.g. for instances. e.g. reset traits?
            # XXX: this is somehow not correct. do i need to use del
            # or remove/reset traits?
            self.rendermanager = None
            self.datasourcemanager = None
            self.surfaces = []

    def _de_activate(self):
        """ Toggles the internal state of the activation """
        if self.active:
            self.active = False
        else:
            self._parentcfile._workbenchwin.status_bar_manager.message = 'Activating network ...'
            self.active = True

    def _edge_parameters(self):
        """ Dialog to change edge attribute and thresholding """
        if self._edge_para is None:
            self._edge_para = EdgeParameters(self, self.rendermanager.attract.point_scalars_name)
            
        self._edge_para.configure_traits()


    def _create_renderer(self):
        """ Creates the renderer instance if not yet available
        and opens the scenes in mayavi """

        if self.active:
            if self.rendermanager is None:
                logger.debug('Create a RenderManager instance')
                self.rendermanager = RenderManager(network=self)
            else:
                logger.debug('RenderManager instance already running. This is an error.')

    def _create_datasourcemanager(self):
        """ Creates the datasource manager instance if not yet available """
        if self.active:
            if self.datasourcemanager is None:
                logger.debug('Create a DatasourceManager instance')
                self.datasourcemanager = DatasourceManager(network=self)
            else:
                logger.debug('DatasourceManager instance already running. This is an error.')


    def _render_matrix(self):
        """ Invokes the connectivity matrix viewer """
        # assume the network is activated (i.e. data source generated)
        # we need the edge parameter instance initialized
        if self._edge_para is None:
            self._edge_para = EdgeParameters(self, self.rendermanager.attract.point_scalars_name)
            
        logger.debug('Invoke Matrix Viewer...')
        self.rendermanager.invoke_matrix_viewer()
        
    def _trackvis_launch(self):
        """ Generates scene file and launch Trackvis on the selected nodes """
        import tempfile
        
        logger.debug('Starting TrackVis ...')

        # extract selected subgraph
        selectionlist = self.get_selectiongraph_list()
        
        if len(selectionlist) == 0:
            # message            
            from enthought.traits.ui.message import message
            message(message = 'No nodes selected for ROI creation!', title = 'Infomessage', buttons = [ 'OK' ], parent = None)

        tmpgraph = self.graph.subgraph(selectionlist)

        # extract trackfile temporarily
        if len(self.tracks) == 0:
            logger.info('No trackfile found to invoke Trackvis.')
            return
        else:

            # load the first trackfile
            trackfname = self.tracks[0].load_trackfile_to_file()

            # find the first valid segmentation volume in the self.volumes list
            for vol in self.volumes:
                if vol.segmentation:
                    logger.debug('Found a segmentation volume file. Assume labels are corresponding.')
                    volumefname = vol.load_volume_to_file()
                    break

        # generate the scene file in the temporary folder
        tmpscenefile=tempfile.mkstemp(prefix='tmp', suffix='.scene')
            
        # generate trackfile        
        generate_scene_file(scenefname=tmpscenefile[1], \
                          trackfname = trackfname, \
                          volumefname = volumefname, \
                          selectiongraph = tmpgraph)
        
        # execute trackvis in a thread
        pref = preference_manager.preferences       
        action = ThreadedTrackvis(tvpath = pref.get('cviewer.plugins.ui.trackvispath'), \
                                    fname = tmpscenefile[1], \
                                    trkfname = trackfname,\
                                    volfname = volumefname)
        action.start()
    

    def add_surface_container(self, surfacecontainer):
        """ Add a surface container to the loaded list
        
        Parameters
        ----------
        surfacecontainer : `ISurfaceContainer` instance
            a surface container object
        
        """
        surfacecontainer._networkref = self
        self.surfaces_loaded.append(surfacecontainer)

    def add_volume(self, volume):
        """ Adds a volume to the volumes list
        
        Parameters
        ----------
        volume : `IVolume` instance
            a volume object
        
        """
        self.volumes.append(volume)
        
    def add_trackfile(self, trackfile):
        """ Adds a trackfile to the tracks list
        
        Parameters
        ----------
        trackfile : `ITrackfile` instance
            a trackfile of type ITrackfile
        
        """
        self.tracks.append(trackfile)

    def unselect_all(self):
        """ Unselects every node in the current network """
        if self.datasourcemanager is None:
            raise Exception('No DatasourceManager. You have to first activate the network and render it.')
        from numpy import array
        # get all the nodes
        graphnodes = self.datasourcemanager._srcobj.relabled_graph.nodes()
        # and unselect all nodes
        self.rendermanager._select_nodes(selection_node_array = array(graphnodes))

    def select_all(self):
        """ Selects all nodes in the current network """
        if self.datasourcemanager is None:
            raise Exception('No DatasourceManager. You have to first activate the network and render it.')
        from numpy import array
        # get all the nodes
        graphnodes = self.datasourcemanager._srcobj.relabled_graph.nodes()
        # and select all nodes
        self.rendermanager._select_nodes(selection_node_array = array(graphnodes), activate = True)
  
    def set_selectiongraph(self, sellist, activate = False):
        """ Sets the selected nodes in the network to active.
        
        Parameters
        ----------
        sellist : array_like
            a list of nodeids conforming to the NetworkX node id
        activate : boolean
            set the selectionlist nodes to activated?
        
        """
        from numpy import array, int16
        graphnodes = self.graph.nodes(data=False)

        if self.rendermanager is None:
            raise Exception('No RenderManager. You have to first activate the network and render it.')

        if len(sellist) == 0:
            self.unselect_all()
            return
        
        from numpy import array, append
        tmparr = array([])
        for node in sellist:
            # check if it is a valid graph node id
            if node in graphnodes:
                # get the node id as integer
                j = int(node.lstrip('n'))-1
                # extend empty array with node id
                tmparr = append(tmparr, j)
                
        self.rendermanager._select_nodes(selection_node_array = array(tmparr, dtype = int16), activate = activate)

    def get_selectiongraph_list(self):
        """ Returns a list of the node ids that were selected in
        the rendered scene.
        
        """
        if self.datasourcemanager is None:
            raise Exception('No DatasourceManager. You have to first activate the network and render it.')
        
        import numpy as np
        
        sel_list = []
        
        if not self.active:
            return sel_list
        
        selnodesarray = self.datasourcemanager._srcobj.selected_nodes
        
        # array with indices where the nodes are selected (==1)
        idx = np.where(selnodesarray == 1)[0]
        
        for i in idx:
            sel_list.append('n' + str(i + 1))
        
        return sel_list
        

    def set_weight_key(self, weight_key = None):
        """ Sets the weight key in the graph representation of the network.
        
        Parameters
        ----------
        weight_key : Str
            Must be a possible existing edge key
            
        """
        if not weight_key is None:
            for u,v,d in self.graph.edges(data=True):
                self.graph[u][v]['weight']=d[weight_key]
            return True
        else:
            return False

    def get_matrix(self, weight_key = None):
        """ Returns the connectivity matrix of the network with the nodes
        ordered according to their id in the GraphML file.
        
        Parameters
        ----------
        weight_key : Str
            Possible key value of the edges
        
        Returns
        -------
        matrix : `Numpy.array` instance
            The connectivity matrix
        
        """
        nr_nodes = len(self.graph.nodes())
        
        if not weight_key is None:
            #FIXME: sanity check if weight_key exists
            # thanks to Aric Hagberg
            for u,v,d in self.graph.edges(data=True):
                self.graph[u][v]['weight']=d[weight_key]
                
        nodes = [(lambda nmod:'n'+str(nmod))(node) for node in range(1,nr_nodes + 1)]
        from networkx import to_numpy_matrix
        return to_numpy_matrix(self.graph, nodelist = nodes)

    def toggle_surface(self):
        """ Toggle the surface for the selected network nodes """
        if self.rendermanager is None:
            raise Exception('No RenderManager. You have to first activate the network and render it.')
        self.rendermanager._toggle_surface()
        
    def show_surface(self):
        """ Shows the surface for the selected network nodes """
        if self.rendermanager is None:
            raise Exception('No RenderManager. You have to first activate the network and render it.')
        self.rendermanager._show_surface()

    def load_pickled_graphml(self, graph):
        """ Loads a pickled GraphML file
        
        Parameters
        ----------
        graph : NetworkX Graph instance
            A graph instance
            
        """
        
        # setting the graph
        self.graph = graph
        
        if self.graph.has_node('n0'):
            if self.graph.node['n0'].has_key('nodekeys'):
                # extracting the node keys from the first node
                self.nodekeys = self.graph.node['n0']['nodekeys']
                
            # extracting the edge keys from the first edge (without explanation)
            if self.graph.node['n0'].has_key('edgekeys'):
                self.edgekeys = self.graph.node['n0']['edgekeys']
                
            if self.graph.node['n0'].has_key('graphid'):
                self.networkid = self.graph.node['n0']['graphid']
                
            # remove node
            self.graph.remove_node('n0')
    
    def _return_default_edgevalue(self, edgekeys, key):
        """ Looks up if there is a default value defined, otherwise
        return zero """
        if edgekeys[key].has_key('default'):
            return float(edgekeys[key]['default'])
        else:
            return 0.0
    
    def parse_network_graphml(self, path):
        """ Read network in GraphML format from a path.
        
        Parameters
        ----------
        path : string
            path the the GraphML file
        
        Returns
        -------
        graph : NetworkX `Graph`
            
        """
        import networkx as nx
        from networkx.utils import _get_fh
        from lxml import etree
        
        # Return a file handle for given path.
        # Path can be a string or a file handle.
        # Attempt to uncompress/compress files ending in .gz and .bz2.
        
        fh=_get_fh(path,mode='r')
        
        tree = etree.parse(fh)
        # get the root node from parsed lxml
        root = tree.getroot()
        
        # Schema Validation
        # http://codespeak.net/lxml/validation.html#xmlschema
        
        # define the namespace prefixes
        nsprefix = "{%s}" % root.nsmap[None]
        nsxlink = "{%s}" % root.nsmap['xlink']
        
        nodekeys = {}
        edgekeys = {}
        defaultDirected = [True]
        
        # Parse the KEYs
        for child in root.iterchildren():
            if child.tag == (nsprefix+'key'):
                
                attribs = child.attrib
        
                ddkeys = {}
                for mchildren in child:
                    if mchildren.tag == (nsprefix+'default'):
                        ddkeys['default'] = mchildren.text
                    elif mchildren.tag == (nsprefix+'desc'):
                        ddkeys['desc'] = mchildren.text
        
                if child.attrib['for'] == 'node':
                    # Parse all the node keys
                    # Read in the description and the default (if existing)
                    # dict of dicts for nodes: key1: the id; key2: rest: attr.name, attr.type, desc, default
                    nodekeys[attribs['id']] = {'attr.name' : attribs['attr.name'], \
                                               'attr.type' : attribs['attr.type']}
                    # add default/desc keys if existing
                    nodekeys[attribs['id']] = ddkeys
                        
                elif child.attrib['for'] == 'edge':
                    # Parse all the edge keys
                    # Read in the description and the default (if existing)
                    # dict of dicts for edges: key1: the id; key2: rest: attr.name, attr.type, desc, default
                    edgekeys[attribs['id']] = {'attr.name' : attribs['attr.name'], \
                                               'attr.type' : attribs['attr.type']}
                    # add default/desc keys if existing
                    edgekeys[attribs['id']] = ddkeys
                    
                else:
                    logger.error("The 'for' attribute of key-tag not known, must be either node or edge")
                    
            elif child.tag == (nsprefix+'graph'):
                # start parsing the graph into networkx data structure
                # create graph depending on (either AttrGraph or AttrDiGraph)
                #   directionality: undirected/directed
                #   version of networkx:
                #   contains self-loops
                #   edges have dicts
                #   data per graph/node/edge
                for attr, value in child.items():
                    if attr == 'edgedefault' and value == 'undirected':
                        defaultDirected[0] = False
                    elif attr == 'id':
                        graphid = value
                
                if defaultDirected[0]:
                    G = nx.DiGraph()
                else:
                    G = nx.Graph()
    
                # add id, nodekeys and edkeys as traits               
                self.networkid = graphid
                self.nodekeys = nodekeys
                self.edgekeys = edgekeys
    
                # iterate over all nodes and edges
                for children in child.iterchildren():
                    if children.tag == (nsprefix+'node'):
                        
                        # parse the node
                        for attr, value in children.items():
                            if attr == 'id':
                                # add the node with corresponding id
                                G.add_node(value)
                                # keep node id to store attributes
                                nodeid = value
                            elif attr == (nsxlink+'href'):
                                # add xlink to node dictionary
                                G.node[nodeid]['xlink'] = value
                            else:
                                # node attribute not known
                                logger.warning('The following node attribute is not known and thus discarded:'+ attr + ':' + value)
        
                        # parse node data, add to node dict
                        for data in children.iterchildren():
                            # read the keylabel, i.e. the data attribute name
                            keylabel = data.attrib['key']
                            # is the keylabel in the list of allowed keys
                            if nodekeys.has_key(keylabel):
                                if not data.text == '':
                                    # add data to the node's dict
                                    G.node[nodeid][keylabel] = data.text

                                else:
                                    # no data available, check if default value exists
                                    if nodekeys[keylabel].has_key('default'):
                                        # add default data to the node's dict
                                        G.node[nodeid][keylabel] = nodekeys[keylabel]['default']
                                        logger.debug('Added default value '+ keylabel + ':' + nodekeys[keylabel]['default'])
                                    else:
                                        logger.warning('Nor data nor default value defined for ' + keylabel)
                                        # TODO: Work with exceptions!
                            else:
                                logger.warning("Data entry with key " + keylabel + " not defined.")
        
                        
                    elif children.tag == (nsprefix+'edge'):
                        
                        # parse the edge
                        # parse its attributes
                        for attr, value in children.items():
                            if attr == 'id':
                                # no usage of edge id
                                # add the edge with corresponding id
                                src = children.attrib['source']
                                tar = children.attrib['target']
                                G.add_edge(src, tar)
                                # keep dest and tar id to store attributes
                                srcid = src
                                tarid = tar
                            elif attr == (nsxlink+'href'):
                                # add xlink to edge dictionary
                                G.edge[srcid][tarid]['xlink'] = value
        
                        # parse data, and add to the edge dict
                        for data in children.iterchildren():
                            # read the keylabel, i.e. the data attribute name
                            keylabel = data.attrib['key']
                            # is the keylabel in the list of allowed keys
                            if self.edgekeys.has_key(keylabel):
                                if not data.text == '':
                                    # add data to the edge's dict, assume float!!
                                    G.edge[srcid][tarid][keylabel] = float(data.text)
                                else:
                                    # no data available, check if default value exists
                                    G.edge[srcid][tarid][keylabel] = self._return_default_edgevalue(self.edgekeys, keylabel)
                        data_keys = G.edge[srcid][tarid].keys()
                        # check if we missed some edge keys that are available in the header
                        for k, v in self.edgekeys.items():
                            if not k in data_keys:
                                G.edge[srcid][tarid][k] = self._return_default_edgevalue(self.edgekeys, k)
        
        # return the generated network graph
        return G