class_docstr: Initializes a Bayesian Network.
  A models stores nodes and edges with conditional probability
  distribution (cpd) and other attributes.
  
  models hold directed edges.  Self loops are not allowed neither
  multiple (parallel) edges.
  
  Nodes can be any hashable python object.
  
  Edges are represented as links between nodes.
  
  Parameters
  ----------
  ebunch: input graph
      Data to initialize graph.  If ebunch=None (default) an empty
      graph is created.  The ebunch can be an edge list, or any
      NetworkX graph object.
  
  latents: list, array-like
      List of variables which are latent (i.e. unobserved) in the model.
  
  Examples
  --------
  Create an empty Bayesian Network with no nodes and no edges.
  
  >>> from pgmpy.models import BayesianNetwork
  >>> G = BayesianNetwork()
  
  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: ["Initializes","a","Bayesian","Network",".","A","models","stores","nodes","and","edges","with","conditional","probability","distribution","(","cpd",")","and","other","attributes",".","models","hold","directed","edges",".","Self","loops","are","not","allowed","neither","multiple","(","parallel",")","edges",".","Nodes","can","be","any","hashable","python","object",".","Edges","are","represented","as","links","between","nodes",".","Parameters","--","--","--","--","--","ebunch",":","input","graph","Data","to","initialize","graph",".","If","ebunch=None","(","default",")","an","empty","graph","is","created",".","The","ebunch","can","be","an","edge","list",",","or","any","NetworkX","graph","object",".","latents",":","list",",","array-like","List","of","variables","which","are","latent","(","i.e",".","unobserved",")","in","the","model",".","Examples","--","--","--","--","Create","an","empty","Bayesian","Network","with","no","nodes","and","no","edges",".",">",">",">","from","pgmpy.models","import","BayesianNetwork",">",">",">","G","=","BayesianNetwork","(",")","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: Simulates data from the given model. Internally uses methods from
  pgmpy.sampling.BayesianModelSampling to generate the data.
  
  Parameters
  ----------
  n_samples: int
      The number of data samples to simulate from the model.
  
  do: dict
      The interventions to apply to the model. dict should be of the form
      {variable_name: state}
  
  evidence: dict
      Observed evidence to apply to the model. dict should be of the form
      {variable_name: state}
  
  virtual_evidence: list
      Probabilistically apply evidence to the model. `virtual_evidence` should
      be a list of `pgmpy.factors.discrete.TabularCPD` objects specifying the
      virtual probabilities.
  
  virtual_intervention: list
      Also known as soft intervention. `virtual_intervention` should be a list
      of `pgmpy.factors.discrete.TabularCPD` objects specifying the virtual/soft
      intervention probabilities.
  
  include_latents: boolean
      Whether to include the latent variable values in the generated samples.
  
  partial_samples: pandas.DataFrame
      A pandas dataframe specifying samples on some of the variables in the model. If
      specified, the sampling procedure uses these sample values, instead of generating them.
      partial_samples.shape[0] must be equal to `n_samples`.
  
  seed: int (default: None)
      If a value is provided, sets the seed for numpy.random.
  
  show_progress: bool
      If True, shows a progress bar when generating samples.
  
  Returns
  -------
  A dataframe with the simulated data: pd.DataFrame
  
  Examples
  --------
  >>> from pgmpy.utils import get_example_model
  
  Simulation without any evidence or intervention:
  
  >>> model = get_example_model('alarm')
  >>> model.simulate(n_samples=10)
  
  Simulation with the hard evidence: MINVOLSET = HIGH:
  
  >>> model.simulate(n_samples=10, evidence={"MINVOLSET": "HIGH"})
  
  Simulation with hard intervention: CVP = LOW:
  
  >>> model.simulate(n_samples=10, do={"CVP": "LOW"})
  
  Simulation with virtual/soft evidence: p(MINVOLSET=LOW) = 0.8, p(MINVOLSET=HIGH) = 0.2,
  p(MINVOLSET=NORMAL) = 0:
  
  >>> virt_evidence = [TabularCPD("MINVOLSET", 3, [[0.8], [0.0], [0.2]], state_names={"MINVOLSET": ["LOW", "NORMAL", "HIGH"]})]
  >>> model.simulate(n_samples, virtual_evidence=virt_evidence)
  
  Simulation with virtual/soft intervention: p(CVP=LOW) = 0.2, p(CVP=NORMAL)=0.5, p(CVP=HIGH)=0.3:
  
  >>> virt_intervention = [TabularCPD("CVP", 3, [[0.2], [0.5], [0.3]], state_names={"CVP": ["LOW", "NORMAL", "HIGH"]})]
  >>> model.simulate(n_samples, virtual_intervention=virt_intervention)
code: def simulate(
      self,
      n_samples=10,
      do=None,
      evidence=None,
      virtual_evidence=None,
      virtual_intervention=None,
      include_latents=False,
      partial_samples=None,
      seed=None,
      show_progress=True,
  ):
      """
      Simulates data from the given model. Internally uses methods from
      pgmpy.sampling.BayesianModelSampling to generate the data.
  
      Parameters
      ----------
      n_samples: int
          The number of data samples to simulate from the model.
  
      do: dict
          The interventions to apply to the model. dict should be of the form
          {variable_name: state}
  
      evidence: dict
          Observed evidence to apply to the model. dict should be of the form
          {variable_name: state}
  
      virtual_evidence: list
          Probabilistically apply evidence to the model. `virtual_evidence` should
          be a list of `pgmpy.factors.discrete.TabularCPD` objects specifying the
          virtual probabilities.
  
      virtual_intervention: list
          Also known as soft intervention. `virtual_intervention` should be a list
          of `pgmpy.factors.discrete.TabularCPD` objects specifying the virtual/soft
          intervention probabilities.
  
      include_latents: boolean
          Whether to include the latent variable values in the generated samples.
  
      partial_samples: pandas.DataFrame
          A pandas dataframe specifying samples on some of the variables in the model. If
          specified, the sampling procedure uses these sample values, instead of generating them.
          partial_samples.shape[0] must be equal to `n_samples`.
  
      seed: int (default: None)
          If a value is provided, sets the seed for numpy.random.
  
      show_progress: bool
          If True, shows a progress bar when generating samples.
  
      Returns
      -------
      A dataframe with the simulated data: pd.DataFrame
  
      Examples
      --------
      >>> from pgmpy.utils import get_example_model
  
      Simulation without any evidence or intervention:
  
      >>> model = get_example_model('alarm')
      >>> model.simulate(n_samples=10)
  
      Simulation with the hard evidence: MINVOLSET = HIGH:
  
      >>> model.simulate(n_samples=10, evidence={"MINVOLSET": "HIGH"})
  
      Simulation with hard intervention: CVP = LOW:
  
      >>> model.simulate(n_samples=10, do={"CVP": "LOW"})
  
      Simulation with virtual/soft evidence: p(MINVOLSET=LOW) = 0.8, p(MINVOLSET=HIGH) = 0.2,
      p(MINVOLSET=NORMAL) = 0:
  
      >>> virt_evidence = [TabularCPD("MINVOLSET", 3, [[0.8], [0.0], [0.2]], state_names={"MINVOLSET": ["LOW", "NORMAL", "HIGH"]})]
      >>> model.simulate(n_samples, virtual_evidence=virt_evidence)
  
      Simulation with virtual/soft intervention: p(CVP=LOW) = 0.2, p(CVP=NORMAL)=0.5, p(CVP=HIGH)=0.3:
  
      >>> virt_intervention = [TabularCPD("CVP", 3, [[0.2], [0.5], [0.3]], state_names={"CVP": ["LOW", "NORMAL", "HIGH"]})]
      >>> model.simulate(n_samples, virtual_intervention=virt_intervention)
      """
      from pgmpy.sampling import BayesianModelSampling
  
      self.check_model()
      model = self.copy()
      state_names = self.states
  
      evidence = {} if evidence is None else evidence
      for var, state in evidence.items():
          if state not in state_names[var]:
              raise ValueError(
                  f"Evidence state: {state} for {var} doesn't exist"
              )
  
      do = {} if do is None else do
      for var, state in do.items():
          if state not in state_names[var]:
              raise ValueError(
                  f"Do state: {state} for {var} doesn't exist"
              )
  
      virtual_intervention = (
          [] if virtual_intervention is None else virtual_intervention
      )
      virtual_evidence = (
          [] if virtual_evidence is None else virtual_evidence
      )
  
      if set(do.keys()).intersection(set(evidence.keys())):
          raise ValueError("Variable can't be in both do and evidence")
  
      # Step 1: If do or virtual_intervention is specified, modify the network structure.
      if (do != {}) or (virtual_intervention != []):
          virt_nodes = [
              cpd.variables[0] for cpd in virtual_intervention
          ]
          model = model.do(list(do.keys()) + virt_nodes)
          evidence = {**evidence, **do}
          virtual_evidence = [*virtual_evidence, *virtual_intervention]
  
      # Step 2: If virtual_evidence; modify the network structure
      if virtual_evidence != []:
          for cpd in virtual_evidence:
              var = cpd.variables[0]
              if var not in model.nodes():
                  raise ValueError(
                      "Evidence provided for variable which is not in the model"
                  )
              elif len(cpd.variables) > 1:
                  raise (
                      "Virtual evidence should be defined on individual variables. Maybe you are looking for soft evidence."
                  )
              elif (
                  self.get_cardinality(var)
                  != cpd.get_cardinality([var])[var]
              ):
                  raise ValueError(
                      "The number of states/cardinality for the evidence should be same as the number of states/cardinality of the variable in the model"
                  )
  
          for cpd in virtual_evidence:
              var = cpd.variables[0]
              new_var = "__" + var
              model.add_edge(var, new_var)
              values = compat_fns.get_compute_backend().vstack(
                  (cpd.values, 1 - cpd.values)
              )
              new_cpd = TabularCPD(
                  variable=new_var,
                  variable_card=2,
                  values=values,
                  evidence=[var],
                  evidence_card=[model.get_cardinality(var)],
                  state_names={
                      new_var: [0, 1],
                      var: cpd.state_names[var],
                  },
              )
              model.add_cpds(new_cpd)
              evidence[new_var] = 0
  
      # Step 3: If no evidence do a forward sampling
      if len(evidence) == 0:
          samples = BayesianModelSampling(model).forward_sample(
              size=n_samples,
              include_latents=include_latents,
              seed=seed,
              show_progress=show_progress,
              partial_samples=partial_samples,
          )
  
      # Step 4: If evidence; do a rejection sampling
      else:
          samples = BayesianModelSampling(model).rejection_sample(
              size=n_samples,
              evidence=[(k, v) for k, v in evidence.items()],
              include_latents=include_latents,
              seed=seed,
              show_progress=show_progress,
              partial_samples=partial_samples,
          )
  
      # Step 5: Postprocess and return
      if include_latents:
          return samples
      else:
          return samples.loc[:, list(set(self.nodes()) - self.latents)]
  
imports: import itertools
  from collections import defaultdict
  from functools import reduce
  from operator import mul
  import networkx
  import numpy
  import pandas
  from joblib import Parallel, delayed
  from tqdm.auto import tqdm
  from pgmpy.base import DAG
  from pgmpy.factors.continuous import ContinuousFactor
  from pgmpy.factors.discrete import DiscreteFactor, JointProbabilityDistribution, TabularCPD
  from pgmpy.global_vars import logger
  from pgmpy.models.MarkovNetwork import MarkovNetwork
  from pgmpy.utils import compat_fns
label_desc: Use image node_id 25 for calling the BayesianNetwork obj's underlying member method code with example usage: obj.simulate(n_samples, do, evidence, virtual_evidence, virtual_intervention, include_latents, partial_samples, seed, show_progress) and returns: samples, samples