class_docstr: Base class for Bayesian Network (BN), a probabilistic weighted DAG where nodes represent variables,
  edges represent the causal relationships between variables.
  
  ``BayesianNetwork`` stores nodes with their possible states, edges and
  conditional probability distributions (CPDs) of each node.
  
  ``BayesianNetwork`` is built on top of the ``StructureModel``, which is an extension of ``networkx.DiGraph``
  (see :func:`causalnex.structure.structuremodel.StructureModel`).
  
  In order to define the ``BayesianNetwork``, users should provide a relevant ``StructureModel``.
  Once ``BayesianNetwork`` is initialised, no changes to the ``StructureModel`` can be made
  and CPDs can be learned from the data.
  
  The learned CPDs can be then used for likelihood estimation and predictions.
  
  Example:
  ::
      >>> # Create a Bayesian Network with a manually defined DAG.
      >>> from causalnex.structure import StructureModel
      >>> from causalnex.network import BayesianNetwork
      >>>
      >>> sm = StructureModel()
      >>> sm.add_edges_from([
      >>>                    ('rush_hour', 'traffic'),
      >>>                    ('weather', 'traffic')
      >>>                    ])
      >>> bn = BayesianNetwork(sm)
      >>> # A created ``BayesianNetwork`` stores nodes and edges defined by the ``StructureModel``
      >>> bn.nodes
      ['rush_hour', 'traffic', 'weather']
      >>>
      >>> bn.edges
      [('rush_hour', 'traffic'), ('weather', 'traffic')]
      >>> # A ``BayesianNetwork`` doesn't store any CPDs yet
      >>> bn.cpds
      >>> {}
      >>>
      >>> # Learn the nodes' states from the data
      >>> import pandas as pd
      >>> data = pd.DataFrame({
      >>>                      'rush_hour': [True, False, False, False, True, False, True],
      >>>                      'weather': ['Terrible', 'Good', 'Bad', 'Good', 'Bad', 'Bad', 'Good'],
      >>>                      'traffic': ['heavy', 'light', 'heavy', 'light', 'heavy', 'heavy', 'heavy']
      >>>                      })
      >>> bn = bn.fit_node_states(data)
      >>> bn.node_states
      {'rush_hour': {False, True}, 'weather': {'Bad', 'Good', 'Terrible'}, 'traffic': {'heavy', 'light'}}
      >>> # Learn the CPDs from the data
      >>> bn = bn.fit_cpds(data)
      >>> # Use the learned CPDs to make predictions on the unseen data
      >>> test_data = pd.DataFrame({
      >>>                           'rush_hour': [False, False, True, True],
      >>>                           'weather': ['Good', 'Bad', 'Good', 'Bad']
      >>>                           })
      >>> bn.predict(test_data, "traffic").to_dict()
      >>> {'traffic_prediction': {0: 'light', 1: 'heavy', 2: 'heavy', 3: 'heavy'}}
      >>> bn.predict_probability(test_data, "traffic").to_dict()
      {'traffic_prediction': {0: 'light', 1: 'heavy', 2: 'heavy', 3: 'heavy'}}
      {'traffic_light': {0: 0.75, 1: 0.25, 2: 0.3333333333333333, 3: 0.3333333333333333},
       'traffic_heavy': {0: 0.25, 1: 0.75, 2: 0.6666666666666666, 3: 0.6666666666666666}}
docstr: Adding a latent variable to the structure model, as well as its corresponding edges
  
  Args:
      node: Name of the node
      edges_to_add: which edges to add to the structure
      edges_to_remove: which edges to remove from the structure
  
  Returns:
      self
  
  Raises:
      ValueError: If lv_name exists in the network or
          if `edges_to_add` include edges NOT containing the latent variable or
          if `edges_to_remove` include edges containing the latent variable
docstr_tok: ["Adding","a","latent","variable","to","the","structure","model",",","as","well","as","its","corresponding","edges","Args",":","node",":","Name","of","the","node","edges_to_add",":","which","edges","to","add","to","the","structure","edges_to_remove",":","which","edges","to","remove","from","the","structure","Returns",":","self","Raises",":","ValueError",":","If","lv_name","exists","in","the","network","or","if","`","edges_to_add","`","include","edges","NOT","containing","the","latent","variable","or","if","`","edges_to_remove","`","include","edges","containing","the","latent","variable"]
code: def add_node(
      self,
      node: str,
      edges_to_add: List[Tuple[str, str]],
      edges_to_remove: List[Tuple[str, str]],
  ) -> "BayesianNetwork":
      """
      Adding a latent variable to the structure model, as well as its corresponding edges
  
      Args:
          node: Name of the node
          edges_to_add: which edges to add to the structure
          edges_to_remove: which edges to remove from the structure
  
      Returns:
          self
  
      Raises:
          ValueError: If lv_name exists in the network or
              if `edges_to_add` include edges NOT containing the latent variable or
              if `edges_to_remove` include edges containing the latent variable
      """
      if any(node not in edges for edges in edges_to_add):
          raise ValueError(
              f"Should only add edges containing node '{node}'"
          )
      if any(node in edges for edges in edges_to_remove):
          raise ValueError(
              f"Should only remove edges NOT containing node '{node}'"
          )
  
      self._structure.add_edges_from(edges_to_add)
      self._structure.remove_edges_from(edges_to_remove)
      self._model.add_edges_from(edges_to_add)
      self._model.remove_edges_from(edges_to_remove)
  
      return self
  
imports: import copy
  import re
  from typing import Dict, Hashable, List, Optional, Set, Tuple, Union
  import networkx
  import pandas
  from pgmpy.estimators import BayesianEstimator, MaximumLikelihoodEstimator
  from pgmpy.factors.discrete.CPD import TabularCPD
  from pgmpy.models import BayesianNetwork
  from causalnex.estimator.em import EMSingleLatentVariable
  from causalnex.structure import StructureModel
  from causalnex.utils.pgmpy_utils import pd_to_tabular_cpd
label_desc: Use image node_id 13 for calling the BayesianNetwork obj's underlying member method code with example usage: obj.add_node(node, edges_to_add, edges_to_remove) and returns: self