class_docstr: Class for heuristic hill climb searches for DAGs, to learn
  network structure from data. `estimate` attempts to find a model with optimal score.
  
  Parameters
  ----------
  data: pandas DataFrame object
      dataframe object where each column represents one variable.
      (If some values in the data are missing the data cells should be set to `numpy.NaN`.
      Note that pandas converts each column containing `numpy.NaN`s to dtype `float`.)
  
  state_names: dict (optional)
      A dict indicating, for each variable, the discrete set of states (or values)
      that the variable can take. If unspecified, the observed values in the data set
      are taken to be the only possible states.
  
  use_caching: boolean
      If True, uses caching of score for faster computation.
      Note: Caching only works for scoring methods which are decomposable. Can
      give wrong results in case of custom scoring methods.
  
  References
  ----------
  Koller & Friedman, Probabilistic Graphical Models - Principles and Techniques, 2009
  Section 18.4.3 (page 811ff)
class_docstr_tok: ["Class","for","heuristic","hill","climb","searches","for","DAGs",",","to","learn","network","structure","from","data",".","`","estimate","`","attempts","to","find","a","model","with","optimal","score",".","Parameters","--","--","--","--","--","data",":","pandas","DataFrame","object","dataframe","object","where","each","column","represents","one","variable",".","(","If","some","values","in","the","data","are","missing","the","data","cells","should","be","set","to","`","numpy.NaN","`",".","Note","that","pandas","converts","each","column","containing","`","numpy.NaN","`","s","to","dtype","`","float","`",".",")","state_names",":","dict","(","optional",")","A","dict","indicating",",","for","each","variable",",","the","discrete","set","of","states","(","or","values",")","that","the","variable","can","take",".","If","unspecified",",","the","observed","values","in","the","data","set","are","taken","to","be","the","only","possible","states",".","use_caching",":","boolean","If","True",",","uses","caching","of","score","for","faster","computation",".","Note",":","Caching","only","works","for","scoring","methods","which","are","decomposable",".","Can","give","wrong","results","in","case","of","custom","scoring","methods",".","References","--","--","--","--","--","Koller","&","Friedman",",","Probabilistic","Graphical","Models","-","Principles","and","Techniques",",","2009","Section","18.4.3","(","page","811ff",")"]
docstr: Generates a list of legal (= not in tabu_list) graph modifications
  for a given model, together with their score changes. Possible graph modifications:
  (1) add, (2) remove, or (3) flip a single edge. For details on scoring
  see Koller & Friedman, Probabilistic Graphical Models, Section 18.4.3.3 (page 818).
  If a number `max_indegree` is provided, only modifications that keep the number
  of parents for each node below `max_indegree` are considered. A list of
  edges can optionally be passed as `black_list` or `white_list` to exclude those
  edges or to limit the search.
docstr_tok: ["Generates","a","list","of","legal","(","=","not","in","tabu_list",")","graph","modifications","for","a","given","model",",","together","with","their","score","changes",".","Possible","graph","modifications",":","(","1",")","add",",","(","2",")","remove",",","or","(","3",")","flip","a","single","edge",".","For","details","on","scoring","see","Koller","&","Friedman",",","Probabilistic","Graphical","Models",",","Section","18.4.3.3","(","page","818",")",".","If","a","number","`","max_indegree","`","is","provided",",","only","modifications","that","keep","the","number","of","parents","for","each","node","below","`","max_indegree","`","are","considered",".","A","list","of","edges","can","optionally","be","passed","as","`","black_list","`","or","`","white_list","`","to","exclude","those","edges","or","to","limit","the","search","."]
code: def _legal_operations(
      self,
      model,
      score,
      structure_score,
      tabu_list,
      max_indegree,
      black_list,
      white_list,
      fixed_edges,
  ):
      """Generates a list of legal (= not in tabu_list) graph modifications
      for a given model, together with their score changes. Possible graph modifications:
      (1) add, (2) remove, or (3) flip a single edge. For details on scoring
      see Koller & Friedman, Probabilistic Graphical Models, Section 18.4.3.3 (page 818).
      If a number `max_indegree` is provided, only modifications that keep the number
      of parents for each node below `max_indegree` are considered. A list of
      edges can optionally be passed as `black_list` or `white_list` to exclude those
      edges or to limit the search.
      """
  
      tabu_list = set(tabu_list)
  
      # Step 1: Get all legal operations for adding edges.
      potential_new_edges = (
          set(permutations(self.variables, 2))
          - set(model.edges())
          - set([(Y, X) for (X, Y) in model.edges()])
      )
  
      for X, Y in potential_new_edges:
          # Check if adding (X, Y) will create a cycle.
          if not nx.has_path(model, Y, X):
              operation = ("+", (X, Y))
              if (
                  (operation not in tabu_list)
                  and ((X, Y) not in black_list)
                  and ((X, Y) in white_list)
              ):
                  old_parents = model.get_parents(Y)
                  new_parents = old_parents + [X]
                  if len(new_parents) <= max_indegree:
                      score_delta = score(Y, new_parents) - score(
                          Y, old_parents
                      )
                      score_delta += structure_score("+")
                      yield (operation, score_delta)
  
      # Step 2: Get all legal operations for removing edges
      for X, Y in model.edges():
          operation = ("-", (X, Y))
          if (operation not in tabu_list) and (
              (X, Y) not in fixed_edges
          ):
              old_parents = model.get_parents(Y)
              new_parents = [var for var in old_parents if var != X]
              score_delta = score(Y, new_parents) - score(
                  Y, old_parents
              )
              score_delta += structure_score("-")
              yield (operation, score_delta)
  
      # Step 3: Get all legal operations for flipping edges
      for X, Y in model.edges():
          # Check if flipping creates any cycles
          if not any(
              map(
                  lambda path: len(path) > 2,
                  nx.all_simple_paths(model, X, Y),
              )
          ):
              operation = ("flip", (X, Y))
              if (
                  (
                      (operation not in tabu_list)
                      and ("flip", (Y, X)) not in tabu_list
                  )
                  and ((X, Y) not in fixed_edges)
                  and ((Y, X) not in black_list)
                  and ((Y, X) in white_list)
              ):
                  old_X_parents = model.get_parents(X)
                  old_Y_parents = model.get_parents(Y)
                  new_X_parents = old_X_parents + [Y]
                  new_Y_parents = [
                      var for var in old_Y_parents if var != X
                  ]
                  if len(new_X_parents) <= max_indegree:
                      score_delta = (
                          score(X, new_X_parents)
                          + score(Y, new_Y_parents)
                          - score(X, old_X_parents)
                          - score(Y, old_Y_parents)
                      )
                      score_delta += structure_score("flip")
                      yield (operation, score_delta)
  
file_path: pgmpy/pgmpy/estimators/HillClimbSearch.py
imports: from collections import deque
  from itertools import permutations
  import networkx
  from tqdm.auto import trange
  from pgmpy import config
  from pgmpy.base import DAG
  from pgmpy.estimators import AICScore, BDeuScore, BDsScore, BicScore, K2Score, ScoreCache, StructureEstimator, StructureScore
label_desc: Use image node_id 2 for calling the HillClimbSearch obj's underlying member method code with example usage: obj._legal_operations(model, score, structure_score, tabu_list, max_indegree, black_list, white_list, fixed_edges) without return types