decomposition.py 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. r"""Function for computing a junction tree of a graph."""
  2. from itertools import combinations
  3. import networkx as nx
  4. from networkx.algorithms import chordal_graph_cliques, complete_to_chordal_graph, moral
  5. from networkx.utils import not_implemented_for
  6. __all__ = ["junction_tree"]
  7. @not_implemented_for("multigraph")
  8. @nx._dispatchable(returns_graph=True)
  9. def junction_tree(G):
  10. r"""Returns a junction tree of a given graph.
  11. A junction tree (or clique tree) is constructed from a (un)directed graph G.
  12. The tree is constructed based on a moralized and triangulated version of G.
  13. The tree's nodes consist of maximal cliques and sepsets of the revised graph.
  14. The sepset of two cliques is the intersection of the nodes of these cliques,
  15. e.g. the sepset of (A,B,C) and (A,C,E,F) is (A,C). These nodes are often called
  16. "variables" in this literature. The tree is bipartite with each sepset
  17. connected to its two cliques.
  18. Junction Trees are not unique as the order of clique consideration determines
  19. which sepsets are included.
  20. The junction tree algorithm consists of five steps [1]_:
  21. 1. Moralize the graph
  22. 2. Triangulate the graph
  23. 3. Find maximal cliques
  24. 4. Build the tree from cliques, connecting cliques with shared
  25. nodes, set edge-weight to number of shared variables
  26. 5. Find maximum spanning tree
  27. Parameters
  28. ----------
  29. G : networkx.Graph
  30. Directed or undirected graph.
  31. Returns
  32. -------
  33. junction_tree : networkx.Graph
  34. The corresponding junction tree of `G`.
  35. Raises
  36. ------
  37. NetworkXNotImplemented
  38. Raised if `G` is an instance of `MultiGraph` or `MultiDiGraph`.
  39. References
  40. ----------
  41. .. [1] Junction tree algorithm:
  42. https://en.wikipedia.org/wiki/Junction_tree_algorithm
  43. .. [2] Finn V. Jensen and Frank Jensen. 1994. Optimal
  44. junction trees. In Proceedings of the Tenth international
  45. conference on Uncertainty in artificial intelligence (UAI’94).
  46. Morgan Kaufmann Publishers Inc., San Francisco, CA, USA, 360–366.
  47. """
  48. clique_graph = nx.Graph()
  49. if G.is_directed():
  50. G = moral.moral_graph(G)
  51. chordal_graph, _ = complete_to_chordal_graph(G)
  52. cliques = [tuple(sorted(i)) for i in chordal_graph_cliques(chordal_graph)]
  53. clique_graph.add_nodes_from(cliques, type="clique")
  54. for edge in combinations(cliques, 2):
  55. set_edge_0 = set(edge[0])
  56. set_edge_1 = set(edge[1])
  57. if not set_edge_0.isdisjoint(set_edge_1):
  58. sepset = tuple(sorted(set_edge_0.intersection(set_edge_1)))
  59. clique_graph.add_edge(edge[0], edge[1], weight=len(sepset), sepset=sepset)
  60. junction_tree = nx.maximum_spanning_tree(clique_graph)
  61. for edge in list(junction_tree.edges(data=True)):
  62. junction_tree.add_node(edge[2]["sepset"], type="sepset")
  63. junction_tree.add_edge(edge[0], edge[2]["sepset"])
  64. junction_tree.add_edge(edge[1], edge[2]["sepset"])
  65. junction_tree.remove_edge(edge[0], edge[1])
  66. return junction_tree