def parsed_tree_to_formula(parsed_tree): """Build a formula object from the result of parsing.""" if parsed_tree[0].name == "START" or parsed_tree[0].name == "r_expr": rep = parsed_tree_to_formula(parsed_tree[1]) elif parsed_tree[0].name == "r_mu": variable_name = parsed_tree[3][1] phi = parsed_tree_to_formula(parsed_tree[5]) rep = Mu(variable_name, phi) elif parsed_tree[0].name == "r_or": sub_formulas = parsed_tree[3::2] rep = Or([parsed_tree_to_formula(f) for f in sub_formulas]) elif parsed_tree[0].name == "r_var": variable_name = parsed_tree[3][1] rep = Var(variable_name) elif parsed_tree[0].name == "r_cnt": variable_name = parsed_tree[3][1] rep = Cnt(variable_name) elif parsed_tree[0].name == "r_next": relation_name = parsed_tree[2][1] sub_formula = parsed_tree_to_formula(parsed_tree[4]) rep = Geq(1, relation_name, sub_formula) elif parsed_tree[0].name == "r_geq": lower_bound = parsed_tree[2][1] relation_name = parsed_tree[4][1] sub_formula = parsed_tree_to_formula(parsed_tree[6]) rep = Geq(int(lower_bound), relation_name, sub_formula) elif parsed_tree[0].name == "r_not": sub_formula = parsed_tree_to_formula(parsed_tree[3]) rep = Not(sub_formula) else: raise ParsingError("Parsing of formula failed") return rep
def make_canonical_commands(g, commands, di=False): """ Takes commands and the graph it refers to and returns a list of canonical transformations that have the same behaviour. The canonical form of a transformation follows this pattern : DELETIONS (DELETE_NODE, DELETE_NODE_ATTRS, DELETE_EDGE, DELETE_EDGE_ATTRS) CLONING (CLONE) ADDING and MERGING (ADD_NODE, ADD_NODE_ATTRS, ADD_EDGE, ADD_EDGE_ATTRS, MERGE) """ res = [] # We do multiple steps of simplification, until we found a fixed-point aux = commands next_step = simplify_commands(commands, di) while next_step != aux: aux = next_step next_step = simplify_commands(aux, di) # We keep updated an environment with our nodes and our edges env_nodes = [n for n in g.nodes()] env_edges = [e for e in g.edges()] if not di: for e in g.edges(): if not (e[1], e[0]) in env_edges: env_edges.append((e[1], e[0])) # For each transformation we choose if we do it in this step or if we # keep it for later while next_step != '': command_strings = [c for c in next_step.splitlines() if len(c) > 0] actions = [] for command in command_strings: try: parsed = parser.parseString(command).asDict() actions.append(parsed) except: raise ParsingError("Cannot parse command '%s'" % command) next_step = '' # We have 3 strings for each line of the canonical pattern add_step = '' del_step = '' clone_step = '' # Added is the list of elements we will add at to our environment # at the end of the step, we add them at the end so they are not # taken into account in the current step added = [] cloned = [] # If a node is in clone_wait, every cloning operation on it will # be delayed to next step. Same for other lists clone_wait = [] merge_wait = [] del_wait = [] ad_wait = [] # If we can't add a node with name n in this step, we don't want # another node with the same name to be added before it protected_names = [] # For each action we update our lists and we chose what to do for i in range(len(actions)): action = actions[i] if action["keyword"] == "add_node": if action["node"] not in protected_names: add_step += command_strings[i] + "\n" added.append(action["node"]) elif action["keyword"] == "delete_node": if action["node"] in env_nodes and\ action["node"] not in del_wait: del_step += command_strings[i] + "\n" env_nodes.remove(action["node"]) else: next_step += command_strings[i] + "\n" ad_wait.append(action["node"]) elif action["keyword"] == "add_node_attrs": if action["node"] in env_nodes and\ action["node"] not in ad_wait: add_step += command_strings[i] + "\n" added.append(action["node"]) clone_wait.append(action["node"]) else: next_step += command_strings[i] + "\n" ad_wait.append(action["node"]) clone_wait.append(action["node"]) elif action["keyword"] == "delete_node_attrs": if action["node"] in env_nodes and\ action["node"] not in del_wait: del_step += command_strings[i] + "\n" else: next_step += command_strings[i] + "\n" clone_wait.append(action["node"]) ad_wait.append(action["node"]) elif action["keyword"] == "add_edge": e = (action["node_1"], action["node_2"]) if e[0] in env_nodes and\ e[1] in env_nodes and\ e[0] not in ad_wait and\ e[1] not in ad_wait: add_step += command_strings[i] + "\n" added.append(e) if not di: added.append((e[1], e[0])) clone_wait.append(action["node_1"]) clone_wait.append(action["node_2"]) else: next_step += command_strings[i] + "\n" clone_wait.append(action["node_1"]) clone_wait.append(action["node_2"]) merge_wait.append(action["node_1"]) merge_wait.append(action["node_2"]) elif action["keyword"] == "delete_edge": e = (action["node_1"], action["node_2"]) if (e in env_edges or (not di and (e[1], e[0]) in env_edges)) and\ e[0] not in del_wait and\ e[1] not in del_wait: is_cloned = False for l in cloned: if e[0] in l: next_step += command_strings[i] + "\n" clone_wait.append(action["node_1"]) clone_wait.append(action["node_2"]) merge_wait.append(action["node_1"]) merge_wait.append(action["node_2"]) is_cloned = True break if not is_cloned: del_step += command_strings[i] + "\n" clone_wait.append(action["node_1"]) clone_wait.append(action["node_2"]) env_edges.remove(e) if not di: env_edges.remove((e[1], e[0])) else: next_step += command_strings[i] + "\n" clone_wait.append(action["node_1"]) clone_wait.append(action["node_2"]) merge_wait.append(action["node_1"]) merge_wait.append(action["node_2"]) elif action["keyword"] == "add_edge_attrs": e = (action["node_1"], action["node_2"]) if (e in env_edges or (not di and (e[1], e[0]) in env_edges)) and\ e[0] not in ad_wait and\ e[1] not in ad_wait: add_step += command_strings[i] + "\n" added.append(e) if not di: added.append((e[1], e[0])) clone_wait.append(action["node_1"]) clone_wait.append(action["node_2"]) else: next_step += command_strings[i] + "\n" clone_wait.append(action["node_1"]) clone_wait.append(action["node_2"]) merge_wait.append(action["node_1"]) merge_wait.append(action["node_2"]) elif action["keyword"] == "delete_edge_attrs": e = (action["node_1"], action["node_2"]) if (e in env_edges or (not di and (e[1], e[0]) in env_edges)) and\ e[0] not in del_wait and\ e[1] not in del_wait: is_cloned = False for l in cloned: if e[0] in l: next_step += command_strings[i] + "\n" clone_wait.append(action["node_1"]) clone_wait.append(action["node_2"]) merge_wait.append(action["node_1"]) merge_wait.append(action["node_2"]) is_cloned = True elif e[1] in l: next_step += command_strings[i] + "\n" clone_wait.append(action["node_1"]) clone_wait.append(action["node_2"]) merge_wait.append(action["node_1"]) merge_wait.append(action["node_2"]) is_cloned = True if not is_cloned: del_step += command_strings[i] + "\n" clone_wait.append(action["node_1"]) clone_wait.append(action["node_2"]) else: next_step += command_strings[i] + "\n" clone_wait.append(action["node_1"]) clone_wait.append(action["node_2"]) merge_wait.append(action["node_1"]) merge_wait.append(action["node_2"]) elif action["keyword"] == "clone": node = action["node"] if "node_name" in action.keys(): new_node = action["node_name"] else: j = 1 new_node = str(node) + str(j) while new_node in env_nodes or new_node in added: j += 1 new_node = str(node) + str(j) if node in env_nodes and\ node not in clone_wait and\ new_node not in protected_names and\ fold_left(lambda e, acc: (e != node or (type(e) == tuple and e[1] != node and e[0] != node)) and acc, True, added): clone_step += command_strings[i] + "\n" added.append(new_node) del_wait.append(node) found = False for i in range(len(cloned)): if node in cloned[i]: cloned[i].append(new_node) found = True if not found: cloned.append([new_node, node]) to_add = [] for e in env_edges: if e[0] == node: to_add.append((new_node, e[1])) elif e[1] == node: to_add.append((e[0], new_node)) for e in added: if type(e) == tuple: if e[0] == node and\ e[1] != node: to_add.append((new_node, e[1])) elif e[1] == node and e[0] != node: to_add.append((e[0], new_node)) for e in to_add: added.append(e) else: next_step += command_strings[i] + "\n" del_wait.append(node) merge_wait.append(node) ad_wait.append(node) protected_names.append(new_node) elif action["keyword"] == "merge": if "node_name" in actions[i].keys(): node_name = actions[i]["node_name"] else: node_name = "_".join(actions[i]["nodes"]) if fold_left(lambda n, acc: (n in env_nodes and n not in merge_wait) and acc, True, action["nodes"]) and\ node_name not in protected_names: add_step += command_strings[i] + "\n" added.append(node_name) clone_wait.append(node_name) rem_el = [] for e in env_edges: if e[0] in action["nodes"] and\ e[1] in action["nodes"]: if not e in rem_el: rem_el.append(e) if e[0] in action["nodes"]: if not e in rem_el: rem_el.append(e) if e[1] not in action["nodes"]: added.append((node_name, e[1])) elif e[1] in action["nodes"]: if not e in rem_el: rem_el.append(e) if e[0] not in action["nodes"]: added.append((e[0], node_name)) for e in rem_el: while e in env_edges: env_edges.remove(e) if not di: env_edges.remove((e[1], e[0])) rem_el = [] for e in added: if type(e) == tuple: if e[0] in action["nodes"] and\ e[1] in action["nodes"]: if e not in rem_el: rem_el.append(e) if e[0] in action["nodes"]: if e not in rem_el: rem_el.append(e) if e[1] not in action["nodes"]: added.append((node_name, e[1])) elif e[1] in action["nodes"]: if e not in rem_el: rem_el.append(e) if e[0] not in action["nodes"]: added.append((e[0], node_name)) for e in rem_el: while e in added: added.remove(e) if not di: added.remove((e[1], e[0])) else: next_step += command_strings[i] + "\n" protected_names.append(node_name) for el in added: if type(el) == tuple: env_edges.append(el) else: env_nodes.append(el) if del_step + clone_step + add_step == '': raise ReGraphError( "Can't find any new transformations and actions is non-empty :\n%s" % next_step) res.append(del_step + clone_step + add_step) return res
def simplify_commands(commands, di=False): """Simplify a list of graph transformation commands.""" command_strings = [c for c in commands.splitlines() if len(c) > 0] actions = [] for command in command_strings: try: parsed = parser.parseString(command).asDict() actions.append(parsed) except: raise ParsingError("Cannot parse command '%s'" % command) # We keep updated a list of the element we added, the lines of # transformations that added them or added attributes to them # and the type of addition we did (node or edge) added = [] ad_index = [] ad_type = [] # We keep updated a list of the element we deleted and the lines of # transformation that deleted them or deleted attributes from them deleted = [] del_index = [] # We keep updated a list of the element we cloned and the line of # transformation that cloned them cloned = [] clone_index = [] # List of elements to remove at the end elements_to_remove = [] # For each line of command we change what to remove and what to keep # We update the lists at each step, the only operations that actually # do simplify the commands are the deletion of nodes and edges and the # merges. They try to find the all the operations they can remove # without changing the behaviour for i in range(len(actions)): action = actions[i] if action["keyword"] == "add_node": added.append(action["node"]) ad_index.append([i]) ad_type.append("node") elif action["keyword"] == "delete_node": if action["node"] not in cloned: # If the node haven't been cloned before rem_el = [] for j in range(len(added)): el = added[j] if (type(el) == tuple and (el[0] == action["node"] or el[1] == action["node"])) or\ el == action["node"]: # If the node have been involved in an addition # we remove that addition since it has been # deleted now, if there are not more lines that # refers to the addition of that node, we can # remove the deletion of the node # Finding the node in added is not enough to # remove the deletion since it can be an # addition of an edge, we have to check if it # the node itself that we added if el == action["node"]: elements_to_remove.append(i) for k in ad_index[j]: elements_to_remove.append(k) rem_el.append(j) k = 0 for j in rem_el: del added[j - k] del ad_index[j - k] del ad_type[j - k] k += 1 rem_el = [] for j in range(len(deleted)): el = deleted[j] if (type(el) == tuple and (el[0] == action["node"] or el[1] == action["node"])) or\ el == action["node"]: # If the node have been involved in a deletion # we can remove that deletion since the deletion # of the node itself will delete what the deletion # would have deleted for k in del_index[j]: elements_to_remove.append(k) rem_el.append(j) k = 0 for j in rem_el: del deleted[j - k] del del_index[j - k] k += 1 else: # If the node have been cloned before, we can't delete the # transformations that happened before the cloning since # they affected the clones too. We do so by comparing the # line of the transformation we are looking at and the line # of the last cloning operation that happened rem_el = [] ind = max([ clone_index[i] for i in range(len(cloned)) if cloned[i] == action["node"] ]) for j in range(len(added)): el = added[j] if (type(el) == tuple and (el[0] == action["node"] or el[1] == action["node"])) or\ el == action["node"]: rem_ind = [] for k in ad_index[j]: if k > ind: elements_to_remove.append(k) rem_ind.append(k) if ad_index[j] == rem_ind: rem_el.append(j) else: for k in rem_ind: ad_index[j].remove(k) m = 0 for j in rem_el: del added[j - m] del ad_index[j - m] del ad_type[j - m] m += 1 rem_el = [] for j in range(len(deleted)): el = deleted[j] if (type(el) == tuple and (el[0] == action["node"] or el[1] == action["node"])) or\ el == action["node"]: rem_ind = [] for k in del_index[j]: if k > ind: elements_to_remove.append(k) rem_ind.append(k) if del_index[j] == rem_ind: rem_el.append(j) else: for k in rem_ind: del_index[j].remove(k) m = 0 for j in rem_el: del deleted[j - m] del del_index[j - m] m += 1 ind = clone_index.index(ind) del cloned[ind] del clone_index[ind] deleted.append(action["node"]) del_index.append([i]) elif action["keyword"] == "add_node_attrs": if action["node"] in added: j = added.index(action["node"]) ad_index[j].append(i) else: added.append(action["node"]) ad_index.append([i]) ad_type.append("node_attrs") elif action["keyword"] == "delete_node_attrs": if action["node"] in deleted: j = deleted.index(action["node"]) del_index[j].append(i) else: deleted.append(action["node"]) del_index.append([i]) elif action["keyword"] == "add_edge": e = (action["node_1"], action["node_2"]) added.append(e) ad_index.append([i]) ad_type.append("edge") elif action["keyword"] == "delete_edge": # It is the same idea as in the delete_node function, but with # a little bit more complexity since we have two nodes that # can possibly be cloned. # This time, finding the edge in the added list automatically # means we have to remove the deletion and the addition in the # case we didn't clone any of our nodes e = (action["node_1"], action["node_2"]) if e[0] not in cloned and e[1] not in cloned: rem_el = [] for j in range(len(added)): el = added[j] if type(el) == tuple and\ (el == e or (not di and el == (e[1], e[0]))): elements_to_remove.append(i) for k in ad_index[j]: elements_to_remove.append(k) rem_el.append(j) k = 0 for j in rem_el: del added[j - k] del ad_index[j - k] del ad_type[j - k] k += 1 rem_el = [] for j in range(len(deleted)): el = deleted[j] if type(el) == tuple and\ (el == e or (not di and el == (e[1], e[0]))): for k in del_index[j]: elements_to_remove.append(k) rem_el.append(j) k = 0 for j in rem_el: del deleted[j - k] del del_index[j - k] k += 1 else: # Same idea as before if one of the nodes have been cloned, # but we have to take the max of the line number of all the # cloning operation on node 0 and node 1 ind = 0 if e[0] in cloned: ind = max([ clone_index[i] for i in range(len(cloned)) if cloned[i] == e[0] ]) if e[1] in cloned: ind = max([ind] + [ clone_index[i] for i in range(len(cloned)) if cloned[i] == e[1] ]) ind = clone_index.index(ind) if e[0] in cloned: rem_el = [] for j in range(len(added)): el = added[j] if type(el) == tuple and\ (el == e or (not di and el == (e[1], e[0]))): rem_ind = [] for k in ad_index[j]: if k > clone_index[ind]: elements_to_remove.append(k) # We remove the delete_edge operation # iff the same edge have been added # after the last cloning operation if ad_type[j] == "edge": elements_to_remove.append(i) rem_ind.append(k) if ad_index[j] == rem_ind: rem_el.append(j) else: for k in rem_ind: ad_index[j].remove(k) m = 0 for j in rem_el: del added[j - m] del ad_index[j - m] del ad_type[j - m] m += 1 rem_el = [] for j in range(len(deleted)): el = deleted[j] if type(el) == tuple and\ (el == e or (not di and el == (e[1], e[0]))): rem_ind = [] for k in del_index[j]: if k > clone_index[ind]: elements_to_remove.append(k) rem_ind.append(k) if del_index[j] == rem_ind: rem_el.append(j) else: for k in rem_ind: del_index[j].remove(k) m = 0 for j in rem_el: del deleted[j - m] del del_index[j - m] m += 1 if e[1] in cloned: rem_el = [] for j in range(len(added)): el = added[j] if type(el) == tuple and\ (el == e or (not di and el == (e[1], e[0]))): rem_ind = [] for k in ad_index[j]: if k > clone_index[ind]: elements_to_remove.append(k) if ad_type[j] == "edge": elements_to_remove.append(i) rem_ind.append(k) if ad_index[j] == rem_ind: rem_el.append(j) else: for k in rem_ind: ad_index[j].remove(k) m = 0 for j in rem_el: del added[j - m] del ad_index[j - m] del ad_type[j - m] m += 1 rem_el = [] for j in range(len(deleted)): el = deleted[j] if type(el) == tuple and\ (el == e or (not di and el == (e[1], e[0]))): rem_ind = [] for k in del_index[j]: if k > clone_index[ind]: elements_to_remove.append(k) rem_ind.append(k) if del_index[j] == rem_ind: rem_el.append(j) else: for k in rem_ind: del_index[j].remove(k) m = 0 for j in rem_el: del deleted[j - m] del del_index[j - m] m += 1 deleted.append(e) del_index.append([i]) elif action["keyword"] == "add_edge_attrs": e = (action["node_1"], action["node_2"]) if e in added: j = added.index(e) ad_index[j].append(i) elif not di and (e[1], e[0]) in added: j = added.index((e[1], e[0])) ad_index[j].append(i) else: added.append(e) ad_index.append([i]) ad_type.append("edge_attrs") elif action["keyword"] == "delete_edge_attrs": e = (action["node_1"], action["node_2"]) if e in deleted: j = deleted.index(e) del_index[j].append(i) elif not di and (e[1], e[0]) in deleted: j = deleted.index((e[1], e[0])) del_index[j].append(i) else: deleted.append(e) del_index.append([i]) elif action["keyword"] == "clone": if "node_name" in action.keys(): added.append(action["node_name"]) ad_index.append([i]) ad_type.append("node") cloned.append(action["node"]) clone_index.append(i) elif action["keyword"] == "merge": if "node_name" in action.keys(): node_name = action["node_name"] else: node_name = "_".join(action["nodes"]) added.append(node_name) ad_index.append([i]) ad_type.append("node") return "\n".join([ command_strings[i] for i in range(len(actions)) if i not in elements_to_remove ])
def from_transform(cls, pattern, commands=None): """Initialize a rule from the transformation. On input takes a pattern which is used as `lhs` of the rule, as an optional argument transformation commands can be provided, by default the list of commands is empty and all `p`, `lhs` and `rhs` are initialized to be the same graph (pattern), later on when transformations are applied `p` and `rhs` are being updated. If list of commands is specified, these commands are simplified, transformed to the canonical order, and applied to `p`, `lhs` and `rhs`. Parameters ---------- pattern : networkx.(Di)Graph Pattern graph to initialize and the lhs of the rule. commands : str, optional Script containing transformation commands, which can be parsed by `regraph.parser.parse`. """ p = copy.deepcopy(pattern) lhs = copy.deepcopy(pattern) rhs = copy.deepcopy(pattern) p_lhs = dict([(n, n) for n in pattern.nodes()]) p_rhs = dict([(n, n) for n in pattern.nodes()]) rule = cls(p, lhs, rhs, p_lhs, p_rhs) # if the commands are provided, perform respecitive transformations if commands: # 1. make the commands canonical commands = make_canonical_commands(p, commands, p.is_directed()) # 2. apply the commands command_strings = [ c for b in commands if len(b) > 0 for c in b.splitlines() ] actions = [] for command in command_strings: try: parsed = parser.parseString(command).asDict() actions.append(parsed) except: raise ParsingError("Cannot parse command '%s'" % command) for action in actions: if action["keyword"] == "clone": node_name = None if "node_name" in action.keys(): node_name = action["node_name"] rule.clone_node(action["node"], node_name) elif action["keyword"] == "merge": node_name = None if "node_name" in action.keys(): node_name = action["node_name"] merged_node = rule.merge_node_list( action["nodes"], node_name) elif action["keyword"] == "add_node": name = None attrs = {} if "node" in action.keys(): name = action["node"] if "attributes" in action.keys(): attrs = action["attributes"] rule.add_node(name, attrs) elif action["keyword"] == "delete_node": rule.remove_node(action["node"]) elif action["keyword"] == "add_edge": attrs = {} if "attributes" in action.keys(): attrs = action["attributes"] rule.add_edge_rhs( action["node_1"], action["node_2"], attrs) elif action["keyword"] == "delete_edge": rule.remove_edge( action["node_1"], action["node_2"]) elif action["keyword"] == "add_node_attrs": rule.add_node_attrs( action["node"], action["attributes"]) elif action["keyword"] == "add_edge_attrs": rule.add_edge_attrs( action["node_1"], action["node_2"], action["attributes"]) elif action["keyword"] == "delete_node_attrs": rule.remove_node_attrs( action["node"], action["attributes"]) elif action["keyword"] == "delete_edge_attrs": rule.remove_edge_attrs( action["node_1"], action["node_2"], action["attributes"]) else: raise ParsingError("Unknown command %s" % action["keyword"]) return rule