code: def _generate_parent_graph(
      X,
      sample_weight,
      n_categories,
      column_idx,
      include_parents=None,
      exclude_parents=None,
      pseudocount=0,
      penalty=None,
      max_parents=None,
      parent_set=None,
  ):
      """Generate a parent graph for a single variable over its parents.
  
      This will generate the parent graph for a single parents given the data.
      A parent graph is the dynamically generated best parent set and respective
      score for each combination of parent variables. For example, if we are
      generating a parent graph for x1 over x2, x3, and x4, we may calculate that
      having x2 as a parent is better than x2,x3 and so store the value
      of x2 in the node for x2,x3.
  
      Parameters
      ----------
      X: list, numpy.ndarray, torch.tensor, shape=(n, d)
              The data to fit the structure too, where each row is a sample and
              each column corresponds to the associated variable.
  
      weights: list, numpy.ndarray, torch.tensor, shape=(n,)
              The weight of each sample as a positive double. Default is None.
  
      n_categories: list, numpy.ndarray, torch.tensor, shape=(d,)
              The number of unique keys in each column.
  
      column_idx: int
              The column index to build the parent graph for.
  
      include_parents: list or tuple or None
              A set of integers indicating the parents that must be included in the
              returned structure. Default is None.
  
      exclude_parents: list or tuple or None
              A set of integers indicating the parents that must be excluded from
              the returned structure. Default is None.
  
      pseudocount: double
              A pseudocount to add to each count. Default is 0.
  
      penalty: float or None, optional
              The weighting of the model complexity term in the objective function.
              Increasing this value will encourage sparsity whereas setting the value
              to 0 will result in an unregularized structure. Default is
              log2(|D|) / 2 where |D| is the sum of the weights of the data.
  
      max_parents: int
              The maximum number of parents a node can have. If used, this means
              using the k-learn procedure. Can drastically speed up algorithms.
              If -1, no max on parents. Default is -1.
  
      parent_set: tuple, default ()
              The variables which are possible parents for this variable. By default,
              this should be all variables except for idx. If excluded parents are
              passed in, this should exclude those variables as well.
  
  
      Returns
      -------
      structure: tuple, shape=(d,)
              The parents for each variable in this SCC
      """
  
      n, d = X.shape
      n_categories = n_categories.numpy()
      max_n_categories = int(n_categories.max())
  
      parent_graph = {}
      X_dicts = {}
      X_cols = X.T.contiguous().type(torch.int64)
  
      w = sample_weight.sum()
      log_w = torch.log2(sample_weight.sum()).item() / 2
  
      if max_parents is None:
          max_parents = int(torch.log2(2 * w / torch.log2(w)).item())
  
      if parent_set is None:
          parent_set = tuple(set(range(d)) - set([column_idx]))
  
      if include_parents is None:
          include_parents = []
  
      if exclude_parents is None:
          exclude_parents = set()
      else:
          exclude_parents = set(exclude_parents)
  
      for j in range(len(parent_set) + 1):
          c_dims = tuple([max_n_categories for _ in range(j + 1)])
          counts = torch.zeros(*c_dims, dtype=sample_weight.dtype)
  
          offset = max_n_categories**j
          X_cols_ = X_cols * offset
  
          for subset in itertools.combinations(parent_set, j):
              best_structure = ()
              best_score = float("-inf")
  
              if j <= max_parents:
                  for parent in include_parents:
                      if parent not in subset:
                          break
                  else:
                      for var in subset:
                          if var in exclude_parents:
                              break
                      else:
                          if j == 0:
                              X_idxs = torch.zeros(n, dtype=torch.int64)
                              idx = column_idx
                          else:
                              X_idxs = X_dicts[subset[:-1]]
                              idx = subset[-1]
  
                          n_params = numpy.prod(
                              n_categories[list(subset)]
                          ) * (n_categories[column_idx] - 1)
  
                          X_idxs = torch.clone(X_idxs) + X_cols_[idx]
  
                          counts[:] = pseudocount
                          counts.view(-1).scatter_add_(
                              0, X_idxs, sample_weight
                          )
                          marginal_counts = counts.sum(
                              dim=-1, keepdims=True
                          )
  
                          logp = torch.sum(
                              counts
                              * torch.log2(counts / marginal_counts)
                          )
  
                          best_structure = subset
                          best_score = logp - log_w * n_params
                          X_dicts[subset] = X_idxs
  
              for k, variable in enumerate(subset):
                  parent_subset = tuple(
                      l for l in subset if l != variable
                  )
                  structure, score = parent_graph[parent_subset]
  
                  if score > best_score:
                      best_score = score
                      best_structure = structure
  
              parent_graph[subset] = (best_structure, best_score)
  
      return parent_graph
  
imports: import time
  import numpy
  import torch
  import itertools
  import networkx
  from ._utils import _cast_as_tensor
  from ._utils import _cast_as_parameter
  from ._utils import _update_parameter
  from ._utils import _check_parameter
  from ._utils import _reshape_weights
  from .distributions._distribution import Distribution
  from .distributions import Categorical
  from .distributions import JointCategorical
  from .distributions import ConditionalCategorical
  from .factor_graph import FactorGraph
label_desc: Use image node_id 5 for calling a global function with example usage: _generate_parent_graph(X, sample_weight, n_categories, column_idx, include_parents, exclude_parents, pseudocount, penalty, max_parents, parent_set) and returns: parent_graph