def eq(g1, g2): return map_values(set, g1) == map_values(set, g2)
def restrict(self, inputs=(), outputs=()): ''' The minimal sub-block that computes 'outputs' from 'inputs'. 'inputs' and 'outputs' must be subsets of 'self.inputs' and 'self.outputs', respectively. ''' # TODO Elaborate docstring inputs = set(inputs) outputs = set(outputs) # TODO Restrict recursively # Look for results in the cache cache_key = (frozenset(inputs), frozenset(outputs)) if cache_key in self.__restrictions: return self.__restrictions[cache_key] if not (inputs or outputs): raise ValueError('Must provide inputs or outputs') if not inputs.issubset(self.inputs): raise ValueError('Unknown inputs: %s' % (inputs - self.inputs)) if not outputs.issubset(self.all_outputs): raise ValueError('Unknown outputs: %s' %(outputs-self.all_outputs)) # If we don't decompose, then we are already as restricted as possible if self.sub_blocks is None: return self # We use the mock constructors `In` and `Out` to separate input and # output names in the dep graph in order to avoid cyclic graphs (in # case input and output names overlap) in_, out = object(), object() # (singletons) In = lambda x: (x, in_) Out = lambda x: (x, out) def wrap_names(wrap): "Wrap names and leave everything else alone" def g(x): if isinstance(x, basestring): return wrap(x) else: return x return g g = self._dep_graph # Decorate input names with `In` and output names with `Out` so that # `g` isn't cyclic. (Whose responsibility is this?) g = g.__class__( map_keys(wrap_names(Out), map_values(lambda l: map(wrap_names(In), l), g.adj)) ) # Find the subgraph reachable from inputs, and then find its subgraph # reachable from outputs. (We could also flip the order.) if inputs: inputs = set(map(In, inputs)) g = reachable_graph(g.reverse(), *inputs).reverse() if outputs: outputs = set(map(Out, outputs)) g = reachable_graph(g, *outputs.intersection(g.nodes())) # Create a new block from the remaining sub-blocks (ordered input to # output, ignoring the variables at the ends) and give it our filename b = Block(node for node in reversed(topological_sort(g)) if isinstance(node, Block)) b.filename = self.filename # Cache result self.__restrictions[cache_key] = b return b