about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/networkx/algorithms/tree/decomposition.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/networkx/algorithms/tree/decomposition.py')
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/tree/decomposition.py88
1 files changed, 88 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/tree/decomposition.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/tree/decomposition.py
new file mode 100644
index 00000000..c8b8f247
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/tree/decomposition.py
@@ -0,0 +1,88 @@
+r"""Function for computing a junction tree of a graph."""
+
+from itertools import combinations
+
+import networkx as nx
+from networkx.algorithms import chordal_graph_cliques, complete_to_chordal_graph, moral
+from networkx.utils import not_implemented_for
+
+__all__ = ["junction_tree"]
+
+
+@not_implemented_for("multigraph")
+@nx._dispatchable(returns_graph=True)
+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 bipartite 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