Ejemplo n.º 1
0
def pullback(b, c, d, b_d, c_d, inplace=False):
    """Find the pullback from b -> d <- c.

    Given h1 : B -> D; h2 : C -> D returns A, rh1, rh2
    with rh1 : A -> B; rh2 : A -> C and A the pullback.
    """
    if inplace is True:
        a = b
    else:
        a = type(b)()

    # Check homomorphisms
    check_homomorphism(b, d, b_d)
    check_homomorphism(c, d, c_d)

    hom1 = {}
    hom2 = {}

    f = b_d
    g = c_d

    for n1 in b.nodes():
        for n2 in c.nodes():
            if f[n1] == g[n2]:
                new_attrs = merge_attributes(b.node[n1],
                                             c.node[n2],
                                             'intersection')
                if n1 not in a.nodes():
                    add_node(a, n1, new_attrs)
                    hom1[n1] = n1
                    hom2[n1] = n2
                else:
                    i = 1
                    new_name = str(n1) + str(i)
                    while new_name in a.nodes():
                        i += 1
                        new_name = str(n1) + str(i)
                    # if n2 not in a.nodes():
                    add_node(a, new_name, new_attrs)
                    hom1[new_name] = n1
                    hom2[new_name] = n2

    for n1 in a.nodes():
        for n2 in a.nodes():
            if (hom1[n1], hom1[n2]) in b.edges() or \
               ((not a.is_directed()) and (hom1[n2], hom1[n1]) in b.edges()):
                if (hom2[n1], hom2[n2]) in c.edges() or \
                   ((not a.is_directed) and (hom2[n2], hom2[n1]) in c.edges()):
                    add_edge(a, n1, n2)
                    set_edge(
                        a,
                        n1,
                        n2,
                        merge_attributes(
                            get_edge(b, hom1[n1], hom1[n2]),
                            get_edge(c, hom2[n1], hom2[n2]),
                            'intersection'))
    check_homomorphism(a, b, hom1)
    check_homomorphism(a, c, hom2)
    return (a, hom1, hom2)
Ejemplo n.º 2
0
def pullback(b, c, d, b_d, c_d, inplace=False):
    """Find the pullback from b -> d <- c.

    Given h1 : B -> D; h2 : C -> D returns A, rh1, rh2
    with rh1 : A -> B; rh2 : A -> C and A the pullback.
    """
    if inplace is True:
        a = b
    else:
        a = type(b)()

    # Check homomorphisms
    check_homomorphism(b, d, b_d)
    check_homomorphism(c, d, c_d)

    hom1 = {}
    hom2 = {}

    f = b_d
    g = c_d

    for n1 in b.nodes():
        for n2 in c.nodes():
            if f[n1] == g[n2]:
                new_attrs = merge_attributes(b.node[n1], c.node[n2],
                                             'intersection')
                if n1 not in a.nodes():
                    add_node(a, n1, new_attrs)
                    hom1[n1] = n1
                    hom2[n1] = n2
                else:
                    i = 1
                    new_name = str(n1) + str(i)
                    while new_name in a.nodes():
                        i += 1
                        new_name = str(n1) + str(i)
                    # if n2 not in a.nodes():
                    add_node(a, new_name, new_attrs)
                    hom1[new_name] = n1
                    hom2[new_name] = n2

    for n1 in a.nodes():
        for n2 in a.nodes():
            if (hom1[n1], hom1[n2]) in b.edges() or \
               ((not a.is_directed()) and (hom1[n2], hom1[n1]) in b.edges()):
                if (hom2[n1], hom2[n2]) in c.edges() or \
                   ((not a.is_directed) and (hom2[n2], hom2[n1]) in c.edges()):
                    add_edge(a, n1, n2)
                    set_edge(
                        a, n1, n2,
                        merge_attributes(get_edge(b, hom1[n1], hom1[n2]),
                                         get_edge(c, hom2[n1], hom2[n2]),
                                         'intersection'))
    check_homomorphism(a, b, hom1)
    check_homomorphism(a, c, hom2)
    return (a, hom1, hom2)
Ejemplo n.º 3
0
def pullback(b, c, d, b_d, c_d):
    """Find the pullback from b -> d <- c.

    Given h1 : B -> D; h2 : C -> D returns A, rh1, rh2
    with rh1 : A -> B; rh2 : A -> C and A the pullback.
    """
    a = NXGraph()

    # Check homomorphisms
    check_homomorphism(b, d, b_d)
    check_homomorphism(c, d, c_d)

    a_b = {}
    a_c = {}

    f = b_d
    g = c_d

    for n1 in b.nodes():
        for n2 in c.nodes():
            if f[n1] == g[n2]:
                new_attrs = merge_attributes(b.get_node(n1), c.get_node(n2),
                                             'intersection')
                if n1 not in a.nodes():
                    a.add_node(n1, new_attrs)
                    a_b[n1] = n1
                    a_c[n1] = n2
                else:
                    i = 1
                    new_name = str(n1) + str(i)
                    while new_name in a.nodes():
                        i += 1
                        new_name = str(n1) + str(i)
                    # if n2 not in a.nodes():
                    a.add_node(new_name, new_attrs)
                    a_b[new_name] = n1
                    a_c[new_name] = n2

    for n1 in a.nodes():
        for n2 in a.nodes():
            if (a_b[n1], a_b[n2]) in b.edges():
                if (a_c[n1], a_c[n2]) in c.edges():
                    a.add_edge(n1, n2)
                    a.set_edge(
                        n1, n2,
                        merge_attributes(b.get_edge(a_b[n1], a_b[n2]),
                                         c.get_edge(a_c[n1], a_c[n2]),
                                         'intersection'))
    check_homomorphism(a, b, a_b)
    check_homomorphism(a, c, a_c)
    return (a, a_b, a_c)
Ejemplo n.º 4
0
def get_edge(graph, s, t):
    """Get edge attributes.

    Parameters
    ----------
    graph : networkx.(Di)Graph
    s : hashable, source node id.
    t : hashable, target node id.
    """
    if graph.is_directed():
        return graph.edge[s][t]
    else:
        return merge_attributes(graph.edge[s][t], graph.edge[s][t])
Ejemplo n.º 5
0
def get_edge(graph, s, t):
    """Get edge attributes.

    Parameters
    ----------
    graph : networkx.(Di)Graph
    s : hashable, source node id.
    t : hashable, target node id.
    """
    if graph.is_directed():
        return graph.edge[s][t]
    else:
        return merge_attributes(graph.edge[s][t], graph.edge[s][t])
Ejemplo n.º 6
0
def pushout(a, b, c, a_b, a_c, inplace=False):
    """Find the pushour of the span b <- a -> c."""
    def get_classes_to_merge():
        pass

    check_homomorphism(a, b, a_b)
    check_homomorphism(a, c, a_c)

    if inplace is True:
        d = b
    else:
        d = NXGraph()
        d.add_nodes_from(b.nodes(data=True))
        d.add_edges_from(b.edges(data=True))

    b_d = id_of(b.nodes())
    c_d = dict()

    # Add/merge nodes
    merged_nodes = dict()
    for c_n in c.nodes():
        a_keys = keys_by_value(a_c, c_n)
        # Add nodes
        if len(a_keys) == 0:
            if c_n not in d.nodes():
                new_name = c_n
            else:
                new_name = d.generate_new_node_id(c_n)
            d.add_node(new_name, c.get_node(c_n))
            c_d[c_n] = new_name
        # Keep nodes
        elif len(a_keys) == 1:
            c_d[a_c[a_keys[0]]] = b_d[a_b[a_keys[0]]]
        # Merge nodes
        else:
            nodes_to_merge = set()
            # find the nodes that need to be merged
            for k in a_keys:
                nodes_to_merge.add(a_b[k])

            # find if exists already some merged node to
            # which the new node should be merged
            groups_to_remove = set()
            new_groups = set()
            merge_done = False
            for k in merged_nodes.keys():
                if nodes_to_merge.issubset(merged_nodes[k]):
                    merge_done = True
                else:
                    intersect_with_group = nodes_to_merge.intersection(
                        merged_nodes[k])
                    if len(intersect_with_group) > 0:
                        new_nodes_to_merge =\
                            nodes_to_merge.difference(merged_nodes[k])
                        if len(new_nodes_to_merge) > 0:
                            new_nodes_to_merge.add(k)
                            new_name = d.merge_nodes(new_nodes_to_merge)
                            merged_nodes[new_name] = merged_nodes[k].union(
                                nodes_to_merge)
                            groups_to_remove.add(k)
                            new_groups.add(new_name)

            if len(groups_to_remove) > 0:
                new_name = d.merge_nodes(new_groups)
                merged_nodes[new_name] = set()
                for g in new_groups:
                    merged_nodes[new_name] = merged_nodes[new_name].union(
                        merged_nodes[g])
                for group in groups_to_remove:
                    del merged_nodes[group]
            elif not merge_done:
                if len(nodes_to_merge) > 1:
                    new_name = d.merge_nodes(nodes_to_merge)
                    merged_nodes[new_name] = nodes_to_merge
                else:
                    new_name = list(nodes_to_merge)[0]

            c_d[c_n] = new_name

            for node in nodes_to_merge:
                b_d[node] = new_name

            for k in c_d.keys():
                for vv in keys_by_value(a_c, k):
                    if b_d[a_b[vv]] == new_name:
                        c_d[k] = new_name

    # Add edges
    for (n1, n2) in c.edges():
        if (c_d[n1], c_d[n2]) not in d.edges():
            d.add_edge(c_d[n1], c_d[n2], c.get_edge(n1, n2))

    # Add node attrs
    for c_n in c.nodes():
        a_keys = keys_by_value(a_c, c_n)
        # Add attributes to the nodes which stayed invariant
        if len(a_keys) == 1:
            attrs_to_add = dict_sub(c.get_node(c_n), a.get_node(a_keys[0]))
            d.add_node_attrs(c_d[c_n], attrs_to_add)
        # Add attributes to the nodes which were merged
        elif len(a_keys) > 1:
            merged_attrs = {}
            for k in a_keys:
                merged_attrs = merge_attributes(merged_attrs, a.get_node(k))
            attrs_to_add = dict_sub(c.get_node(c_n), merged_attrs)
            d.add_node_attrs(c_d[c_n], attrs_to_add)

    # Add edge attrs
    for (n1, n2) in c.edges():
        d_n1 = c_d[n1]
        d_n2 = c_d[n2]
        attrs_to_add = dict_sub(c.get_edge(n1, n2), d.get_edge(d_n1, d_n2))
        d.add_edge_attrs(c_d[n1], c_d[n2], attrs_to_add)
    return (d, b_d, c_d)
Ejemplo n.º 7
0
def merge_nodes(graph,
                nodes,
                node_id=None,
                method="union",
                edge_method="union"):
    """Merge a list of nodes.

    Parameters
    ----------

    graph : nx.(Di)Graph
    nodes : iterable
        Collection of node id's to merge.
    node_id : hashable, optional
        Id of a new node corresponding to the result of merge.
    method : optional
        Method of node attributes merge: if `"union"` the resulting node
        will contain the union of all attributes of the merged nodes,
        if `"intersection"`, the resulting node will contain their
        intersection. Default value is `"union"`.
    edge_method : optional
        Method of edge attributes merge: if `"union"` the edges that were
        merged will contain the union of all attributes,
        if `"intersection"` -- their ntersection. Default value is `"union"`.

    Returns
    -------
    node_id : hashable
        Id of a new node corresponding to the result of merge.

    Raises
    ------
    ReGraphError
        If unknown merging method is provided
    GraphError
        If some nodes from `nodes` do not exist in the graph.

    Examples
    --------

    >>> g = nx.DiGraph()
    >>> add_nodes_from(g, [(1, {"a": 1, "b": 1}), 2, (3, {"a": 3, "c": 3})])
    >>> add_edges_from(g, [(1, 3), (1, 2), (2, 3)])
    >>> merge_nodes(g, [1, 3], "merged_node")
    >>> g.nodes()
    ["merged_node", 2]
    >>> g.edges()
    [("merged_node", "merged_node"), ("merged_node", 2), (2, "merged_node")]
    >>> g.node["merged_node"]
    {"a": {1, 3}, "b": {1}, "c": {3}}

    """
    if len(nodes) == 1:
        if node_id is not None:
            relabel_node(graph, nodes[0], node_id)

    elif len(nodes) > 1:

        if method is None:
            method = "union"

        if edge_method is None:
            method = "union"

        # Generate name for new node
        if node_id is None:
            node_id = "_".join([str(n) for n in nodes])
        elif node_id in graph.nodes() and (node_id not in nodes):
            raise GraphError("New name for merged node is not valid: "
                             "node with name '%s' already exists!" % node_id)

        # Merge data attached to node according to the method specified
        # restore proper connectivity
        if method == "union":
            attr_accumulator = {}
        elif method == "intersection":
            attr_accumulator = deepcopy(graph.node[nodes[0]])
        else:
            raise ReGraphError("Merging method '%s' is not defined!" % method)

        self_loop = False
        self_loop_attrs = {}

        if graph.is_directed():
            source_nodes = set()
            target_nodes = set()

            source_dict = {}
            target_dict = {}
        else:
            neighbors = set()
            neighbors_dict = {}
        all_neighbors = set()

        for node in nodes:
            all_neighbors |= set(graph.__getitem__(node).keys())
            attr_accumulator = merge_attributes(attr_accumulator,
                                                graph.node[node], method)

            if graph.is_directed():
                in_edges = graph.in_edges(node)
                out_edges = graph.out_edges(node)

                # manage self loops
                for s, t in in_edges:
                    if s in nodes:
                        self_loop = True
                        if len(self_loop_attrs) == 0:
                            self_loop_attrs = graph.edge[s][t]
                        else:
                            self_loop_attrs = merge_attributes(
                                self_loop_attrs, graph.edge[s][t], edge_method)

                for s, t in out_edges:
                    if t in nodes:
                        self_loop = True
                        if len(self_loop_attrs) == 0:
                            self_loop_attrs = graph.edge[s][t]
                        else:
                            self_loop_attrs = merge_attributes(
                                self_loop_attrs, graph.edge[s][t], edge_method)

                source_nodes.update(
                    [n if n not in nodes else node_id for n, _ in in_edges])
                target_nodes.update(
                    [n if n not in nodes else node_id for _, n in out_edges])

                for edge in in_edges:
                    if not edge[0] in source_dict.keys():
                        attrs = graph.edge[edge[0]][edge[1]]
                        source_dict.update({edge[0]: attrs})
                    else:
                        attrs = merge_attributes(source_dict[edge[0]],
                                                 graph.edge[edge[0]][edge[1]],
                                                 edge_method)
                        source_dict.update({edge[0]: attrs})

                for edge in out_edges:
                    if not edge[1] in target_dict.keys():
                        attrs = graph.edge[edge[0]][edge[1]]
                        target_dict.update({edge[1]: attrs})
                    else:
                        attrs = merge_attributes(target_dict[edge[1]],
                                                 graph.edge[edge[0]][edge[1]],
                                                 edge_method)
                        target_dict.update({edge[1]: attrs})
            else:
                for n in graph.neighbors(node):
                    if n in nodes:
                        self_loop = True
                        if len(self_loop_attrs) == 0:
                            self_loop_attrs = graph.edge[n][node]
                        else:
                            self_loop_attrs = merge_attributes(
                                self_loop_attrs, graph.edge[n][node],
                                edge_method)

                neighbors.update(
                    [n for n in graph.neighbors(node) if n not in nodes])
                for n in graph.neighbors(node):
                    if n not in nodes:
                        if n not in neighbors_dict.keys():
                            attrs = graph.edge[n][node]
                            neighbors_dict.update({n: attrs})
                        else:
                            attrs = merge_attributes(neighbors_dict[n],
                                                     graph.edge[n][node],
                                                     edge_method)
                            neighbors_dict.update({n: attrs})

            graph.remove_node(node)
            all_neighbors -= {node}

        add_node(graph, node_id, attr_accumulator)
        all_neighbors.add(node_id)

        if graph.is_directed():
            if self_loop:
                add_edges_from(graph, [(node_id, node_id)])
                graph.edge[node_id][node_id] = self_loop_attrs
            for n in source_nodes:
                if not exists_edge(graph, n, node_id):
                    add_edge(graph, n, node_id)
            for n in target_nodes:
                if not exists_edge(graph, node_id, n):
                    add_edge(graph, node_id, n)

            # Attach accumulated attributes to edges
            for node, attrs in source_dict.items():
                if node not in nodes:
                    graph.edge[node][node_id] = attrs
            for node, attrs in target_dict.items():
                if node not in nodes:
                    graph.edge[node_id][node] = attrs
        else:
            if self_loop:
                add_edges_from(graph, [(node_id, node_id)])
                graph.edge[node_id][node_id] = self_loop_attrs

            add_edges_from(graph, [(n, node_id) for n in neighbors])

            # Attach accumulated attributes to edges
            for node, attrs in neighbors_dict.items():
                if node not in nodes:
                    graph.edge[node][node_id] = attrs
                    graph.edge[node_id][node] = attrs

        return node_id
Ejemplo n.º 8
0
    def merge_nodes(self,
                    nodes,
                    node_id=None,
                    method="union",
                    edge_method="union"):
        """Merge a list of nodes.

        Parameters
        ----------

        nodes : iterable
            Collection of node id's to merge.
        node_id : hashable, optional
            Id of a new node corresponding to the result of merge.
        method : optional
            Method of node attributes merge: if `"union"` the resulting node
            will contain the union of all attributes of the merged nodes,
            if `"intersection"`, the resulting node will contain their
            intersection. Default value is `"union"`.
        edge_method : optional
            Method of edge attributes merge: if `"union"` the edges that were
            merged will contain the union of all attributes,
            if `"intersection"` -- their ntersection.
            Default value is `"union"`.
        """
        if len(nodes) > 1:

            if method is None:
                method = "union"

            if edge_method is None:
                method = "union"

            # Generate name for new node
            if node_id is None:
                node_id = "_".join(sorted([str(n) for n in nodes]))
                if node_id in self.nodes():
                    node_id = self.generate_new_node_id(node_id)

            elif node_id in self.nodes() and (node_id not in nodes):
                raise GraphError("New name for merged node is not valid: "
                                 "node with name '%s' already exists!" %
                                 node_id)
            # Merge data attached to node according to the method specified
            # restore proper connectivity
            if method == "union":
                attr_accumulator = {}
            elif method == "intersection":
                attr_accumulator = safe_deepcopy_dict(self.get_node(nodes[0]))
            else:
                raise ReGraphError(
                    "Merging method '{}' is not defined!".format(method))

            self_loop = False
            self_loop_attrs = {}

            source_nodes = set()
            target_nodes = set()

            source_dict = {}
            target_dict = {}

            for node in nodes:
                attr_accumulator = merge_attributes(attr_accumulator,
                                                    self.get_node(node),
                                                    method)

                in_edges = self.in_edges(node)
                out_edges = self.out_edges(node)

                # manage self loops
                for s, t in in_edges:
                    if s in nodes:
                        self_loop = True
                        if len(self_loop_attrs) == 0:
                            self_loop_attrs = self.get_edge(s, t)
                        else:
                            self_loop_attrs = merge_attributes(
                                self_loop_attrs, self.get_edge(s, t),
                                edge_method)

                for s, t in out_edges:
                    if t in nodes:
                        self_loop = True
                        if len(self_loop_attrs) == 0:
                            self_loop_attrs = self.get_edge(s, t)
                        else:
                            self_loop_attrs = merge_attributes(
                                self_loop_attrs, self.get_edge(s, t),
                                edge_method)

                source_nodes.update(
                    [n if n not in nodes else node_id for n, _ in in_edges])
                target_nodes.update(
                    [n if n not in nodes else node_id for _, n in out_edges])

                for edge in in_edges:
                    if not edge[0] in source_dict.keys():
                        attrs = self.get_edge(edge[0], edge[1])
                        source_dict.update({edge[0]: attrs})
                    else:
                        attrs = merge_attributes(
                            source_dict[edge[0]],
                            self.get_edge(edge[0], edge[1]), edge_method)
                        source_dict.update({edge[0]: attrs})

                for edge in out_edges:
                    if not edge[1] in target_dict.keys():
                        attrs = self.get_edge(edge[0], edge[1])
                        target_dict.update({edge[1]: attrs})
                    else:
                        attrs = merge_attributes(
                            target_dict[edge[1]],
                            self.get_edge(edge[0], edge[1]), edge_method)
                        target_dict.update({edge[1]: attrs})
                self.remove_node(node)

            self.add_node(node_id, attr_accumulator)

            if self_loop:
                self.add_edges_from([(node_id, node_id)])
                self.set_edge(node_id, node_id, self_loop_attrs)
            for n in source_nodes:
                if not self.exists_edge(n, node_id):
                    self.add_edge(n, node_id)
            for n in target_nodes:
                if not self.exists_edge(node_id, n):
                    self.add_edge(node_id, n)

            # Attach accumulated attributes to edges
            for node, attrs in source_dict.items():
                if node not in nodes:
                    self.set_edge(node, node_id, attrs)
            for node, attrs in target_dict.items():
                if node not in nodes:
                    self.set_edge(node_id, node, attrs)

            return node_id
        else:
            raise ReGraphError(
                "More than two nodes should be specified for merging!")
Ejemplo n.º 9
0
def new_merge_nodes(graph, nodes, node_id=None, method="union", edge_method="union"):
    """Merge a list of nodes.

    Parameters
    ----------

    graph : nx.(Di)Graph
    nodes : list
        List of node id's to merge.
    node_id : hashable, optional
        Id of a new node corresponding to the result of merge.
    method : optional
        Method of node attributes merge: if `"union"` the resulting node
        will contain the union of all attributes of the merged nodes,
        if `"intersection"`, the resulting node will contain their
        intersection. Default value is `"union"`.
    edge_method : optional
        Method of edge attributes merge: if `"union"` the edges that were
        merged will contain the union of all attributes,
        if `"intersection"` -- their ntersection. Default value is `"union"`.

    Returns
    -------
    node_id : hashable
        Id of a new node corresponding to the result of merge.

    Raises
    ------
    ReGraphError
        If unknown merging method is provided
    GraphError
        If some nodes from `nodes` do not exist in the graph.

    Examples
    --------

    >>> g = nx.DiGraph()
    >>> add_nodes_from(g, [(1, {"a": 1, "b": 1}), 2, (3, {"a": 3, "c": 3})])
    >>> add_edges_from(g, [(1, 3), (1, 2), (2, 3)])
    >>> merge_nodes(g, [1, 3], "merged_node")
    >>> g.nodes()
    ["merged_node", 2]
    >>> g.edges()
    [("merged_node", "merged_node"), ("merged_node", 2), (2, "merged_node")]
    >>> g.node["merged_node"]
    {"a": {1, 3}, "b": {1}, "c": {3}}

    """
    if len(nodes) == 1:
        if node_id is not None:
            relabel_node(graph, nodes[0], node_id)

    elif len(nodes) > 1:

        if method is None:
            method = "union"

        if edge_method is None:
            method = "union"

        # Generate name for new node
        if node_id is None:
            node_id = "_".join([str(n) for n in sorted(nodes)])
            if node_id in graph.nodes():
                node_id = unique_node_id(graph, node_id)
        elif node_id in graph.nodes() and (node_id not in nodes):
            raise GraphError(
                "New name for merged node is not valid: "
                "node with name '%s' already exists!" % node_id
            )

        degrees = [graph.degree(n) for n in nodes]
        invariant_node_index = np.argmax(degrees)
        invariant_node = nodes[invariant_node_index]

        # Merge data attached to node according to the method specified
        # restore proper connectivity
        if method == "union":
            attrs_acc = {}
        elif method == "intersection":
            attrs_acc = deepcopy(graph.node[invariant_node])
        else:
            raise ReGraphError("Merging method '%s' is not defined!" % method)

        if graph.is_directed():
            sucs_acc = dict()
            preds_acc = dict()
            invariant_successors = graph.successors(invariant_node)
            invariant_predecessors = graph.predecessors(invariant_node)

            for s in invariant_successors:
                sucs_acc[s] = get_edge(graph, invariant_node, s)

            for p in invariant_predecessors:
                preds_acc[p] = get_edge(graph, p, invariant_node)

            other_nodes = [n for i, n in enumerate(nodes) if i != invariant_node_index]
            for n in other_nodes:
                attrs_acc = merge_attributes(attrs_acc, graph.node[n])

                sucs = graph.successors(n)
                preds = graph.predecessors(n)

                for s in sucs:
                    if s in sucs_acc.keys():
                        sucs_acc[s] = merge_attributes(
                            sucs_acc[s], get_edge(graph, n, s))
                    else:
                        sucs_acc[s] = get_edge(graph, n, s)

                for p in preds:
                    if p in preds_acc.keys():
                        preds_acc[p] = merge_attributes(
                            preds_acc[p], get_edge(graph, p, n))
                    else:
                        preds_acc[p] = get_edge(graph, p, n)

            graph.node[invariant_node] = attrs_acc

            loop_acc = dict()
            for s, attrs in sucs_acc.items():
                if s in nodes:
                    loop_acc = merge_attributes(loop_acc, attrs)
                else:
                    if (invariant_node, s) in graph.edges():
                        set_edge(graph, invariant_node, s, attrs)
                    else:
                        add_edge(graph, invariant_node, s, attrs)
            for p, attrs in preds_acc.items():
                if p in nodes:
                    loop_acc = merge_attributes(loop_acc, attrs)
                else:
                    if (p, invariant_node) in graph.edges():
                        set_edge(graph, p, invariant_node, attrs)
                    else:
                        add_edge(graph, p, invariant_node, attrs)

            if (invariant_node, invariant_node) in graph.edges():
                set_edge(graph, invariant_node, invariant_node, loop_acc)
            else:
                add_edge(graph, invariant_node, invariant_node, loop_acc)
        else:
            neighbours_acc = dict()
            invariant_neighbours = graph.neighbors(invariant_node)

            for n in invariant_neighbours:
                neighbours_acc[n] = get_edge(graph, invariant_node, n)

            other_nodes = [n for i, n in enumerate(nodes) if i != invariant_node_index]
            for n in other_nodes:
                attrs_acc = merge_attributes(attrs_acc, graph.node[n])

                neighbors = graph.neighbors(n)

                for s in neighbors:
                    if s in neighbours_acc.keys():
                        neighbours_acc[s] = merge_attributes(
                            neighbours_acc[s], get_edge(graph, n, s))
                    else:
                        neighbours_acc[s] = get_edge(graph, n, s)

            graph.node[invariant_node] = attrs_acc

            loop_acc = dict()
            for s, attrs in neighbours_acc.items():
                if s in nodes:
                    loop_acc = merge_attributes(loop_acc, attrs)
                else:
                    if exists_edge(graph, invariant_node, s):
                        set_edge(graph, invariant_node, s, attrs)
                    else:
                        add_edge(graph, invariant_node, s, attrs)

            if exists_edge(graph, invariant_node, invariant_node):
                set_edge(graph, invariant_node, invariant_node, loop_acc)
            else:
                add_edge(graph, invariant_node, invariant_node, loop_acc)

        for n in other_nodes:
            graph.remove_node(n)

        relabel_node(graph, invariant_node, node_id)

    else:
        raise ReGraphError("Cannot merge an empty set of nodes!")
Ejemplo n.º 10
0
def pushout(a, b, c, a_b, a_c, inplace=False):
    """Find the pushour of the span b <- a -> c."""
    check_homomorphism(a, b, a_b)
    check_homomorphism(a, c, a_c)

    if inplace is True:
        d = b
    else:
        d = copy.deepcopy(b)

    b_d = id_of(b.nodes())
    c_d = dict()

    # Add/merge nodes
    for c_n in c.nodes():
        a_keys = keys_by_value(a_c, c_n)
        # Add nodes
        if len(a_keys) == 0:
            add_node(d, c_n, c.node[c_n])
            c_d[c_n] = c_n
        # Keep nodes
        elif len(a_keys) == 1:
            c_d[a_c[a_keys[0]]] = a_b[a_keys[0]]
        # Merge nodes
        else:
            nodes_to_merge = []
            for k in a_keys:
                nodes_to_merge.append(a_b[k])
            new_name = merge_nodes(d, nodes_to_merge)
            c_d[c_n] = new_name
            for node in nodes_to_merge:
                b_d[node] = new_name

    # Add edges
    for (n1, n2) in c.edges():
        if b.is_directed():
            if (c_d[n1], c_d[n2]) not in d.edges():
                add_edge(
                    d, c_d[n1], c_d[n2],
                    get_edge(c, n1, n2))
        else:
            if (c_d[n1], c_d[n2]) not in d.edges() and\
               (c_d[n2], c_d[n1]) not in d.edges():
                add_edge(
                    d, c_d[n1], c_d[n2],
                    get_edge(c, n1, n2)
                )

    # Add node attrs
    for c_n in c.nodes():
        a_keys = keys_by_value(a_c, c_n)
        # Add attributes to the nodes which stayed invariant
        if len(a_keys) == 1:
            attrs_to_add = dict_sub(
                c.node[c_n],
                a.node[a_keys[0]]
            )
            add_node_attrs(d, c_d[c_n], attrs_to_add)
        # Add attributes to the nodes which were merged
        elif len(a_keys) > 1:
            merged_attrs = {}
            for k in a_keys:
                merged_attrs = merge_attributes(
                    merged_attrs,
                    a.node[k]
                )
            attrs_to_add = dict_sub(c.node[c_n], merged_attrs)
            add_node_attrs(d, c_d[c_n], attrs_to_add)

    # Add edge attrs
    for (n1, n2) in c.edges():
        d_n1 = c_d[n1]
        d_n2 = c_d[n2]
        if d.is_directed():
            attrs_to_add = dict_sub(
                get_edge(c, n1, n2),
                get_edge(d, d_n1, d_n2)
            )
            add_edge_attrs(
                d, c_d[n1], c_d[n2],
                attrs_to_add
            )
        else:
            attrs_to_add = dict_sub(
                get_edge(c, n1, n2),
                get_edge(d, d_n1, d_n2)
            )
            add_edge_attrs(
                d, c_d[n1], c_d[n2],
                attrs_to_add
            )
    return (d, b_d, c_d)
Ejemplo n.º 11
0
def merge_nodes(graph, nodes, node_id=None, method="union", edge_method="union"):
    """Merge a list of nodes.

    Parameters
    ----------

    graph : nx.(Di)Graph
    nodes : iterable
        Collection of node id's to merge.
    node_id : hashable, optional
        Id of a new node corresponding to the result of merge.
    method : optional
        Method of node attributes merge: if `"union"` the resulting node
        will contain the union of all attributes of the merged nodes,
        if `"intersection"`, the resulting node will contain their
        intersection. Default value is `"union"`.
    edge_method : optional
        Method of edge attributes merge: if `"union"` the edges that were
        merged will contain the union of all attributes,
        if `"intersection"` -- their ntersection. Default value is `"union"`.

    Returns
    -------
    node_id : hashable
        Id of a new node corresponding to the result of merge.

    Raises
    ------
    ReGraphError
        If unknown merging method is provided
    GraphError
        If some nodes from `nodes` do not exist in the graph.

    Examples
    --------

    >>> g = nx.DiGraph()
    >>> add_nodes_from(g, [(1, {"a": 1, "b": 1}), 2, (3, {"a": 3, "c": 3})])
    >>> add_edges_from(g, [(1, 3), (1, 2), (2, 3)])
    >>> merge_nodes(g, [1, 3], "merged_node")
    >>> g.nodes()
    ["merged_node", 2]
    >>> g.edges()
    [("merged_node", "merged_node"), ("merged_node", 2), (2, "merged_node")]
    >>> g.node["merged_node"]
    {"a": {1, 3}, "b": {1}, "c": {3}}

    """
    if len(nodes) == 1:
        if node_id is not None:
            relabel_node(graph, nodes[0], node_id)

    elif len(nodes) > 1:

        if method is None:
            method = "union"

        if edge_method is None:
            method = "union"

        # Generate name for new node
        if node_id is None:
            node_id = "_".join([str(n) for n in nodes])
        elif node_id in graph.nodes() and (node_id not in nodes):
            raise GraphError(
                "New name for merged node is not valid: "
                "node with name '%s' already exists!" % node_id
            )

        # Merge data attached to node according to the method specified
        # restore proper connectivity
        if method == "union":
            attr_accumulator = {}
        elif method == "intersection":
            attr_accumulator = deepcopy(graph.node[nodes[0]])
        else:
            raise ReGraphError("Merging method '%s' is not defined!" % method)

        self_loop = False
        self_loop_attrs = {}

        if graph.is_directed():
            source_nodes = set()
            target_nodes = set()

            source_dict = {}
            target_dict = {}
        else:
            neighbors = set()
            neighbors_dict = {}
        all_neighbors = set()

        for node in nodes:
            all_neighbors |= set(graph.__getitem__(node).keys())
            attr_accumulator = merge_attributes(
                attr_accumulator, graph.node[node], method)

            if graph.is_directed():
                in_edges = graph.in_edges(node)
                out_edges = graph.out_edges(node)

                # manage self loops
                for s, t in in_edges:
                    if s in nodes:
                        self_loop = True
                        if len(self_loop_attrs) == 0:
                            self_loop_attrs = graph.edge[s][t]
                        else:
                            self_loop_attrs = merge_attributes(
                                self_loop_attrs,
                                graph.edge[s][t],
                                edge_method)

                for s, t in out_edges:
                    if t in nodes:
                        self_loop = True
                        if len(self_loop_attrs) == 0:
                            self_loop_attrs = graph.edge[s][t]
                        else:
                            self_loop_attrs = merge_attributes(
                                self_loop_attrs,
                                graph.edge[s][t],
                                edge_method)

                source_nodes.update(
                    [n if n not in nodes else node_id
                     for n, _ in in_edges])
                target_nodes.update(
                    [n if n not in nodes else node_id
                     for _, n in out_edges])

                for edge in in_edges:
                    if not edge[0] in source_dict.keys():
                        attrs = graph.edge[edge[0]][edge[1]]
                        source_dict.update({edge[0]: attrs})
                    else:
                        attrs = merge_attributes(
                            source_dict[edge[0]],
                            graph.edge[edge[0]][edge[1]],
                            edge_method)
                        source_dict.update({edge[0]: attrs})

                for edge in out_edges:
                    if not edge[1] in target_dict.keys():
                        attrs = graph.edge[edge[0]][edge[1]]
                        target_dict.update({edge[1]: attrs})
                    else:
                        attrs = merge_attributes(
                            target_dict[edge[1]],
                            graph.edge[edge[0]][edge[1]],
                            edge_method)
                        target_dict.update({edge[1]: attrs})
            else:
                for n in graph.neighbors(node):
                    if n in nodes:
                        self_loop = True
                        if len(self_loop_attrs) == 0:
                            self_loop_attrs = graph.edge[n][node]
                        else:
                            self_loop_attrs = merge_attributes(
                                self_loop_attrs,
                                graph.edge[n][node],
                                edge_method)

                neighbors.update(
                    [n for n in graph.neighbors(node) if n not in nodes])
                for n in graph.neighbors(node):
                    if n not in nodes:
                        if n not in neighbors_dict.keys():
                            attrs = graph.edge[n][node]
                            neighbors_dict.update({n: attrs})
                        else:
                            attrs = merge_attributes(
                                neighbors_dict[n],
                                graph.edge[n][node],
                                edge_method)
                            neighbors_dict.update({n: attrs})

            graph.remove_node(node)
            all_neighbors -= {node}

        add_node(graph, node_id, attr_accumulator)
        all_neighbors.add(node_id)

        if graph.is_directed():
            if self_loop:
                add_edges_from(graph, [(node_id, node_id)])
                graph.edge[node_id][node_id] = self_loop_attrs
            for n in source_nodes:
                if not exists_edge(graph, n, node_id):
                    add_edge(graph, n, node_id)
            for n in target_nodes:
                if not exists_edge(graph, node_id, n):
                    add_edge(graph, node_id, n)

            # Attach accumulated attributes to edges
            for node, attrs in source_dict.items():
                if node not in nodes:
                    graph.edge[node][node_id] = attrs
            for node, attrs in target_dict.items():
                if node not in nodes:
                    graph.edge[node_id][node] = attrs
        else:
            if self_loop:
                add_edges_from(graph, [(node_id, node_id)])
                graph.edge[node_id][node_id] = self_loop_attrs

            add_edges_from(graph, [(n, node_id) for n in neighbors])

            # Attach accumulated attributes to edges
            for node, attrs in neighbors_dict.items():
                if node not in nodes:
                    graph.edge[node][node_id] = attrs
                    graph.edge[node_id][node] = attrs

        return node_id
Ejemplo n.º 12
0
def pushout(a, b, c, a_b, a_c, inplace=False):
    """Find the pushour of the span b <- a -> c."""
    def get_classes_to_merge():
        pass

    check_homomorphism(a, b, a_b)
    check_homomorphism(a, c, a_c)

    if inplace is True:
        d = b
    else:
        d = copy.deepcopy(b)

    b_d = id_of(b.nodes())
    c_d = dict()

    # Add/merge nodes
    merged_nodes = dict()
    for c_n in c.nodes():
        a_keys = keys_by_value(a_c, c_n)
        # Add nodes
        if len(a_keys) == 0:
            if c_n not in d.nodes():
                new_name = c_n
            else:
                new_name = unique_node_id(d, c_n)
            add_node(d, new_name, c.node[c_n])
            c_d[c_n] = new_name
        # Keep nodes
        elif len(a_keys) == 1:
            c_d[a_c[a_keys[0]]] = a_b[a_keys[0]]
        # Merge nodes
        else:
            nodes_to_merge = set()
            # find the nodes that need to be merged
            for k in a_keys:
                nodes_to_merge.add(a_b[k])

            # find if exists already some merged node to
            # which the new node should be merged
            groups_to_remove = set()
            new_groups = set()
            merge_done = False
            for k in merged_nodes.keys():
                if nodes_to_merge.issubset(merged_nodes[k]):
                    merge_done = True
                else:
                    intersect_with_group = nodes_to_merge.intersection(
                        merged_nodes[k])
                    if len(intersect_with_group) > 0:
                        new_nodes_to_merge =\
                            nodes_to_merge.difference(merged_nodes[k])
                        if len(new_nodes_to_merge) > 0:
                            new_nodes_to_merge.add(k)
                            new_name = merge_nodes(d, new_nodes_to_merge)
                            merged_nodes[new_name] = merged_nodes[k].union(
                                nodes_to_merge)
                            groups_to_remove.add(k)
                            new_groups.add(new_name)

            if len(groups_to_remove) > 0:
                new_name = merge_nodes(d, new_groups)
                merged_nodes[new_name] = set()
                for g in new_groups:
                    merge_nodes[new_name] = merge_nodes[new_name].union(
                        merged_nodes[g])
                for group in groups_to_remove:
                    del merged_nodes[group]
            elif not merge_done:
                new_name = merge_nodes(d, nodes_to_merge)
                merged_nodes[new_name] = nodes_to_merge
            for node in nodes_to_merge:
                c_d[c_n] = new_name
                b_d[node] = new_name

    # Add edges
    for (n1, n2) in c.edges():
        if b.is_directed():
            if (c_d[n1], c_d[n2]) not in d.edges():
                add_edge(
                    d, c_d[n1], c_d[n2],
                    get_edge(c, n1, n2))
        else:
            if (c_d[n1], c_d[n2]) not in d.edges() and\
               (c_d[n2], c_d[n1]) not in d.edges():
                add_edge(
                    d, c_d[n1], c_d[n2],
                    get_edge(c, n1, n2)
                )

    # Add node attrs
    for c_n in c.nodes():
        a_keys = keys_by_value(a_c, c_n)
        # Add attributes to the nodes which stayed invariant
        if len(a_keys) == 1:
            attrs_to_add = dict_sub(
                c.node[c_n],
                a.node[a_keys[0]]
            )
            add_node_attrs(d, c_d[c_n], attrs_to_add)
        # Add attributes to the nodes which were merged
        elif len(a_keys) > 1:
            merged_attrs = {}
            for k in a_keys:
                merged_attrs = merge_attributes(
                    merged_attrs,
                    a.node[k]
                )
            attrs_to_add = dict_sub(c.node[c_n], merged_attrs)
            add_node_attrs(d, c_d[c_n], attrs_to_add)

    # Add edge attrs
    for (n1, n2) in c.edges():
        d_n1 = c_d[n1]
        d_n2 = c_d[n2]
        if d.is_directed():
            attrs_to_add = dict_sub(
                get_edge(c, n1, n2),
                get_edge(d, d_n1, d_n2)
            )
            add_edge_attrs(
                d, c_d[n1], c_d[n2],
                attrs_to_add
            )
        else:
            attrs_to_add = dict_sub(
                get_edge(c, n1, n2),
                get_edge(d, d_n1, d_n2)
            )
            add_edge_attrs(
                d, c_d[n1], c_d[n2],
                attrs_to_add
            )
    return (d, b_d, c_d)
Ejemplo n.º 13
0
def pushout(a, b, c, a_b, a_c, inplace=False):
    """Find the pushour of the span b <- a -> c."""
    check_homomorphism(a, b, a_b)
    check_homomorphism(a, c, a_c)

    if inplace is True:
        d = b
    else:
        d = copy.deepcopy(b)

    b_d = id_of(b.nodes())
    c_d = dict()

    # Add/merge nodes
    for c_n in c.nodes():
        a_keys = keys_by_value(a_c, c_n)
        # Add nodes
        if len(a_keys) == 0:
            add_node(d, c_n, c.node[c_n])
            c_d[c_n] = c_n
        # Keep nodes
        elif len(a_keys) == 1:
            c_d[a_c[a_keys[0]]] = a_b[a_keys[0]]
        # Merge nodes
        else:
            nodes_to_merge = []
            for k in a_keys:
                nodes_to_merge.append(a_b[k])
            new_name = merge_nodes(d, nodes_to_merge)
            c_d[c_n] = new_name
            for node in nodes_to_merge:
                b_d[node] = new_name

    # Add edges
    for (n1, n2) in c.edges():
        if b.is_directed():
            if (c_d[n1], c_d[n2]) not in d.edges():
                add_edge(d, c_d[n1], c_d[n2], get_edge(c, n1, n2))
        else:
            if (c_d[n1], c_d[n2]) not in d.edges() and\
               (c_d[n2], c_d[n1]) not in d.edges():
                add_edge(d, c_d[n1], c_d[n2], get_edge(c, n1, n2))

    # Add node attrs
    for c_n in c.nodes():
        a_keys = keys_by_value(a_c, c_n)
        # Add attributes to the nodes which stayed invariant
        if len(a_keys) == 1:
            attrs_to_add = dict_sub(c.node[c_n], a.node[a_keys[0]])
            add_node_attrs(d, c_d[c_n], attrs_to_add)
        # Add attributes to the nodes which were merged
        elif len(a_keys) > 1:
            merged_attrs = {}
            for k in a_keys:
                merged_attrs = merge_attributes(merged_attrs, a.node[k])
            attrs_to_add = dict_sub(c.node[c_n], merged_attrs)
            add_node_attrs(d, c_d[c_n], attrs_to_add)

    # Add edge attrs
    for (n1, n2) in c.edges():
        d_n1 = c_d[n1]
        d_n2 = c_d[n2]
        if d.is_directed():
            attrs_to_add = dict_sub(get_edge(c, n1, n2),
                                    get_edge(d, d_n1, d_n2))
            add_edge_attrs(d, c_d[n1], c_d[n2], attrs_to_add)
        else:
            attrs_to_add = dict_sub(get_edge(c, n1, n2),
                                    get_edge(d, d_n1, d_n2))
            add_edge_attrs(d, c_d[n1], c_d[n2], attrs_to_add)
    return (d, b_d, c_d)
Ejemplo n.º 14
0
def pushout(a, b, c, a_b, a_c, inplace=False):
    """Find the pushour of the span b <- a -> c."""
    check_homomorphism(a, b, a_b)
    check_homomorphism(a, c, a_c)

    if inplace is True:
        d = b
    else:
        d = copy.deepcopy(b)

    b_d = id_of(b.nodes())
    c_d = dict()

    # Add/merge nodes
    merged_nodes = dict()
    for c_n in c.nodes():
        a_keys = keys_by_value(a_c, c_n)
        # Add nodes
        if len(a_keys) == 0:
            if c_n not in d.nodes():
                new_name = c_n
            else:
                new_name = unique_node_id(d, c_n)
            add_node(d, new_name, c.node[c_n])

            c_d[c_n] = new_name
        # Keep nodes
        elif len(a_keys) == 1:
            c_d[a_c[a_keys[0]]] = a_b[a_keys[0]]
        # Merge nodes
        else:
            nodes_to_merge = set()
            # find the nodes that need to be merged
            for k in a_keys:
                nodes_to_merge.add(a_b[k])

            # find if exists already some merged node to
            # which the new node should be merged
            groups_to_remove = set()
            new_groups = set()
            merge_done = False
            for k in merged_nodes.keys():
                if nodes_to_merge.issubset(merged_nodes[k]):
                    merge_done = True
                else:
                    intersect_with_group = nodes_to_merge.intersection(
                        merged_nodes[k])
                    if len(intersect_with_group) > 0:
                        new_nodes_to_merge =\
                            nodes_to_merge.difference(merged_nodes[k])
                        if len(new_nodes_to_merge) > 0:
                            new_nodes_to_merge.add(k)
                            new_name = merge_nodes(d, new_nodes_to_merge)
                            merged_nodes[new_name] = merged_nodes[k].union(
                                nodes_to_merge)
                            groups_to_remove.add(k)
                            new_groups.add(new_name)

            if len(groups_to_remove) > 0:
                new_name = merge_nodes(d, new_groups)
                merged_nodes[new_name] = set()
                for g in new_groups:
                    merge_nodes[new_name] = merge_nodes[new_name].union(
                        merged_nodes[g])
                for group in groups_to_remove:
                    del merged_nodes[group]
            elif not merge_done:
                new_name = merge_nodes(d, nodes_to_merge)
                merged_nodes[new_name] = nodes_to_merge
            for node in nodes_to_merge:
                c_d[c_n] = new_name
                b_d[node] = new_name

    # Add edges
    for (n1, n2) in c.edges():
        if b.is_directed():
            if (c_d[n1], c_d[n2]) not in d.edges():
                add_edge(d, c_d[n1], c_d[n2], get_edge(c, n1, n2))
        else:
            if (c_d[n1], c_d[n2]) not in d.edges() and\
               (c_d[n2], c_d[n1]) not in d.edges():
                add_edge(d, c_d[n1], c_d[n2], get_edge(c, n1, n2))

    # Add node attrs
    for c_n in c.nodes():
        a_keys = keys_by_value(a_c, c_n)
        # Add attributes to the nodes which stayed invariant
        if len(a_keys) == 1:
            attrs_to_add = dict_sub(c.node[c_n], a.node[a_keys[0]])
            add_node_attrs(d, c_d[c_n], attrs_to_add)
        # Add attributes to the nodes which were merged
        elif len(a_keys) > 1:
            merged_attrs = {}
            for k in a_keys:
                merged_attrs = merge_attributes(merged_attrs, a.node[k])
            attrs_to_add = dict_sub(c.node[c_n], merged_attrs)
            add_node_attrs(d, c_d[c_n], attrs_to_add)

    # Add edge attrs
    for (n1, n2) in c.edges():
        d_n1 = c_d[n1]
        d_n2 = c_d[n2]
        if d.is_directed():
            attrs_to_add = dict_sub(get_edge(c, n1, n2),
                                    get_edge(d, d_n1, d_n2))
            add_edge_attrs(d, c_d[n1], c_d[n2], attrs_to_add)
        else:
            attrs_to_add = dict_sub(get_edge(c, n1, n2),
                                    get_edge(d, d_n1, d_n2))
            add_edge_attrs(d, c_d[n1], c_d[n2], attrs_to_add)
    return (d, b_d, c_d)