docs: def likelihood_weighted_sample(self, evidence=None, size=1, return_type="dataframe"):
          """
          Generates weighted sample(s) from joint distribution of the bayesian
          network, that comply with the given evidence.
          'Probabilistic Graphical Model Principles and Techniques', Koller and
          Friedman, Algorithm 12.2 pp 493.
  
          Parameters
          ----------
          evidence: list of `pgmpy.factor.State` namedtuples
              None if no evidence
          size: int
              size of sample to be generated
          return_type: string (dataframe | recarray)
              Return type for samples, either of 'dataframe' or 'recarray'.
              Defaults to 'dataframe'
  
          Returns
          -------
          sampled: A pandas.DataFrame or a numpy.recarray object depending upon return_type argument
              the generated samples with corresponding weights
  
          Examples
          --------
          >>> from pgmpy.factors.discrete import State
          >>> from pgmpy.models.BayesianModel import BayesianModel
          >>> from pgmpy.factors.discrete import TabularCPD
          >>> from pgmpy.sampling import BayesianModelSampling
          >>> student = BayesianModel([('diff', 'grade'), ('intel', 'grade')])
          >>> cpd_d = TabularCPD('diff', 2, [[0.6], [0.4]])
          >>> cpd_i = TabularCPD('intel', 2, [[0.7], [0.3]])
          >>> cpd_g = TabularCPD('grade', 3, [[0.3, 0.05, 0.9, 0.5], [0.4, 0.25,
          ...         0.08, 0.3], [0.3, 0.7, 0.02, 0.2]],
          ...         ['intel', 'diff'], [2, 2])
          >>> student.add_cpds(cpd_d, cpd_i, cpd_g)
          >>> inference = BayesianModelSampling(student)
          >>> evidence = [State('diff', 0)]
          >>> inference.likelihood_weighted_sample(evidence=evidence, size=2, return_type='recarray')
          rec.array([(0, 0, 1, 0.6), (0, 0, 2, 0.6)], dtype=
                    [('diff', '<i8'), ('intel', '<i8'), ('grade', '<i8'), ('_weight', '<f8')])
          """
          types = [(var_name, 'int') for var_name in self.topological_order]
          types.append(('_weight', 'float'))
          sampled = np.zeros(size, dtype=types).view(np.recarray)
          sampled['_weight'] = np.ones(size)
          evidence_dict = {var: st for var, st in evidence}
  
          for node in self.topological_order:
              cpd = self.model.get_cpds(node)
              states = range(self.cardinality[node])
              evidence = cpd.get_evidence()
  
              if evidence:
                  evidence_values = np.vstack([sampled[i] for i in evidence])
                  cached_values = self.pre_compute_reduce(node)
                  weights = list(map(lambda t: cached_values[tuple(t)], evidence_values.T))
                  if node in evidence_dict:
                      sampled[node] = evidence_dict[node]
                      for i in range(size):
                          sampled['_weight'][i] *= weights[i][evidence_dict[node]]
                  else:
                      sampled[node] = sample_discrete(states, weights)
              else:
                  if node in evidence_dict:
                      sampled[node] = evidence_dict[node]
                      for i in range(size):
                          sampled['_weight'][i] *= cpd.values[evidence_dict[node]]
                  else:
                      sampled[node] = sample_discrete(states, cpd.values, size)
  
          return _return_samples(return_type, sampled)