Python networkx.condensation() Examples

The following are 25 code examples of networkx.condensation(). You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may also want to check out all available functions/classes of the module networkx , or try the search function .
Example #1
Source File: test_minors.py    From qgisSpaceSyntaxToolkit with GNU General Public License v3.0 6 votes vote down vote up
def test_condensation_as_quotient(self):
        """This tests that the condensation of a graph can be viewed as the
        quotient graph under the "in the same connected component" equivalence
        relation.

        """
        # This example graph comes from the file `test_strongly_connected.py`.
        G = nx.DiGraph()
        G.add_edges_from([(1, 2), (2, 3), (2, 11), (2, 12), (3, 4), (4, 3),
                          (4, 5), (5, 6), (6, 5), (6, 7), (7, 8), (7, 9),
                          (7, 10), (8, 9), (9, 7), (10, 6), (11, 2), (11, 4),
                          (11, 6), (12, 6), (12, 11)])
        scc = list(nx.strongly_connected_components(G))
        C = nx.condensation(G, scc)
        component_of = C.graph['mapping']
        # Two nodes are equivalent if they are in the same connected component.
        same_component = lambda u, v: component_of[u] == component_of[v]
        Q = nx.quotient_graph(G, same_component)
        assert_true(nx.is_isomorphic(C, Q)) 
Example #2
Source File: test_strongly_connected.py    From qgisSpaceSyntaxToolkit with GNU General Public License v3.0 6 votes vote down vote up
def test_contract_scc1(self):
        G = nx.DiGraph()
        G.add_edges_from([
            (1, 2), (2, 3), (2, 11), (2, 12), (3, 4), (4, 3), (4, 5), (5, 6),
            (6, 5), (6, 7), (7, 8), (7, 9), (7, 10), (8, 9), (9, 7), (10, 6),
            (11, 2), (11, 4), (11, 6), (12, 6), (12, 11),
        ])
        scc = list(nx.strongly_connected_components(G))
        cG = nx.condensation(G, scc)
        # DAG
        assert_true(nx.is_directed_acyclic_graph(cG))
        # nodes
        assert_equal(sorted(cG.nodes()), [0, 1, 2, 3])
        # edges
        mapping={}
        for i, component in enumerate(scc):
            for n in component:
                mapping[n] = i
        edge = (mapping[2], mapping[3])
        assert_true(cG.has_edge(*edge))
        edge = (mapping[2], mapping[5])
        assert_true(cG.has_edge(*edge))
        edge = (mapping[3], mapping[5])
        assert_true(cG.has_edge(*edge)) 
Example #3
Source File: test_strongly_connected.py    From aws-kube-codesuite with Apache License 2.0 6 votes vote down vote up
def test_contract_scc1(self):
        G = nx.DiGraph()
        G.add_edges_from([
            (1, 2), (2, 3), (2, 11), (2, 12), (3, 4), (4, 3), (4, 5), (5, 6),
            (6, 5), (6, 7), (7, 8), (7, 9), (7, 10), (8, 9), (9, 7), (10, 6),
            (11, 2), (11, 4), (11, 6), (12, 6), (12, 11),
        ])
        scc = list(nx.strongly_connected_components(G))
        cG = nx.condensation(G, scc)
        # DAG
        assert_true(nx.is_directed_acyclic_graph(cG))
        # nodes
        assert_equal(sorted(cG.nodes()), [0, 1, 2, 3])
        # edges
        mapping={}
        for i, component in enumerate(scc):
            for n in component:
                mapping[n] = i
        edge = (mapping[2], mapping[3])
        assert_true(cG.has_edge(*edge))
        edge = (mapping[2], mapping[5])
        assert_true(cG.has_edge(*edge))
        edge = (mapping[3], mapping[5])
        assert_true(cG.has_edge(*edge)) 
Example #4
Source File: test_minors.py    From aws-kube-codesuite with Apache License 2.0 6 votes vote down vote up
def test_condensation_as_quotient(self):
        """This tests that the condensation of a graph can be viewed as the
        quotient graph under the "in the same connected component" equivalence
        relation.

        """
        # This example graph comes from the file `test_strongly_connected.py`.
        G = nx.DiGraph()
        G.add_edges_from([(1, 2), (2, 3), (2, 11), (2, 12), (3, 4), (4, 3),
                          (4, 5), (5, 6), (6, 5), (6, 7), (7, 8), (7, 9),
                          (7, 10), (8, 9), (9, 7), (10, 6), (11, 2), (11, 4),
                          (11, 6), (12, 6), (12, 11)])
        scc = list(nx.strongly_connected_components(G))
        C = nx.condensation(G, scc)
        component_of = C.graph['mapping']
        # Two nodes are equivalent if they are in the same connected component.

        def same_component(u, v):
            return component_of[u] == component_of[v]

        Q = nx.quotient_graph(G, same_component)
        assert_true(nx.is_isomorphic(C, Q)) 
Example #5
Source File: test_minors.py    From Carnets with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
def test_condensation_as_quotient(self):
        """This tests that the condensation of a graph can be viewed as the
        quotient graph under the "in the same connected component" equivalence
        relation.

        """
        # This example graph comes from the file `test_strongly_connected.py`.
        G = nx.DiGraph()
        G.add_edges_from([(1, 2), (2, 3), (2, 11), (2, 12), (3, 4), (4, 3),
                          (4, 5), (5, 6), (6, 5), (6, 7), (7, 8), (7, 9),
                          (7, 10), (8, 9), (9, 7), (10, 6), (11, 2), (11, 4),
                          (11, 6), (12, 6), (12, 11)])
        scc = list(nx.strongly_connected_components(G))
        C = nx.condensation(G, scc)
        component_of = C.graph['mapping']
        # Two nodes are equivalent if they are in the same connected component.

        def same_component(u, v):
            return component_of[u] == component_of[v]

        Q = nx.quotient_graph(G, same_component)
        assert_true(nx.is_isomorphic(C, Q)) 
Example #6
Source File: test_strongly_connected.py    From Carnets with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
def test_contract_scc1(self):
        G = nx.DiGraph()
        G.add_edges_from([
            (1, 2), (2, 3), (2, 11), (2, 12), (3, 4), (4, 3), (4, 5), (5, 6),
            (6, 5), (6, 7), (7, 8), (7, 9), (7, 10), (8, 9), (9, 7), (10, 6),
            (11, 2), (11, 4), (11, 6), (12, 6), (12, 11),
        ])
        scc = list(nx.strongly_connected_components(G))
        cG = nx.condensation(G, scc)
        # DAG
        assert_true(nx.is_directed_acyclic_graph(cG))
        # nodes
        assert_equal(sorted(cG.nodes()), [0, 1, 2, 3])
        # edges
        mapping = {}
        for i, component in enumerate(scc):
            for n in component:
                mapping[n] = i
        edge = (mapping[2], mapping[3])
        assert_true(cG.has_edge(*edge))
        edge = (mapping[2], mapping[5])
        assert_true(cG.has_edge(*edge))
        edge = (mapping[3], mapping[5])
        assert_true(cG.has_edge(*edge)) 
Example #7
Source File: attracting.py    From aws-kube-codesuite with Apache License 2.0 5 votes vote down vote up
def attracting_components(G):
    """Generates a list of attracting components in `G`.

    An attracting component in a directed graph `G` is a strongly connected
    component with the property that a random walker on the graph will never
    leave the component, once it enters the component.

    The nodes in attracting components can also be thought of as recurrent
    nodes.  If a random walker enters the attractor containing the node, then
    the node will be visited infinitely often.

    Parameters
    ----------
    G : DiGraph, MultiDiGraph
        The graph to be analyzed.

    Returns
    -------
    attractors : generator of sets
        A generator of sets of nodes, one for each attracting component of G.

    Raises
    ------
    NetworkXNotImplemented :
        If the input graph is undirected.

    See Also
    --------
    number_attracting_components
    is_attracting_component
    attracting_component_subgraphs

    """
    scc = list(nx.strongly_connected_components(G))
    cG = nx.condensation(G, scc)
    for n in cG:
        if cG.out_degree(n) == 0:
            yield scc[n] 
Example #8
Source File: _graphs.py    From tmppy with Apache License 2.0 5 votes vote down vote up
def compute_condensation_in_topological_order(dependency_graph: nx.DiGraph, sort_by = lambda x: x):
    if not dependency_graph.number_of_nodes():
        return

    condensed_graph = nx.condensation(dependency_graph)
    assert isinstance(condensed_graph, nx.DiGraph)

    for connected_component_index in nx.lexicographical_topological_sort(condensed_graph, key=sort_by):
        yield list(sorted(condensed_graph.nodes[connected_component_index]['members'], key=sort_by)) 
Example #9
Source File: test_strongly_connected.py    From aws-kube-codesuite with Apache License 2.0 5 votes vote down vote up
def test_connected_raise(self):
        G=nx.Graph()
        assert_raises(NetworkXNotImplemented, nx.strongly_connected_components, G)
        assert_raises(NetworkXNotImplemented, nx.kosaraju_strongly_connected_components, G)
        assert_raises(NetworkXNotImplemented, nx.strongly_connected_components_recursive, G)
        assert_raises(NetworkXNotImplemented, nx.strongly_connected_component_subgraphs, G)
        assert_raises(NetworkXNotImplemented, nx.is_strongly_connected, G)
        assert_raises(nx.NetworkXPointlessConcept, nx.is_strongly_connected, nx.DiGraph())
        assert_raises(NetworkXNotImplemented, nx.condensation, G) 
Example #10
Source File: test_strongly_connected.py    From aws-kube-codesuite with Apache License 2.0 5 votes vote down vote up
def test_contract_scc_edge(self):
        G = nx.DiGraph()
        G.add_edge(1, 2)
        G.add_edge(2, 1)
        G.add_edge(2, 3)
        G.add_edge(3, 4)
        G.add_edge(4, 3)
        scc = list(nx.strongly_connected_components(G))
        cG = nx.condensation(G, scc)
        assert_equal(sorted(cG.nodes()), [0, 1])
        if 1 in scc[0]:
            edge = (0, 1)
        else:
            edge = (1, 0)
        assert_equal(list(cG.edges()), [edge]) 
Example #11
Source File: test_strongly_connected.py    From aws-kube-codesuite with Apache License 2.0 5 votes vote down vote up
def test_contract_scc_isolate(self):
        # Bug found and fixed in [1687].
        G = nx.DiGraph()
        G.add_edge(1, 2)
        G.add_edge(2, 1)
        scc = list(nx.strongly_connected_components(G))
        cG = nx.condensation(G, scc)
        assert_equal(list(cG.nodes()), [0])
        assert_equal(list(cG.edges()), []) 
Example #12
Source File: attracting.py    From Carnets with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
def attracting_components(G):
    """Generates the attracting components in `G`.

    An attracting component in a directed graph `G` is a strongly connected
    component with the property that a random walker on the graph will never
    leave the component, once it enters the component.

    The nodes in attracting components can also be thought of as recurrent
    nodes.  If a random walker enters the attractor containing the node, then
    the node will be visited infinitely often.

    Parameters
    ----------
    G : DiGraph, MultiDiGraph
        The graph to be analyzed.

    Returns
    -------
    attractors : generator of sets
        A generator of sets of nodes, one for each attracting component of G.

    Raises
    ------
    NetworkXNotImplemented :
        If the input graph is undirected.

    See Also
    --------
    number_attracting_components
    is_attracting_component

    """
    scc = list(nx.strongly_connected_components(G))
    cG = nx.condensation(G, scc)
    for n in cG:
        if cG.out_degree(n) == 0:
            yield scc[n] 
Example #13
Source File: test_strongly_connected.py    From Carnets with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
def test_connected_raise(self):
        G = nx.Graph()
        assert_raises(NetworkXNotImplemented, nx.strongly_connected_components, G)
        assert_raises(NetworkXNotImplemented, nx.kosaraju_strongly_connected_components, G)
        assert_raises(NetworkXNotImplemented, nx.strongly_connected_components_recursive, G)
        assert_raises(NetworkXNotImplemented, nx.is_strongly_connected, G)
        assert_raises(nx.NetworkXPointlessConcept, nx.is_strongly_connected, nx.DiGraph())
        assert_raises(NetworkXNotImplemented, nx.condensation, G)
        # deprecated
        assert_raises(NetworkXNotImplemented, nx.strongly_connected_component_subgraphs, G)

#    Commented out due to variability on Travis-CI hardware/operating systems
#    def test_linear_time(self):
#        # See Issue #2831
#        count = 100  # base case
#        dg = nx.DiGraph()
#        dg.add_nodes_from([0, 1])
#        for i in range(2, count):
#            dg.add_node(i)
#            dg.add_edge(i, 1)
#            dg.add_edge(0, i)
#        t = time.time()
#        ret = tuple(nx.strongly_connected_components(dg))
#        dt = time.time() - t
#
#        count = 200
#        dg = nx.DiGraph()
#        dg.add_nodes_from([0, 1])
#        for i in range(2, count):
#            dg.add_node(i)
#            dg.add_edge(i, 1)
#            dg.add_edge(0, i)
#        t = time.time()
#        ret = tuple(nx.strongly_connected_components(dg))
#        dt2 = time.time() - t
#        assert_less(dt2, dt * 2.3)  # should be 2 times longer for this graph 
Example #14
Source File: test_strongly_connected.py    From Carnets with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
def test_null_graph(self):
        G = nx.DiGraph()
        assert_equal(list(nx.strongly_connected_components(G)), [])
        assert_equal(list(nx.kosaraju_strongly_connected_components(G)), [])
        assert_equal(list(nx.strongly_connected_components_recursive(G)), [])
        assert_equal(len(nx.condensation(G)), 0)
        assert_raises(nx.NetworkXPointlessConcept, nx.is_strongly_connected, nx.DiGraph()) 
Example #15
Source File: test_strongly_connected.py    From Carnets with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
def test_contract_scc_edge(self):
        G = nx.DiGraph()
        G.add_edge(1, 2)
        G.add_edge(2, 1)
        G.add_edge(2, 3)
        G.add_edge(3, 4)
        G.add_edge(4, 3)
        scc = list(nx.strongly_connected_components(G))
        cG = nx.condensation(G, scc)
        assert_equal(sorted(cG.nodes()), [0, 1])
        if 1 in scc[0]:
            edge = (0, 1)
        else:
            edge = (1, 0)
        assert_equal(list(cG.edges()), [edge]) 
Example #16
Source File: test_strongly_connected.py    From Carnets with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
def test_contract_scc_isolate(self):
        # Bug found and fixed in [1687].
        G = nx.DiGraph()
        G.add_edge(1, 2)
        G.add_edge(2, 1)
        scc = list(nx.strongly_connected_components(G))
        cG = nx.condensation(G, scc)
        assert_equal(list(cG.nodes()), [0])
        assert_equal(list(cG.edges()), []) 
Example #17
Source File: attracting.py    From qgisSpaceSyntaxToolkit with GNU General Public License v3.0 5 votes vote down vote up
def attracting_components(G):
    """Generates a list of attracting components in `G`.

    An attracting component in a directed graph `G` is a strongly connected
    component with the property that a random walker on the graph will never
    leave the component, once it enters the component.

    The nodes in attracting components can also be thought of as recurrent
    nodes.  If a random walker enters the attractor containing the node, then
    the node will be visited infinitely often.

    Parameters
    ----------
    G : DiGraph, MultiDiGraph
        The graph to be analyzed.

    Returns
    -------
    attractors : generator of sets
        A generator of sets of nodes, one for each attracting component of G.

    See Also
    --------
    number_attracting_components
    is_attracting_component 
    attracting_component_subgraphs

    """
    scc = list(nx.strongly_connected_components(G))
    cG = nx.condensation(G, scc)
    for n in cG:
        if cG.out_degree(n) == 0:
            yield scc[n] 
Example #18
Source File: test_strongly_connected.py    From qgisSpaceSyntaxToolkit with GNU General Public License v3.0 5 votes vote down vote up
def test_connected_raise(self):
        G=nx.Graph()
        assert_raises(NetworkXNotImplemented, nx.strongly_connected_components, G)
        assert_raises(NetworkXNotImplemented, nx.kosaraju_strongly_connected_components, G)
        assert_raises(NetworkXNotImplemented, nx.strongly_connected_components_recursive, G)
        assert_raises(NetworkXNotImplemented, nx.strongly_connected_component_subgraphs, G)
        assert_raises(NetworkXNotImplemented, nx.is_strongly_connected, G)
        assert_raises(nx.NetworkXPointlessConcept, nx.is_strongly_connected, nx.DiGraph())
        assert_raises(NetworkXNotImplemented, nx.condensation, G) 
Example #19
Source File: test_strongly_connected.py    From qgisSpaceSyntaxToolkit with GNU General Public License v3.0 5 votes vote down vote up
def test_contract_scc_edge(self):
        G = nx.DiGraph()
        G.add_edge(1, 2)
        G.add_edge(2, 1)
        G.add_edge(2, 3)
        G.add_edge(3, 4)
        G.add_edge(4, 3)
        scc = list(nx.strongly_connected_components(G))
        cG = nx.condensation(G, scc)
        assert_equal(cG.nodes(), [0, 1])
        if 1 in scc[0]:
            edge = (0, 1)
        else:
            edge = (1, 0)
        assert_equal(list(cG.edges()), [edge]) 
Example #20
Source File: test_strongly_connected.py    From qgisSpaceSyntaxToolkit with GNU General Public License v3.0 5 votes vote down vote up
def test_contract_scc_isolate(self):
        # Bug found and fixed in [1687].
        G = nx.DiGraph()
        G.add_edge(1, 2)
        G.add_edge(2, 1)
        scc = list(nx.strongly_connected_components(G))
        cG = nx.condensation(G, scc)
        assert_equal(list(cG.nodes()), [0])
        assert_equal(list(cG.edges()), []) 
Example #21
Source File: _recalculate_template_instantiation_can_trigger_static_asserts_info.py    From tmppy with Apache License 2.0 5 votes vote down vote up
def recalculate_template_instantiation_can_trigger_static_asserts_info(header: ir.Header):
    if not header.template_defns:
        return header

    template_defn_by_name = {template_defn.name: template_defn
                             for template_defn in header.template_defns}
    template_defn_dependency_graph = compute_template_dependency_graph(header.template_defns, template_defn_by_name)

    condensed_graph = nx.condensation(template_defn_dependency_graph)
    assert isinstance(condensed_graph, nx.DiGraph)

    template_defn_dependency_graph_transitive_closure = nx.transitive_closure(template_defn_dependency_graph)
    assert isinstance(template_defn_dependency_graph_transitive_closure, nx.DiGraph)

    # Determine which connected components can trigger static assert errors.
    condensed_node_can_trigger_static_asserts = defaultdict(lambda: False)
    for connected_component_index in reversed(list(nx.lexicographical_topological_sort(condensed_graph))):
        condensed_node = condensed_graph.nodes[connected_component_index]

        # If a template defn in this connected component can trigger a static assert, the whole component can.
        for template_defn_name in condensed_node['members']:
            if _template_defn_contains_static_assert_stmt(template_defn_by_name[template_defn_name]):
                condensed_node_can_trigger_static_asserts[connected_component_index] = True

        # If a template defn in this connected component references a template defn in a connected component that can
        # trigger static asserts, this connected component can also trigger them.
        for called_condensed_node_index in condensed_graph.successors(connected_component_index):
            if condensed_node_can_trigger_static_asserts[called_condensed_node_index]:
                condensed_node_can_trigger_static_asserts[connected_component_index] = True

    template_defn_can_trigger_static_asserts = dict()
    for connected_component_index in condensed_graph:
        for template_defn_name in condensed_graph.nodes[connected_component_index]['members']:
            template_defn_can_trigger_static_asserts[template_defn_name] = condensed_node_can_trigger_static_asserts[connected_component_index]

    return _apply_template_instantiation_can_trigger_static_asserts_info(header, template_defn_can_trigger_static_asserts) 
Example #22
Source File: semiconnected.py    From Carnets with BSD 3-Clause "New" or "Revised" License 4 votes vote down vote up
def is_semiconnected(G):
    """Returns True if the graph is semiconnected, False otherwise.

    A graph is semiconnected if, and only if, for any pair of nodes, either one
    is reachable from the other, or they are mutually reachable.

    Parameters
    ----------
    G : NetworkX graph
        A directed graph.

    Returns
    -------
    semiconnected : bool
        True if the graph is semiconnected, False otherwise.

    Raises
    ------
    NetworkXNotImplemented :
        If the input graph is undirected.

    NetworkXPointlessConcept :
        If the graph is empty.

    Examples
    --------
    >>> G=nx.path_graph(4,create_using=nx.DiGraph())
    >>> print(nx.is_semiconnected(G))
    True
    >>> G=nx.DiGraph([(1, 2), (3, 2)])
    >>> print(nx.is_semiconnected(G))
    False

    See Also
    --------
    is_strongly_connected
    is_weakly_connected
    is_connected
    is_biconnected
    """
    if len(G) == 0:
        raise nx.NetworkXPointlessConcept(
            'Connectivity is undefined for the null graph.')

    if not nx.is_weakly_connected(G):
        return False

    G = nx.condensation(G)
    path = nx.topological_sort(G)
    return all(G.has_edge(u, v) for u, v in pairwise(path)) 
Example #23
Source File: semiconnected.py    From aws-kube-codesuite with Apache License 2.0 4 votes vote down vote up
def is_semiconnected(G):
    """Return True if the graph is semiconnected, False otherwise.

    A graph is semiconnected if, and only if, for any pair of nodes, either one
    is reachable from the other, or they are mutually reachable.

    Parameters
    ----------
    G : NetworkX graph
        A directed graph.

    Returns
    -------
    semiconnected : bool
        True if the graph is semiconnected, False otherwise.

    Raises
    ------
    NetworkXNotImplemented :
        If the input graph is undirected.

    NetworkXPointlessConcept :
        If the graph is empty.

    Examples
    --------
    >>> G=nx.path_graph(4,create_using=nx.DiGraph())
    >>> print(nx.is_semiconnected(G))
    True
    >>> G=nx.DiGraph([(1, 2), (3, 2)])
    >>> print(nx.is_semiconnected(G))
    False

    See Also
    --------
    is_strongly_connected
    is_weakly_connected
    is_connected
    is_biconnected
    """
    if len(G) == 0:
        raise nx.NetworkXPointlessConcept(
            'Connectivity is undefined for the null graph.')

    if not nx.is_weakly_connected(G):
        return False

    G = nx.condensation(G)
    path = nx.topological_sort(G)
    return all(G.has_edge(u, v) for u, v in pairwise(path)) 
Example #24
Source File: semiconnected.py    From qgisSpaceSyntaxToolkit with GNU General Public License v3.0 4 votes vote down vote up
def is_semiconnected(G):
    """Return True if the graph is semiconnected, False otherwise.

    A graph is semiconnected if, and only if, for any pair of nodes, either one
    is reachable from the other, or they are mutually reachable.

    Parameters
    ----------
    G : NetworkX graph
        A directed graph.

    Returns
    -------
    semiconnected : bool
        True if the graph is semiconnected, False otherwise.

    Raises
    ------
    NetworkXNotImplemented :
        If the input graph is not directed.

    NetworkXPointlessConcept :
        If the graph is empty.

    Examples
    --------
    >>> G=nx.path_graph(4,create_using=nx.DiGraph())
    >>> print(nx.is_semiconnected(G))
    True
    >>> G=nx.DiGraph([(1, 2), (3, 2)])
    >>> print(nx.is_semiconnected(G))
    False

    See Also
    --------
    is_strongly_connected,
    is_weakly_connected
    """
    if len(G) == 0:
        raise nx.NetworkXPointlessConcept(
            'Connectivity is undefined for the null graph.')

    if not nx.is_weakly_connected(G):
        return False

    G = nx.condensation(G)
    path = nx.topological_sort(G)
    return all(G.has_edge(u, v) for u, v in zip(path[:-1], path[1:])) 
Example #25
Source File: _recalculate_function_can_throw_info.py    From tmppy with Apache License 2.0 4 votes vote down vote up
def recalculate_function_can_throw_info(module: ir.Module, context_object_file_content: ObjectFileContent):
    if not module.function_defns:
        return module

    function_dependency_graph = nx.DiGraph()

    function_defn_by_name = {function_defn.name: function_defn
                             for function_defn in module.function_defns}

    for function_defn in module.function_defns:
        function_dependency_graph.add_node(function_defn.name)

        for global_function_name in get_referenced_global_function_names(function_defn):
            if global_function_name in function_defn_by_name.keys():
                function_dependency_graph.add_edge(function_defn.name, global_function_name)

    condensed_graph = nx.condensation(function_dependency_graph)
    assert isinstance(condensed_graph, nx.DiGraph)

    function_dependency_graph_transitive_closure = nx.transitive_closure(function_dependency_graph)
    assert isinstance(function_dependency_graph_transitive_closure, nx.DiGraph)

    # Determine which connected components can throw.
    condensed_node_can_throw = defaultdict(lambda: False)
    for connected_component_index in reversed(list(nx.lexicographical_topological_sort(condensed_graph))):
        condensed_node = condensed_graph.nodes[connected_component_index]

        # If a function in this connected component can throw, the whole component can throw.
        for function_name in condensed_node['members']:
            if function_contains_raise_stmt(function_defn_by_name[function_name]):
                condensed_node_can_throw[connected_component_index] = True

        # If a function in this connected component calls a function in a connected component that can throw, this
        # connected component can also throw.
        for called_condensed_node_index in condensed_graph.successors(connected_component_index):
            if condensed_node_can_throw[called_condensed_node_index]:
                condensed_node_can_throw[connected_component_index] = True

    function_can_throw = dict()
    for connected_component_index in condensed_graph:
        for function_name in condensed_graph.nodes[connected_component_index]['members']:
            function_can_throw[function_name] = condensed_node_can_throw[connected_component_index]

    external_function_can_throw = dict()
    for module_name, module_info in context_object_file_content.modules_by_name.items():
        for elem in itertools.chain(module_info.ir2_module.custom_types, module_info.ir2_module.function_defns):
            if elem.name in module_info.ir2_module.public_names:
                external_function_can_throw[(module_name, elem.name)] = (isinstance(elem, ir.FunctionDefn)
                                                                         and (function_contains_raise_stmt(elem)
                                                                              or function_contains_var_reference_that_can_throw(elem)))

    return apply_function_can_throw_info(module, function_can_throw, external_function_can_throw)