Esempio n. 1
0
def test_get_moral_graph():
    graph = nx.DiGraph()
    graph.add_nodes_from([1, 2, 3, 4, 5, 6, 7])
    graph.add_edges_from([(1, 2), (3, 2), (4, 1), (4, 5), (6, 5), (7, 5)])
    H = moral_graph(graph)
    assert_true(not H.is_directed())
    assert_true(H.has_edge(1, 3))
    assert_true(H.has_edge(4, 6))
    assert_true(H.has_edge(6, 7))
    assert_true(H.has_edge(4, 7))
    assert_true(not H.has_edge(1, 5))
Esempio n. 2
0
def test_get_moral_graph():
    graph = nx.DiGraph()
    graph.add_nodes_from([1, 2, 3, 4, 5, 6, 7])
    graph.add_edges_from([(1, 2), (3, 2), (4, 1), (4, 5), (6, 5), (7, 5)])
    H = moral_graph(graph)
    assert not H.is_directed()
    assert H.has_edge(1, 3)
    assert H.has_edge(4, 6)
    assert H.has_edge(6, 7)
    assert H.has_edge(4, 7)
    assert not H.has_edge(1, 5)
Esempio n. 3
0
def independent(G, n1, n2, n3=None):
    """Computes whether n1 and n2 are independent given n3 on the DAG G
    
    Can find a decent exposition of the algorithm at http://web.mit.edu/jmn/www/6.034/d-separation.pdf
    """
    if n3 is None:
        n3 = set()
    elif isinstance(n3, (int, str)):
        n3 = set([n3])
    elif not isinstance(n3, set):
        n3 = set(n3)
    # Construct the ancestral graph of n1, n2, and n3
    a = ancestors(G, n1) | ancestors(G, n2) | {n1, n2} | n3
    G = G.subgraph(a)
    # Moralize the graph
    M = moral_graph(G)
    # Remove n3 (if applicable)
    M.remove_nodes_from(n3)
    # Check that path exists between n1 and n2
    return not has_path(M, n1, n2)
Esempio n. 4
0
def test_moralize():
    # Generate random graphs from nodes count
    nodes = [2, 10, 25, 50, 75, 100, 250]
    graphs = [nx.gnp_random_graph(n, 0.5, directed=True) for n in nodes]
    # Transform random graphs to DAGs by edge selection
    graphs = [
        nx.DiGraph([(u, v) for (u, v) in G.edges() if u < v]) for G in graphs
    ]
    # Check if transformed graphs are DAGs
    are_dags = [nx.is_directed_acyclic_graph(G) for G in graphs]
    assert (all(are_dags))
    # Moralize graphs with reference function and extract matrices
    morals = [moral_graph(G) for G in graphs]
    morals = [nx.to_numpy_array(G).astype(bool) for G in morals]
    # Moralize graphs with test function and extract matrices
    wi_graphs = [mb.DirectedGraph.from_networkx(G) for G in graphs]
    wi_morals = [mb.moralize(G) for G in wi_graphs]
    wi_morals = [G.adjacency_matrix() for G in wi_morals]
    # Check if graphs are equals
    are_equals = [
        np.array_equal(morals[i], wi_morals[i]) for i in range(len(nodes))
    ]
    assert (all(are_equals))
Esempio n. 5
0
from networkx.algorithms import moral
from networkx.algorithms.tree.decomposition import junction_tree
from networkx.drawing.nx_agraph import graphviz_layout as layout
import matplotlib.pyplot as plt

B = nx.DiGraph()
B.add_nodes_from(["A", "B", "C", "D", "E", "F"])
B.add_edges_from([("A", "B"), ("A", "C"), ("B", "D"), ("B", "F"), ("C", "E"),
                  ("E", "F")])

options = {"with_labels": True, "node_color": "white", "edgecolors": "blue"}

bayes_pos = layout(B, prog="neato")
ax1 = plt.subplot(1, 3, 1)
plt.title("Bayesian Network")
nx.draw_networkx(B, pos=bayes_pos, **options)

mg = moral.moral_graph(B)
plt.subplot(1, 3, 2, sharex=ax1, sharey=ax1)
plt.title("Moralized Graph")
nx.draw_networkx(mg, pos=bayes_pos, **options)

jt = junction_tree(B)
plt.subplot(1, 3, 3)
plt.title("Junction Tree")
nsize = [2000 * len(n) for n in list(jt.nodes())]
nx.draw_networkx(jt, pos=layout(jt, prog="neato"), node_size=nsize, **options)

plt.tight_layout()
plt.show()
Esempio n. 6
0
def junction_tree(G):
    r"""Returns a junction tree of a given graph.

    A junction tree (or clique tree) is constructed from a (un)directed graph G.
    The tree is constructed based on a moralized and triangulated version of G.
    The tree's nodes consist of maximal cliques and sepsets of the revised graph.
    The sepset of two cliques is the intersection of the nodes of these cliques,
    e.g. the sepset of (A,B,C) and (A,C,E,F) is (A,C). These nodes are often called
    "variables" in this literature. The tree is bipartitie with each sepset
    connected to its two cliques.

    Junction Trees are not unique as the order of clique consideration determines
    which sepsets are included.

    The junction tree algorithm consists of five steps [1]_:

    1. Moralize the graph
    2. Triangulate the graph
    3. Find maximal cliques
    4. Build the tree from cliques, connecting cliques with shared
       nodes, set edge-weight to number of shared variables
    5. Find maximum spanning tree


    Parameters
    ----------
    G : networkx.Graph
        Directed or undirected graph.

    Returns
    -------
    junction_tree : networkx.Graph
        The corresponding junction tree of `G`.

    Raises
    ------
    NetworkXNotImplemented
        Raised if `G` is an instance of `MultiGraph` or `MultiDiGraph`.

    References
    ----------
    .. [1] Junction tree algorithm:
       https://en.wikipedia.org/wiki/Junction_tree_algorithm

    .. [2] Finn V. Jensen and Frank Jensen. 1994. Optimal
       junction trees. In Proceedings of the Tenth international
       conference on Uncertainty in artificial intelligence (UAI’94).
       Morgan Kaufmann Publishers Inc., San Francisco, CA, USA, 360–366.
    """

    clique_graph = nx.Graph()

    if G.is_directed():
        G = moral.moral_graph(G)
    chordal_graph, _ = complete_to_chordal_graph(G)

    cliques = [tuple(sorted(i)) for i in chordal_graph_cliques(chordal_graph)]
    clique_graph.add_nodes_from(cliques, type="clique")

    for edge in combinations(cliques, 2):
        set_edge_0 = set(edge[0])
        set_edge_1 = set(edge[1])
        if not set_edge_0.isdisjoint(set_edge_1):
            sepset = tuple(sorted(set_edge_0.intersection(set_edge_1)))
            clique_graph.add_edge(edge[0],
                                  edge[1],
                                  weight=len(sepset),
                                  sepset=sepset)

    junction_tree = nx.maximum_spanning_tree(clique_graph)

    for edge in list(junction_tree.edges(data=True)):
        junction_tree.add_node(edge[2]["sepset"], type="sepset")
        junction_tree.add_edge(edge[0], edge[2]["sepset"])
        junction_tree.add_edge(edge[1], edge[2]["sepset"])
        junction_tree.remove_edge(edge[0], edge[1])

    return junction_tree
 def _moralize_graph(self, g: nx.DiGraph) -> nx.Graph:
     return moral_graph(g)