def pushout_from_partial_mapping(b, c, b_c, b_typings, c_typings): """typings are dict {id_of_typing_graph:mapping}""" # a = b.subgraph(b_c.keys()) a = nx.DiGraph() a.add_nodes_from(b_c.keys()) a_b = id_of(a) a_c = b_c (d, b_d, c_d) = pushout(a, b, c, a_b, a_c) d_typings = typing_of_pushout(b, c, d, b_d, c_d, b_typings, c_typings) return (d, d_typings)
def pushout_from_relation(g1, g2, relation, inplace=False): """Find the pushout from a relation.""" left_dict = left_relation_dict(relation) right_dict = right_relation_dict(relation) if inplace is True: g12 = g1 else: g12 = copy.deepcopy(g1) g1_g12 = id_of(g12.nodes()) g2_g12 = dict() for node in g1.nodes(): if node in left_dict.keys(): for g2_node in left_dict[node]: g2_g12[g2_node] = node for node in g2.nodes(): if node not in right_dict.keys(): node_id = node if node_id in g12.nodes(): node_id = unique_node_id(g12, node) add_node(g12, node_id, g2.node[node]) g2_g12[node] = node_id elif len(right_dict[node]) == 1: node_attrs_diff = dict_sub( g2.node[node], g1.node[list(right_dict[node])[0]]) add_node_attrs( g12, list(right_dict[node])[0], node_attrs_diff) elif len(right_dict[node]) > 1: new_name = merge_nodes(g12, right_dict[node]) for g1_node in right_dict[node]: g1_g12[g1_node] = new_name g2_g12[node] = new_name node_attrs_diff = dict_sub( g2.node[node], g12.node[new_name]) add_node_attrs(g12, new_name, node_attrs_diff) for u, v in g2.edges(): if (g2_g12[u], g2_g12[v]) not in g12.edges(): add_edge(g12, g2_g12[u], g2_g12[v], get_edge(g2, u, v)) else: edge_attrs_diff = dict_sub( g2.adj[u][v], g12.adj[g2_g12[u]][g2_g12[v]]) add_edge_attrs(g12, g2_g12[u], g2_g12[v], edge_attrs_diff) return (g12, g1_g12, g2_g12)
def pushout_from_relation(g1, g2, relation, inplace=False): """Find the pushout from a relation.""" left_dict = left_relation_dict(relation) right_dict = right_relation_dict(relation) if inplace is True: g12 = g1 else: g12 = copy.deepcopy(g1) g1_g12 = id_of(g12.nodes()) g2_g12 = dict() for node in g1.nodes(): if node in left_dict.keys(): for g2_node in left_dict[node]: g2_g12[g2_node] = node for node in g2.nodes(): if node not in right_dict.keys(): node_id = node if node_id in g12.nodes(): node_id = g12.generate_new_node_id(g12, node) g12.add_node(node_id, g2.get_node(node)) g2_g12[node] = node_id elif len(right_dict[node]) == 1: node_attrs_diff = dict_sub(g2.get_node(node), g1.get_node(list(right_dict[node])[0])) g12.add_node_attrs(list(right_dict[node])[0], node_attrs_diff) elif len(right_dict[node]) > 1: new_name = g12.merge_nodes(right_dict[node]) for g1_node in right_dict[node]: g1_g12[g1_node] = new_name g2_g12[node] = new_name node_attrs_diff = dict_sub(g2.get_node(node), g12.get_node(new_name)) g12.add_node_attrs(new_name, node_attrs_diff) for u, v in g2.edges(): if (g2_g12[u], g2_g12[v]) not in g12.edges(): g12.add_edge(g2_g12[u], g2_g12[v], g2.get_edge(u, v)) else: edge_attrs_diff = dict_sub(g2.get_edge(u, v), g12.get_edge(g2_g12[u], g2_g12[v])) g12.add_edge_attrs(g2_g12[u], g2_g12[v], edge_attrs_diff) return (g12, g1_g12, g2_g12)
def pushout_from_relation(g1, g2, relation, inplace=False): """Find the pushout from a relation.""" left_dict = left_relation_dict(relation) right_dict = right_relation_dict(relation) if inplace is True: g12 = g1 else: g12 = copy.deepcopy(g1) g1_g12 = id_of(g12.nodes()) g2_g12 = dict() for node in g1.nodes(): if node in left_dict.keys(): for g2_node in left_dict[node]: g2_g12[g2_node] = node for node in g2.nodes(): if node not in right_dict.keys(): add_node(g12, node, g2.node[node]) g2_g12[node] = node elif len(right_dict[node]) == 1: node_attrs_diff = dict_sub(g2.node[node], g1.node[list(right_dict[node])[0]]) add_node_attrs(g12, list(right_dict[node])[0], node_attrs_diff) elif len(right_dict[node]) > 1: new_name = merge_nodes(g12, right_dict[node]) for g1_node in right_dict[node]: g1_g12[g1_node] = new_name g2_g12[node] = new_name node_attrs_diff = dict_sub(g2.node[node], g12.node[new_name]) add_node_attrs(g12, new_name, node_attrs_diff) for u, v in g2.edges(): if (g2_g12[u], g2_g12[v]) not in g12.edges(): add_edge(g12, g2_g12[u], g2_g12[v], get_edge(g2, u, v)) else: edge_attrs_diff = dict_sub(g2.edge[u][v], g12.edge[g2_g12[u]][g2_g12[v]]) add_edge_attrs(g12, g2_g12[u], g2_g12[v], edge_attrs_diff) return (g12, g1_g12, g2_g12)
def pullback_complement(a, b, d, a_b, b_d, inplace=False): """Find the final pullback complement from a->b->d. Makes changes to d inplace. """ check_homomorphism(a, b, a_b, total=True) check_homomorphism(b, d, b_d, total=True) if not is_monic(b_d): raise InvalidHomomorphism("Second homomorphism is not monic, " "cannot find final pullback complement!") if inplace is True: c = d else: c = NXGraph() c.add_nodes_from(d.nodes(data=True)) c.add_edges_from(d.edges(data=True)) a_c = dict() c_d = id_of(c.nodes()) # Remove/clone nodes for b_node in b.nodes(): a_keys = keys_by_value(a_b, b_node) # Remove nodes if len(a_keys) == 0: c.remove_node(b_d[b_node]) del c_d[b_d[b_node]] # Keep nodes elif len(a_keys) == 1: a_c[a_keys[0]] = b_d[b_node] # Clone nodes else: i = 1 for k in a_keys: if i == 1: a_c[k] = b_d[b_node] c_d[b_d[b_node]] = b_d[b_node] else: new_name = c.clone_node(b_d[b_node]) a_c[k] = new_name c_d[new_name] = b_d[b_node] i += 1 # Remove edges for (b_n1, b_n2) in b.edges(): a_keys_1 = keys_by_value(a_b, b_n1) a_keys_2 = keys_by_value(a_b, b_n2) if len(a_keys_1) > 0 and len(a_keys_2) > 0: for k1 in a_keys_1: for k2 in a_keys_2: if (k1, k2) not in a.edges() and\ (a_c[k1], a_c[k2]) in c.edges(): c.remove_edge(a_c[k1], a_c[k2]) # Remove node attrs for a_node in a.nodes(): attrs_to_remove = dict_sub(b.get_node(a_b[a_node]), a.get_node(a_node)) c.remove_node_attrs(a_c[a_node], attrs_to_remove) # removed_node_attrs[a_c[a_node]] = attrs_to_remove # Remove edge attrs for (n1, n2) in a.edges(): attrs_to_remove = dict_sub(b.get_edge(a_b[n1], a_b[n2]), a.get_edge(n1, n2)) c.remove_edge_attrs(a_c[n1], a_c[n2], attrs_to_remove) # removed_edge_attrs[(a_c[n1], a_c[n2])] = attrs_to_remove return (c, a_c, c_d)
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 pullback_complement(a, b, d, a_b, b_d, inplace=False): """Find the final pullback complement from a->b->d. Makes changes to d inplace. """ check_homomorphism(a, b, a_b, total=True) check_homomorphism(b, d, b_d, total=True) if not is_monic(b_d): raise InvalidHomomorphism( "Second homomorphism is not monic, " "cannot find final pullback complement!" ) if inplace is True: c = d else: c = copy.deepcopy(d) a_c = dict() c_d = id_of(c.nodes()) # Remove/clone nodes for b_node in b.nodes(): a_keys = keys_by_value(a_b, b_node) # Remove nodes if len(a_keys) == 0: remove_node(c, b_d[b_node]) del c_d[b_d[b_node]] # Keep nodes elif len(a_keys) == 1: a_c[a_keys[0]] = b_d[b_node] # Clone nodes else: i = 1 for k in a_keys: if i == 1: a_c[k] = b_d[b_node] c_d[b_d[b_node]] = b_d[b_node] else: new_name = clone_node(c, b_d[b_node]) a_c[k] = new_name c_d[new_name] = b_d[b_node] i += 1 # Remove edges for (b_n1, b_n2) in b.edges(): a_keys_1 = keys_by_value(a_b, b_n1) a_keys_2 = keys_by_value(a_b, b_n2) if len(a_keys_1) > 0 and len(a_keys_2) > 0: for k1 in a_keys_1: for k2 in a_keys_2: if d.is_directed(): if (k1, k2) not in a.edges() and\ (a_c[k1], a_c[k2]) in c.edges(): remove_edge(c, a_c[k1], a_c[k2]) else: if (k1, k2) not in a.edges() and\ (k2, k1) not in a.edges(): if (a_c[k1], a_c[k2]) in d.edges() or\ (a_c[k2], a_c[k1]) in d.edges(): remove_edge(c, a_c[k1], a_c[k2]) # Remove node attrs for a_node in a.nodes(): attrs_to_remove = dict_sub( b.node[a_b[a_node]], a.node[a_node] ) remove_node_attrs(c, a_c[a_node], attrs_to_remove) # removed_node_attrs[a_c[a_node]] = attrs_to_remove # Remove edge attrs for (n1, n2) in a.edges(): attrs_to_remove = dict_sub( get_edge(b, a_b[n1], a_b[n2]), get_edge(a, n1, n2) ) remove_edge_attrs(c, a_c[n1], a_c[n2], attrs_to_remove) # removed_edge_attrs[(a_c[n1], a_c[n2])] = attrs_to_remove return (c, a_c, 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 remove_conflict(hie, ag_id, mm_id, locus, suffix=None): """duplicates a locus in order to remove conflicts""" ag_gr = hie.node[ag_id].graph ag_mm = hie.get_typing(ag_id, mm_id) nuggets = [ nug for nug in tree.get_children_id_by_node(hie, ag_id, locus) if hie.node[nug].attrs["type"] == "nugget" ] # Do not merge nodes that are Not valid # As they are removed from the botom graph before the pushout not_valid = [locus] def valid_pullback_node(a, b, c, d, a_b, a_c, b_d, c_d, n): a_d = union_mappings(compose_homomorphisms(b_d, a_b), compose_homomorphisms(c_d, a_c)) return n not in a_d or a_d[n] not in not_valid (pp, pp_ag) = multi_pullback_pushout( ag_gr, [(hie.node[nug].graph, hie.get_typing(nug, ag_id)) for nug in nuggets], valid_pullback_node) adj_nodes = [suc for suc in ag_gr.successors(locus)] + [locus] lhs = ag_gr.subgraph(adj_nodes) new_pp = pp.subgraph(reverse_image(pp_ag, adj_nodes)) # add regions and agents that do not appear in any nuggets to the # preserved part, so we can remove edges from the locus to them to_add = { suc for suc in ag_gr.successors(locus) if ag_mm[suc] in ["region", "agent"] } - set(pp_ag.values()) for node in to_add: node_id = unique_node_id(new_pp, node) add_node(new_pp, node_id) pp_ag[node_id] = node newpp_lhs = restrict_mapping(new_pp.nodes(), pp_ag) # merge loci from preserved part that arr linked to the same other loci def linked_to(loc): """loc being a locus from new_pp, returns the ag loci linked to loc """ adj_acts = { pp_ag[act] for act in new_pp.successors(loc) if ag_mm[pp_ag[act]] not in ["region", "agent"] } return { other_loc for act in adj_acts for other_loc in ag_gr.predecessors(act) if other_loc != locus } # compute equivalence classes of loci loci = [pploc for pploc in new_pp if pp_ag[pploc] == locus] classes = [{pploc} for pploc in loci] partial_eq = [{loc1, loc2} for loc1 in loci for loc2 in loci if loc1 != loc2 and linked_to(loc1) & linked_to(loc2)] for eq in partial_eq: classes = merge_classes(eq, classes) eq_gr = nx.DiGraph() newpp_eq = {} for i, cl in enumerate(classes): eq_gr.add_node(i) for node in cl: newpp_eq[node] = i (new_pp, newpp_lhs) = pushout_from_partial_mapping(new_pp, eq_gr, newpp_eq, newpp_lhs, {}) lhs_ag = id_of(lhs) rhs = copy.deepcopy(new_pp) rule = Rule(new_pp, lhs, rhs, newpp_lhs) if suffix is None: apply_rule_on_parent_inplace(hie, ag_id, rule, lhs_ag) else: raise ValueError("TODO? rewrite not in place")
def unfold_locus(hie, ag_id, mm_id, locus, suffix=None): """duplicate a locus that is shared between agents""" ag_gr = hie.node[ag_id].graph ag_mm = hie.get_typing(ag_id, mm_id) nuggets = [ nug for nug in tree.get_children_id_by_node(hie, ag_id, locus) if hie.node[nug].attrs["type"] == "nugget" ] # Do not merge nodes that are Not valid # As they are removed from the botom graph before the pushout not_valid = [locus] + [ node for node in ag_gr[locus] if ag_mm[node] not in ["region", "agent"] ] def valid_pullback_node(a, b, c, d, a_b, a_c, b_d, c_d, n): a_d = union_mappings(compose_homomorphisms(b_d, a_b), compose_homomorphisms(c_d, a_c)) return n not in a_d or a_d[n] not in not_valid (pp, pp_ag) = multi_pullback_pushout( ag_gr, [(hie.node[nug].graph, hie.get_typing(nug, ag_id)) for nug in nuggets], valid_pullback_node) adj_nodes = [suc for suc in ag_gr.successors(locus)] + [locus] lhs = ag_gr.subgraph(adj_nodes) new_pp = pp.subgraph(reverse_image(pp_ag, adj_nodes)) # add regions and agents that do not appear in any nuggets to the # preserved part, so we can remove edges from the locus to them to_add = { suc for suc in ag_gr.successors(locus) if ag_mm[suc] in ["region", "agent"] } - set(pp_ag.values()) for node in to_add: node_id = unique_node_id(new_pp, node) add_node(new_pp, node_id) pp_ag[node_id] = node newpp_lhs = restrict_mapping(new_pp.nodes(), pp_ag) # merge loci that have a shared successor component def common_comp(loc1, loc2): comps1 = { c for c in new_pp.successors(loc1) if ag_mm[pp_ag[c]] in ["region", "agent"] } comps2 = { c for c in new_pp.successors(loc2) if ag_mm[pp_ag[c]] in ["region", "agent"] } return comps1 & comps2 # compute equivalence classes of loci loci = [pploc for pploc in new_pp if pp_ag[pploc] == locus] classes = [{pploc} for pploc in loci] partial_eq = [{loc1, loc2} for (loc1, loc2) in combinations(loci, 2) if loc1 != loc2 and common_comp(loc1, loc2)] for eq in partial_eq: classes = merge_classes(eq, classes) # compute equivalence classes of action nodes def equiv_acts(act1, act2): def equiv_loci(locs1, locs2): if len(locs1) != 1: raise ValueError( "should have exactly one locus next to action") if len(locs2) != 1: raise ValueError( "should have exactly one locus next to action") return any(set(locs1) | set(locs2) <= cl for cl in classes) return (pp_ag[act1] == pp_ag[act2] and equiv_loci( new_pp.predecessors(act1), new_pp.predecessors(act2))) actions = [ act for act in new_pp if ag_mm[pp_ag[act]] in ["is_bnd", "bnd", "is_free", "brk"] ] action_classes = [{act} for act in actions] for (act1, act2) in combinations(actions, 2): if equiv_acts(act1, act2): action_classes = merge_classes({act1, act2}, action_classes) eq_gr = nx.DiGraph() newpp_eq = {} for i, cl in enumerate(classes + action_classes): eq_gr.add_node(i) for node in cl: newpp_eq[node] = i (new_pp, newpp_lhs) = pushout_from_partial_mapping(new_pp, eq_gr, newpp_eq, newpp_lhs, {}) lhs_ag = id_of(lhs) rhs = copy.deepcopy(new_pp) rule = Rule(new_pp, lhs, rhs, newpp_lhs) if suffix is None: apply_rule_on_parent_inplace(hie, ag_id, rule, lhs_ag) else: raise ValueError("TODO? rewrite not in place")
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)