def test_helpers(): g = Graph() cg = Constant(g) assert is_constant_graph(cg) one = Constant(1) assert not is_constant_graph(one) assert is_constant(one) a = Apply([cg, one], g) assert is_apply(a) p = Parameter(g) assert is_parameter(p)
def test_dfs_variants(): def f(x): z = x * x def g(y): return y + z w = z + 3 q = g(w) return q graph = parse(f) inner_graph_ct, = [x for x in dfs(graph.return_) if is_constant_graph(x)] inner_graph = inner_graph_ct.value inner_ret = inner_graph.return_ deep = _name_nodes(_dfs(inner_ret, succ_deep)) assert deep == set('. return add y z mul x'.split()) deeper = _name_nodes(_dfs(inner_ret, succ_deeper)) assert deeper == set('. return add y z mul x w 3 q g'.split()) _bound_fv = freevars_boundary(inner_graph, True) bound_fv = _name_nodes(_dfs(inner_ret, succ_deeper, _bound_fv)) assert bound_fv == set('. return add y z'.split()) _no_fv = freevars_boundary(inner_graph, False) no_fv = _name_nodes(_dfs(inner_ret, succ_deeper, _no_fv)) assert no_fv == set('. return add y'.split()) _excl_root = exclude_from_set([inner_ret]) excl_root = _name_nodes(_dfs(inner_ret, succ_deeper, _excl_root)) assert excl_root == set()
def name(node): if is_constant_graph(node): return node.value.debug.name or '.' elif isinstance(node, Constant): return str(node.value) else: return node.debug.name or '.'
def label(self, node, force=None, fn_label=None): """Label a node.""" if isinstance(node, DebugInfo): return self.name(node, True if force is None else force) elif isinstance(node, Graph): return self.name(node.debug, True if force is None else force) elif is_constant_graph(node): return self.name(node.value.debug, True if force is None else force) elif is_constant(node): v = node.value if isinstance(v, (int, float, str)): return repr(v) elif isinstance(v, Primitive): return v.name else: class_name = v.__class__.__name__ return f'{self.label(node.debug, True)}:{class_name}' elif is_parameter(node): return self.label(node.debug, True) else: lbl = '' if self.function_in_node: if fn_label is None: fn_label = self.const_fn(node) if fn_label: lbl = fn_label name = self.name(node, force) if name: if lbl: lbl += f'→{name}' else: lbl = name return lbl or '·'
def graphs_used(self) -> Dict[Graph, Set[Graph]]: """Map each graph to the set of graphs it uses. For each graph, this is the set of graphs that it refers to directly. """ coverage = self.coverage() return { g: { node.value for node in dfs(g.return_, succ_incoming, freevars_boundary(g)) if is_constant_graph(node) } for g in coverage }
def process_node_generic(self, node, g, cl): """Create node and edges for a node.""" lbl = self.label(node) self.cynode(id=node, label=lbl, parent=g, classes=cl) fn = node.inputs[0] if node.inputs else None if fn and is_constant_graph(fn): self.graphs.add(fn.value) edges = [] if fn and not (is_constant(fn) and self.function_in_node): edges.append((node, 'F', fn)) edges += [(node, i + 1, inp) for i, inp in enumerate(node.inputs[1:]) or []] self.process_edges(edges)
def free_variables_total(self) -> Dict[Graph, Set[ANFNode]]: """Map each graph to its free variables. This differs from `free_variables_direct` in that it also includes free variables needed by children graphs. Furthermore, graph Constants may figure as free variables. """ parents = self.parents() fvs: Dict[Graph, Set[ANFNode]] = defaultdict(set) for node in dfs(self.root.return_, succ_deeper): for inp in node.inputs: if is_constant_graph(inp): owner = parents[inp.value] else: owner = inp.graph if owner is None: continue g = node.graph while g is not owner: fvs[g].add(inp) g = parents[g] return fvs
def coverage(self) -> Iterable[Graph]: """Return a collection of graphs accessible from the root.""" root: ANFNode = Constant(self.root) nodes = dfs(root, succ_deeper) return set(node.value if is_constant_graph(node) else node.graph for node in nodes) - {None}
def follow(self, node): """Add this node's graph if follow_references is True.""" if is_constant_graph(node) and self.follow_references: self.graphs.add(node.value)