class_docstr: Base class for Markov Model.
  
  A MarkovNetwork stores nodes and edges with potentials
  
  MarkovNetwork holds undirected edges.
  
  Parameters
  ----------
  data : input graph
      Data to initialize graph.  If data=None (default) an empty
      graph is created.  The data can be an edge list, or any
      NetworkX graph object.
  
  Examples
  --------
  Create an empty Markov Model with no nodes and no edges.
  
  >>> from pgmpy.models import MarkovNetwork
  >>> G = MarkovNetwork()
  
  G can be grown in several ways.
  
  **Nodes:**
  
  Add one node at a time:
  
  >>> G.add_node('a')
  
  Add the nodes from any container (a list, set or tuple or the nodes
  from another graph).
  
  >>> G.add_nodes_from(['a', 'b'])
  
  **Edges:**
  
  G can also be grown by adding edges.
  
  Add one edge,
  
  >>> G.add_edge('a', 'b')
  
  a list of edges,
  
  >>> G.add_edges_from([('a', 'b'), ('b', 'c')])
  
  If some edges connect nodes not yet in the model, the nodes
  are added automatically.  There are no errors when adding
  nodes or edges that already exist.
  
  **Shortcuts:**
  
  Many common graph features allow python syntax for speed reporting.
  
  >>> 'a' in G     # check if node in graph
  True
  >>> len(G)  # number of nodes in graph
  3
class_docstr_tok: ["Base","class","for","Markov","Model",".","A","MarkovNetwork","stores","nodes","and","edges","with","potentials","MarkovNetwork","holds","undirected","edges",".","Parameters","--","--","--","--","--","data",":","input","graph","Data","to","initialize","graph",".","If","data=None","(","default",")","an","empty","graph","is","created",".","The","data","can","be","an","edge","list",",","or","any","NetworkX","graph","object",".","Examples","--","--","--","--","Create","an","empty","Markov","Model","with","no","nodes","and","no","edges",".",">",">",">","from","pgmpy.models","import","MarkovNetwork",">",">",">","G","=","MarkovNetwork","(",")","G","can","be","grown","in","several","ways",".","*","*","Nodes",":","*","*","Add","one","node","at","a","time",":",">",">",">","G.add_node","(","'","a","'",")","Add","the","nodes","from","any","container","(","a","list",",","set","or","tuple","or","the","nodes","from","another","graph",")",".",">",">",">","G.add_nodes_from","(","[","'","a","'",",","'","b","'","]",")","*","*","Edges",":","*","*","G","can","also","be","grown","by","adding","edges",".","Add","one","edge",",",">",">",">","G.add_edge","(","'","a","'",",","'","b","'",")","a","list","of","edges",",",">",">",">","G.add_edges_from","(","[","(","'","a","'",",","'","b","'",")",",","(","'","b","'",",","'","c","'",")","]",")","If","some","edges","connect","nodes","not","yet","in","the","model",",","the","nodes","are","added","automatically",".","There","are","no","errors","when","adding","nodes","or","edges","that","already","exist",".","*","*","Shortcuts",":","*","*","Many","common","graph","features","allow","python","syntax","for","speed","reporting",".",">",">",">","'","a","'","in","G","#","check","if","node","in","graph","True",">",">",">","len","(","G",")","#","number","of","nodes","in","graph","3"]
docstr: Creates a junction tree (or clique tree) for a given markov model.
  
  For a given markov model (H) a junction tree (G) is a graph
  1. where each node in G corresponds to a maximal clique in H
  2. each sepset in G separates the variables strictly on one side of the
  edge to other.
  
  Examples
  --------
  >>> from pgmpy.models import MarkovNetwork
  >>> from pgmpy.factors.discrete import DiscreteFactor
  >>> mm = MarkovNetwork()
  >>> mm.add_nodes_from(['x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7'])
  >>> mm.add_edges_from([('x1', 'x3'), ('x1', 'x4'), ('x2', 'x4'),
  ...                    ('x2', 'x5'), ('x3', 'x6'), ('x4', 'x6'),
  ...                    ('x4', 'x7'), ('x5', 'x7')])
  >>> phi = [DiscreteFactor(edge, [2, 2], np.random.rand(4)) for edge in mm.edges()]
  >>> mm.add_factors(*phi)
  >>> junction_tree = mm.to_junction_tree()
docstr_tok: ["Creates","a","junction","tree","(","or","clique","tree",")","for","a","given","markov","model",".","For","a","given","markov","model","(","H",")","a","junction","tree","(","G",")","is","a","graph","1.","where","each","node","in","G","corresponds","to","a","maximal","clique","in","H","2.","each","sepset","in","G","separates","the","variables","strictly","on","one","side","of","the","edge","to","other",".","Examples","--","--","--","--",">",">",">","from","pgmpy.models","import","MarkovNetwork",">",">",">","from","pgmpy.factors.discrete","import","DiscreteFactor",">",">",">","mm","=","MarkovNetwork","(",")",">",">",">","mm.add_nodes_from","(","[","'x1","'",",","'x2","'",",","'x3","'",",","'x4","'",",","'x5","'",",","'x6","'",",","'x7","'","]",")",">",">",">","mm.add_edges_from","(","[","(","'x1","'",",","'x3","'",")",",","(","'x1","'",",","'x4","'",")",",","(","'x2","'",",","'x4","'",")",",","...","(","'x2","'",",","'x5","'",")",",","(","'x3","'",",","'x6","'",")",",","(","'x4","'",",","'x6","'",")",",","...","(","'x4","'",",","'x7","'",")",",","(","'x5","'",",","'x7","'",")","]",")",">",">",">","phi","=","[","DiscreteFactor","(","edge",",","[","2",",","2","]",",","np.random.rand","(","4",")",")","for","edge","in","mm.edges","(",")","]",">",">",">","mm.add_factors","(","*","phi",")",">",">",">","junction_tree","=","mm.to_junction_tree","(",")"]
code: def to_junction_tree(self):
      """
      Creates a junction tree (or clique tree) for a given markov model.
  
      For a given markov model (H) a junction tree (G) is a graph
      1. where each node in G corresponds to a maximal clique in H
      2. each sepset in G separates the variables strictly on one side of the
      edge to other.
  
      Examples
      --------
      >>> from pgmpy.models import MarkovNetwork
      >>> from pgmpy.factors.discrete import DiscreteFactor
      >>> mm = MarkovNetwork()
      >>> mm.add_nodes_from(['x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7'])
      >>> mm.add_edges_from([('x1', 'x3'), ('x1', 'x4'), ('x2', 'x4'),
      ...                    ('x2', 'x5'), ('x3', 'x6'), ('x4', 'x6'),
      ...                    ('x4', 'x7'), ('x5', 'x7')])
      >>> phi = [DiscreteFactor(edge, [2, 2], np.random.rand(4)) for edge in mm.edges()]
      >>> mm.add_factors(*phi)
      >>> junction_tree = mm.to_junction_tree()
      """
      from pgmpy.models import JunctionTree
  
      # Check whether the model is valid or not
      self.check_model()
  
      # Triangulate the graph to make it chordal
      triangulated_graph = self.triangulate()
  
      # Find maximal cliques in the chordal graph
      cliques = list(map(tuple, nx.find_cliques(triangulated_graph)))
  
      # If there is only 1 clique, then the junction tree formed is just a
      # clique tree with that single clique as the node
      if len(cliques) == 1:
          clique_trees = JunctionTree()
          clique_trees.add_node(cliques[0])
  
      # Else if the number of cliques is more than 1 then create a complete
      # graph with all the cliques as nodes and weight of the edges being
      # the length of sepset between two cliques
      elif len(cliques) >= 2:
          complete_graph = UndirectedGraph()
          edges = list(itertools.combinations(cliques, 2))
          weights = list(
              map(
                  lambda x: len(set(x[0]).intersection(set(x[1]))),
                  edges,
              )
          )
          for edge, weight in zip(edges, weights):
              complete_graph.add_edge(*edge, weight=-weight)
  
          # Create clique trees by minimum (or maximum) spanning tree method
          clique_trees = JunctionTree(
              nx.minimum_spanning_tree(complete_graph).edges()
          )
  
      # Check whether the factors are defined for all the random variables or not
      all_vars = itertools.chain(
          *[factor.scope() for factor in self.factors]
      )
      if set(all_vars) != set(self.nodes()):
          ValueError(
              "DiscreteFactor for all the random variables not specified"
          )
  
      # Dictionary stating whether the factor is used to create clique
      # potential or not
      # If false, then it is not used to create any clique potential
      is_used = {factor: False for factor in self.factors}
  
      for node in clique_trees.nodes():
          clique_factors = []
          for factor in self.factors:
              # If the factor is not used in creating any clique potential as
              # well as has any variable of the given clique in its scope,
              # then use it in creating clique potential
              if not is_used[factor] and set(factor.scope()).issubset(
                  node
              ):
                  clique_factors.append(factor)
                  is_used[factor] = True
  
          # To compute clique potential, initially set it as unity factor
          var_card = [self.get_cardinality()[x] for x in node]
          clique_potential = DiscreteFactor(
              node, var_card, np.ones(np.prod(var_card))
          )
          # multiply it with the factors associated with the variables present
          # in the clique (or node)
          # Checking if there's clique_factors, to handle the case when clique_factors
          # is empty, otherwise factor_product with throw an error [ref #889]
          if clique_factors:
              clique_potential *= factor_product(*clique_factors)
          clique_trees.add_factors(clique_potential)
  
      if not all(is_used.values()):
          raise ValueError(
              "All the factors were not used to create Junction Tree."
              "Extra factors are defined."
          )
  
      return clique_trees
  
imports: import itertools
  from collections import defaultdict
  import networkx
  import numpy
  from networkx.algorithms.components import connected_components
  from pgmpy.base import UndirectedGraph
  from pgmpy.factors import factor_product
  from pgmpy.factors.discrete import DiscreteFactor
  from pgmpy.independencies import Independencies
label_desc: Use image node_id 11 for calling the MarkovNetwork obj's underlying member method code with example usage: obj.to_junction_tree() and returns: clique_trees