def _base(self, code, inputs, outputs, conditional_outputs, dep_graph):
        b = Block(code)

        # Compare inputs and outputs
        self.assertEqual(set(b.inputs), set(inputs))
        self.assertEqual(set(b.outputs), set(outputs))
        self.assertEqual(set(b.conditional_outputs), set(conditional_outputs))

        # Compare dependency graphs: since the real dep_graphs contain crazy
        # block objects, the given dep_graph specifies them by their index in
        # the sequence, e.g.
        #   for code
        #       'a=z; b=f(a,y)'
        #   the given dep_graph should be
        #       { '0':'z',  # Command 0 depends on input z
        #         '1':'0y', # Command 1 depends on command 0 and input y
        #         'a':'0',  # Output a depends on command 0
        #         'b':'1' } # Output b depends on command 1

        # Resolve indices to blocks
        def num_to_block(x):
            '''If x is number-like, lookup the xth block in b.sub_blocks;
            otherwise, leave x unchanged.'''
            try:
                return b.sub_blocks[int(x)]
            except ValueError:  # If int(x) fails
                return x
            except TypeError:  # In case b doesn't decompose
                return b

        dep_graph = graph.map(num_to_block, dep_graph)

        # Hand-make a trivial dep graph if 'b' doesn't have sub-blocks
        if b.sub_blocks is not None:
            b_dep_graph = b._dep_graph
        else:
            b_dep_graph = {}
            if b.inputs:
                b_dep_graph[b] = b.inputs
            for o in b.all_outputs:
                b_dep_graph[o] = set([b])

        # Compare dep_graphs
        self.assertEqual(
            map_values(set, b_dep_graph), map_values(set, dep_graph))
Example #2
0
    def _base(self, code, inputs, outputs, conditional_outputs,
              dep_graph):
        b = Block(code)

        # Compare inputs and outputs
        self.assertEqual(set(b.inputs), set(inputs))
        self.assertEqual(set(b.outputs), set(outputs))
        self.assertEqual(set(b.conditional_outputs), set(conditional_outputs))

        # Compare dependency graphs: since the real dep_graphs contain crazy
        # block objects, the given dep_graph specifies them by their index in
        # the sequence, e.g.
        #   for code
        #       'a=z; b=f(a,y)'
        #   the given dep_graph should be
        #       { '0':'z',  # Command 0 depends on input z
        #         '1':'0y', # Command 1 depends on command 0 and input y
        #         'a':'0',  # Output a depends on command 0
        #         'b':'1' } # Output b depends on command 1

        # Resolve indices to blocks
        def num_to_block(x):
            '''If x is number-like, lookup the xth block in b.sub_blocks;
            otherwise, leave x unchanged.'''
            try:
                return b.sub_blocks[int(x)]
            except ValueError: # If int(x) fails
                return x
            except TypeError: # In case b doesn't decompose
                return b
        dep_graph = graph.map(num_to_block, dep_graph)

        # Hand-make a trivial dep graph if 'b' doesn't have sub-blocks
        if b.sub_blocks is not None:
            b_dep_graph = b._dep_graph
        else:
            b_dep_graph = {}
            if b.inputs:
                b_dep_graph[b] = b.inputs
            for o in b.all_outputs:
                b_dep_graph[o] = set([b])

        # Compare dep_graphs
        self.assertEqual(map_values(set, b_dep_graph),
                         map_values(set, dep_graph))
Example #3
0
    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