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)
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)
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)
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])
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)
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
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!")
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!")
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)
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
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)
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)
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)