def test_get_inner_edges(self):
        self.assertEqual(len(_get_inner_edges(self.dep, ['b'], ['c'])), 6)
        self.assertEqual(set(_get_inner_edges(self.dep, ['b'], ['c'])),
                         set([('b[3]','A.b'),('A.d.z','B.b[4]'),('A.c[2]','B.a.x.y'),
                              ('B.c','C.a'),('B.d','C.b'),('C.c','c')]))

        dep, scope = _make_graph(comps=['A','B'],
                                 connections=[('A.c','B.a'),('A.d','B.b')],
                                 inputs=['a','b'],
                                 outputs=['c','d'])
        self.assertEqual(len(_get_inner_edges(dep, ['A.a'], ['B.c'])), 2)
        self.assertEqual(set(_get_inner_edges(dep, ['A.a'], ['B.c'])),
                         set([('A.c','B.a'),('A.d','B.b')]))
        
        dep, scope = _make_graph(comps=['A','B', 'C'],
                                 connections=[('A.c','B.a'),('A.d','B.b'),('B.d','C.a')],
                                 inputs=['a','b'],
                                 outputs=['c','d'])
        self.assertEqual(set(_get_inner_edges(dep, ['A.a'], ['C.c'])),
                         set([('A.c','B.a'),('A.d','B.b'),('B.d','C.a')]))

        # same output feeding two inputs
        dep, scope = _make_graph(comps=['A','B', 'C'],
                                 connections=[('A.d','B.a'),('A.d','B.b'),('B.d','C.a')],
                                 inputs=['a','b'],
                                 outputs=['c','d'])
        edges = _get_inner_edges(dep, ['A.a'], ['C.c'])
        self.assertEqual(set(edges), set([('A.d','B.a'),('A.d','B.b'),('B.d','C.a')]))
        edict = edges_to_dict(edges)
        self.assertEqual(len(edict), 2)
        self.assertEqual(set(edict['A.d']), set(['B.a','B.b']))
        self.assertEqual(edict['B.d'], ['C.a'])

        # loop
        dep, scope = _make_graph(comps=['A','B', 'C'],
                                 connections=[('A.d','B.a'),('B.d','C.a'),('C.d','A.a')],
                                 inputs=['a','b'],
                                 outputs=['c','d'])
        self.assertEqual(set(_get_inner_edges(dep, ['A.a'], ['C.d'])),
                         set([('A.d','B.a'),('B.d','C.a'),('C.d','A.a')]))
    def calc_derivatives(self,
                         first=False,
                         second=False,
                         savebase=True,
                         required_inputs=None,
                         required_outputs=None):
        """Calculate the derivatives for this non-differentiable block using
        Finite Difference."""
        # We don't do this in __init__ because some inputs and outputs
        # are added after creation (for nested driver support).
        if self.fd is None:
            from openmdao.main.derivatives import FiniteDifference
            self.fd = FiniteDifference(self)

        if hasattr(self.wflow, '_severed_edges'):
            self.wflow.sever_edges(self.wflow._severed_edges)

        try:
            # First, linearize about operating point.
            # Note: Only needed for differentiable islands, which are handled
            # with Fake Finite Difference.
            # Don't do this for full-model finite difference.
            if first and self.ffd_order > 0:
                for name in self.comps:

                    # TODO: I think the cache is blown away each time before
                    # this is called
                    if name in self.wflow._J_cache:
                        continue

                    comp = self.wflow.scope.get(name)

                    # Assemblies need some required inputs and outputs
                    # to calculate the Jacobians
                    if has_interface(comp, IAssembly):

                        # Need to know which assy bdry variables are
                        # required, and pass them in. Cache this once.
                        if name not in self.ffd_cache:
                            dgraph = self.wflow.scope._depgraph
                            inputs = [
                                dgraph.base_var(inp)
                                for inp in flatten_list_of_iters(self.inputs)
                            ]
                            outputs = [
                                dgraph.base_var(outp) for outp in self.outputs
                            ]
                            from openmdao.main.depgraph import _get_inner_edges
                            edges = _get_inner_edges(dgraph, inputs, outputs)

                            req_inputs = []
                            req_outputs = []
                            for inp in inputs:
                                comp_str, _, var_str = inp.partition('.')
                                if comp_str == name:
                                    req_inputs.append(var_str)

                            for inp in outputs:
                                comp_str, _, var_str = inp.partition('.')
                                if comp_str == name:
                                    req_outputs.append(var_str)

                            for edge in edges:
                                src, target = edge
                                comp_str, _, var_str = src.partition('.')
                                if comp_str == name:
                                    req_outputs.append(var_str)
                                comp_str, _, var_str = target.partition('.')
                                if comp_str == name:
                                    req_inputs.append(var_str)

                            self.ffd_cache[name] = (req_inputs, req_outputs)

                        req_inputs, req_outputs = self.ffd_cache[name]

                        comp.calc_derivatives(first,
                                              second,
                                              savebase=True,
                                              required_inputs=req_inputs,
                                              required_outputs=req_outputs)

                    # Comp list contains full graph, so don't double up on
                    # the subdrivers.
                    elif not has_interface(comp, IDriver):
                        comp.calc_derivatives(first, second, True)

            self.J = self.fd.calculate()
        finally:
            if hasattr(self.wflow, '_severed_edges'):
                self.wflow.unsever_edges()

        return self.J
    def calc_derivatives(self, first=False, second=False, savebase=True,
                         required_inputs=None, required_outputs=None):
        """Calculate the derivatives for this non-differentiable block using
        Finite Difference."""
        # We don't do this in __init__ because some inputs and outputs
        # are added after creation (for nested driver support).
        if self.fd is None:
            from openmdao.main.derivatives import FiniteDifference
            self.fd = FiniteDifference(self)

        if hasattr(self.wflow, '_severed_edges'):
            self.wflow.sever_edges(self.wflow._severed_edges)

        try:
            # First, linearize about operating point.
            # Note: Only needed for differentiable islands, which are handled
            # with Fake Finite Difference.
            # Don't do this for full-model finite difference.
            if first and self.ffd_order > 0:
                for name in self.comps:

                    # TODO: I think the cache is blown away each time before
                    # this is called
                    if name in self.wflow._J_cache:
                        continue

                    comp = self.wflow.scope.get(name)

                    # Assemblies need some required inputs and outputs
                    # to calculate the Jacobians
                    if has_interface(comp, IAssembly):

                        # Need to know which assy bdry variables are
                        # required, and pass them in. Cache this once.
                        if name not in self.ffd_cache:
                            dgraph = self.wflow.scope._depgraph
                            inputs = [dgraph.base_var(inp)
                                       for inp in flatten_list_of_iters(self.inputs)]
                            outputs = [dgraph.base_var(outp)
                                       for outp in self.outputs]
                            from openmdao.main.depgraph import _get_inner_edges
                            edges = _get_inner_edges(dgraph, inputs, outputs)

                            req_inputs = []
                            req_outputs = []
                            for inp in inputs:
                                comp_str, _, var_str = inp.partition('.')
                                if comp_str == name:
                                    req_inputs.append(var_str)

                            for inp in outputs:
                                comp_str, _, var_str = inp.partition('.')
                                if comp_str == name:
                                    req_outputs.append(var_str)

                            for edge in edges:
                                src, target = edge
                                comp_str, _, var_str = src.partition('.')
                                if comp_str == name:
                                    req_outputs.append(var_str)
                                comp_str, _, var_str = target.partition('.')
                                if comp_str == name:
                                    req_inputs.append(var_str)

                            self.ffd_cache[name] = (req_inputs, req_outputs)

                        req_inputs, req_outputs = self.ffd_cache[name]

                        comp.calc_derivatives(first, second, savebase=True,
                                              required_inputs=req_inputs,
                                              required_outputs=req_outputs)

                    # Comp list contains full graph, so don't double up on
                    # the subdrivers.
                    elif not has_interface(comp, IDriver):
                        comp.calc_derivatives(first, second, True)

            self.J = self.fd.calculate()
        finally:
            if hasattr(self.wflow, '_severed_edges'):
                self.wflow.unsever_edges()

        return self.J
 def test_inner_edges(self):
     edges = _get_inner_edges(self.dep, ['a'], ['c'])
     self.assertEqual(set(edges), set([('C2.s2', 'c'), ('C1.s1', 'C2.a'), ('a', 'C1.s1')]))