class_docstr: Base class for graphical representation of Structural Equation Models(SEMs).
  
  All variables are by default assumed to have an associated error latent variable, therefore
  doesn't need to be specified.
  
  Parameters
  ----------
  ebunch: list/array-like
      List of edges in form of tuples. Each tuple can be of two possible shape:
          1. (u, v): This would add an edge from u to v without setting any parameter
                     for the edge.
          2. (u, v, parameter): This would add an edge from u to v and set the edge's
                      parameter to `parameter`.
  
  latents: list/array-like
      List of nodes which are latent. All other variables are considered observed.
  
  err_corr: list/array-like
      List of tuples representing edges between error terms. It can be of the following forms:
          1. (u, v): Add correlation between error terms of `u` and `v`. Doesn't set any variance or
                     covariance values.
          2. (u, v, covar): Adds correlation between the error terms of `u` and `v` and sets the
                            parameter to `covar`.
  
  err_var: dict (variable: variance)
      Sets variance for the error terms in the model.
  
  Examples
  --------
  Defining a model (Union sentiment model[1]) without setting any paramaters.
  >>> from pgmpy.models import SEMGraph
  >>> sem = SEMGraph(ebunch=[('deferenc', 'unionsen'), ('laboract', 'unionsen'),
  ...                        ('yrsmill', 'unionsen'), ('age', 'deferenc'),
  ...                        ('age', 'laboract'), ('deferenc', 'laboract')],
  ...                latents=[],
  ...                err_corr=[('yrsmill', 'age')],
  ...                err_var={})
  
  Defining a model (Education [2]) with all the parameters set. For not setting any
  parameter `np.NaN` can be explicitly passed.
  >>> sem_edu = SEMGraph(ebunch=[('intelligence', 'academic', 0.8), ('intelligence', 'scale_1', 0.7),
  ...                            ('intelligence', 'scale_2', 0.64), ('intelligence', 'scale_3', 0.73),
  ...                            ('intelligence', 'scale_4', 0.82), ('academic', 'SAT_score', 0.98),
  ...                            ('academic', 'High_school_gpa', 0.75), ('academic', 'ACT_score', 0.87)],
  ...                    latents=['intelligence', 'academic'],
  ...                    err_corr=[],
  ...                    err_var={'intelligence': 1})
  
  References
  ----------
  [1] McDonald, A, J., & Clelland, D. A. (1984). Textile Workers and Union Sentiment.
      Social Forces, 63(2), 502–521
  [2] https://en.wikipedia.org/wiki/Structural_equation_modeling#/
      media/File:Example_Structural_equation_model.svg
  
  Attributes
  ----------
  latents: list
      List of all the latent variables in the model except the error terms.
  
  observed: list
      List of all the observed variables in the model.
  
  graph: nx.DirectedGraph
      The graphical structure of the latent and observed variables except the error terms.
      The parameters are stored in the `weight` attribute of each edge.
  
  err_graph: nx.Graph
      An undirected graph representing the relations between the error terms of the model.
      The node of the graph has the same name as the variable but represents the error terms.
      The variance is stored in the `weight` attribute of the node and the covariance are stored
      in the `weight` attribute of the edge.
  
  full_graph_struct: nx.DiGraph
      Represents the full graph structure. The names of error terms start with `.` and
      new nodes are added for each correlation which starts with `..`.
class_docstr_tok: ["Base","class","for","graphical","representation","of","Structural","Equation","Models","(","SEMs",")",".","All","variables","are","by","default","assumed","to","have","an","associated","error","latent","variable",",","therefore","does","n't","need","to","be","specified",".","Parameters","--","--","--","--","--","ebunch",":","list\/array-like","List","of","edges","in","form","of","tuples",".","Each","tuple","can","be","of","two","possible","shape",":","1",".","(","u",",","v",")",":","This","would","add","an","edge","from","u","to","v","without","setting","any","parameter","for","the","edge",".","2",".","(","u",",","v",",","parameter",")",":","This","would","add","an","edge","from","u","to","v","and","set","the","edge's","parameter","to","`","parameter","`",".","latents",":","list\/array-like","List","of","nodes","which","are","latent",".","All","other","variables","are","considered","observed",".","err_corr",":","list\/array-like","List","of","tuples","representing","edges","between","error","terms",".","It","can","be","of","the","following","forms",":","1",".","(","u",",","v",")",":","Add","correlation","between","error","terms","of","`","u","`","and","`","v","`",".","Does","n't","set","any","variance","or","covariance","values",".","2",".","(","u",",","v",",","covar",")",":","Adds","correlation","between","the","error","terms","of","`","u","`","and","`","v","`","and","sets","the","parameter","to","`","covar","`",".","err_var",":","dict","(","variable",":","variance",")","Sets","variance","for","the","error","terms","in","the","model",".","Examples","--","--","--","--","Defining","a","model","(","Union","sentiment","model","[","1","]",")","without","setting","any","paramaters",".",">",">",">","from","pgmpy.models","import","SEMGraph",">",">",">","sem","=","SEMGraph","(","ebunch=","[","(","'deferenc","'",",","'unionsen","'",")",",","(","'laboract","'",",","'unionsen","'",")",",","...","(","'yrsmill","'",",","'unionsen","'",")",",","(","'age","'",",","'deferenc","'",")",",","...","(","'age","'",",","'laboract","'",")",",","(","'deferenc","'",",","'laboract","'",")","]",",","...","latents=","[","]",",","...","err_corr=","[","(","'yrsmill","'",",","'age","'",")","]",",","...","err_var=","{","}",")","Defining","a","model","(","Education","[","2","]",")","with","all","the","parameters","set",".","For","not","setting","any","parameter","`","np.NaN","`","can","be","explicitly","passed",".",">",">",">","sem_edu","=","SEMGraph","(","ebunch=","[","(","'intelligence","'",",","'academic","'",",","0.8",")",",","(","'intelligence","'",",","'scale_1","'",",","0.7",")",",","...","(","'intelligence","'",",","'scale_2","'",",","0.64",")",",","(","'intelligence","'",",","'scale_3","'",",","0.73",")",",","...","(","'intelligence","'",",","'scale_4","'",",","0.82",")",",","(","'academic","'",",","'SAT_score","'",",","0.98",")",",","...","(","'academic","'",",","'High_school_gpa","'",",","0.75",")",",","(","'academic","'",",","'ACT_score","'",",","0.87",")","]",",","...","latents=","[","'intelligence","'",",","'academic","'","]",",","...","err_corr=","[","]",",","...","err_var=","{","'intelligence","'",":","1","}",")","References","--","--","--","--","--","[","1","]","McDonald",",","A",",","J.",",","&","Clelland",",","D.","A",".","(","1984",")",".","Textile","Workers","and","Union","Sentiment",".","Social","Forces",",","63","(","2",")",",","502\u2013521","[","2","]","https",":","\/\/en.wikipedia.org\/wiki\/Structural_equation_modeling","#","\/","media\/File",":","Example_Structural_equation_model.svg","Attributes","--","--","--","--","--","latents",":","list","List","of","all","the","latent","variables","in","the","model","except","the","error","terms",".","observed",":","list","List","of","all","the","observed","variables","in","the","model",".","graph",":","nx.DirectedGraph","The","graphical","structure","of","the","latent","and","observed","variables","except","the","error","terms",".","The","parameters","are","stored","in","the","`","weight","`","attribute","of","each","edge",".","err_graph",":","nx.Graph","An","undirected","graph","representing","the","relations","between","the","error","terms","of","the","model",".","The","node","of","the","graph","has","the","same","name","as","the","variable","but","represents","the","error","terms",".","The","variance","is","stored","in","the","`","weight","`","attribute","of","the","node","and","the","covariance","are","stored","in","the","`","weight","`","attribute","of","the","edge",".","full_graph_struct",":","nx.DiGraph","Represents","the","full","graph","structure",".","The","names","of","error","terms","start","with","`",".","`","and","new","nodes","are","added","for","each","correlation","which","starts","with","`","..","`","."]
docstr: Finds the set of the nearest separators for `Y` and `Z` in `G`.
  
  Parameters
  ----------
  G: nx.DiGraph instance
      The graph in which to the find the nearest separation for `Y` and `Z`.
  
  Y: str
      The variable name for which the separators are needed.
  
  Z: str
      The other variable for which the separators are needed.
  
  Returns
  -------
  set or None: If there is a nearest separator returns the set of separators else returns None.
docstr_tok: ["Finds","the","set","of","the","nearest","separators","for","`","Y","`","and","`","Z","`","in","`","G","`",".","Parameters","--","--","--","--","--","G",":","nx.DiGraph","instance","The","graph","in","which","to","the","find","the","nearest","separation","for","`","Y","`","and","`","Z","`",".","Y",":","str","The","variable","name","for","which","the","separators","are","needed",".","Z",":","str","The","other","variable","for","which","the","separators","are","needed",".","Returns","--","--","--","-","set","or","None",":","If","there","is","a","nearest","separator","returns","the","set","of","separators","else","returns","None","."]
code: def _nearest_separator(self, G, Y, Z):
      """
      Finds the set of the nearest separators for `Y` and `Z` in `G`.
  
      Parameters
      ----------
      G: nx.DiGraph instance
          The graph in which to the find the nearest separation for `Y` and `Z`.
  
      Y: str
          The variable name for which the separators are needed.
  
      Z: str
          The other variable for which the separators are needed.
  
      Returns
      -------
      set or None: If there is a nearest separator returns the set of separators else returns None.
      """
      W = set()
      ancestral_G = G.subgraph(
          nx.ancestors(G, Y).union(nx.ancestors(G, Z)).union({Y, Z})
      ).copy()
  
      # Optimization: Remove all error nodes which don't have any correlation as it doesn't add any new path. If not removed it can create a lot of
      # extra paths resulting in a much higher runtime.
      err_nodes_to_remove = set(self.err_graph.nodes()) - set(
          [node for edge in self.err_graph.edges() for node in edge]
      )
      ancestral_G.remove_nodes_from(
          ["." + node for node in err_nodes_to_remove]
      )
  
      M = self.moralize(graph=ancestral_G)
      visited = set([Y])
      to_visit = list(M.neighbors(Y))
  
      # Another optimization over the original algo. Rather than going through all the paths does
      # a DFS search to find a markov blanket of observed variables. This doesn't ensure minimal observed
      # set.
      while to_visit:
          node = to_visit.pop()
          if node == Z:
              return None
          visited.add(node)
          if node in self.observed:
              W.add(node)
          else:
              to_visit.extend(
                  [
                      node
                      for node in M.neighbors(node)
                      if node not in visited
                  ]
              )
      # for path in nx.all_simple_paths(M, Y, Z):
      #     path_set = set(path)
      #     if (len(path) >= 3) and not (W & path_set):
      #         for index in range(1, len(path)-1):
      #             if path[index] in self.observed:
      #                 W.add(path[index])
      #                 break
      if (
          Y
          not in self.active_trail_nodes(
              [Z], observed=W, struct=ancestral_G
          )[Z]
      ):
          return W
      else:
          return None
  
imports: import itertools
  import networkx
  import numpy
  import pandas
  from networkx.algorithms.dag import descendants
  from pyparsing import OneOrMore, Optional, Suppress, Word, alphanums, nums
  from pgmpy.base import DAG
  from pgmpy.global_vars import logger
label_desc: Use image node_id 8 for calling the SEMGraph obj's underlying member method code with example usage: obj._nearest_separator(G, Y, Z) and returns: W, None, None