class_docstr: The DestroyHandler class detects when a graph is impossible to evaluate
  because of aliasing and destructive operations.
  
  Several data structures are used to do this.
  
  An Op can use its view_map property to declare that an output may be
  aliased to an input. If that output is destroyed, the input is also
  considered to be destroyed. The view_maps of several Ops can feed into
  one another and form a directed graph. The consequence of destroying any
  variable in such a graph is that all variables in the graph must be
  considered to be destroyed, because they could all be referring to the
  same underlying storage.
  
  In the current implementation, that graph is a tree, and the root of that
  tree is called the foundation.
  
  TODO: why "in the current implementation" ? is there another implementation
        planned?
  TODO: why is the graph a tree? isn't it possible that one variable could
        be aliased to many variables? for example, don't switch and ifelse
        have to do this?
  
  The original DestroyHandler (if 0'ed out above) computed several data
  structures from scratch each time it was asked to validate the graph.
  Because this happens potentially thousands of times and each graph to
  validate is extremely similar to the previous one, computing the
  data structures from scratch repeatedly was wasteful and resulted in
  high compile times for large graphs.
  
  This implementation computes the data structures once at initialization
  and then incrementally updates them.
  
  It is a work in progress. The following data structures have been
  converted to use the incremental strategy:
      <none>
  
  The following data structures remain to be converted:
      <unknown>
class_docstr_tok: ["The","DestroyHandler","class","detects","when","a","graph","is","impossible","to","evaluate","because","of","aliasing","and","destructive","operations",".","Several","data","structures","are","used","to","do","this",".","An","Op","can","use","its","view_map","property","to","declare","that","an","output","may","be","aliased","to","an","input",".","If","that","output","is","destroyed",",","the","input","is","also","considered","to","be","destroyed",".","The","view_maps","of","several","Ops","can","feed","into","one","another","and","form","a","directed","graph",".","The","consequence","of","destroying","any","variable","in","such","a","graph","is","that","all","variables","in","the","graph","must","be","considered","to","be","destroyed",",","because","they","could","all","be","referring","to","the","same","underlying","storage",".","In","the","current","implementation",",","that","graph","is","a","tree",",","and","the","root","of","that","tree","is","called","the","foundation",".","TODO",":","why","``","in","the","current","implementation","''","?","is","there","another","implementation","planned","?","TODO",":","why","is","the","graph","a","tree","?","is","n't","it","possible","that","one","variable","could","be","aliased","to","many","variables","?","for","example",",","do","n't","switch","and","ifelse","have","to","do","this","?","The","original","DestroyHandler","(","if","0'ed","out","above",")","computed","several","data","structures","from","scratch","each","time","it","was","asked","to","validate","the","graph",".","Because","this","happens","potentially","thousands","of","times","and","each","graph","to","validate","is","extremely","similar","to","the","previous","one",",","computing","the","data","structures","from","scratch","repeatedly","was","wasteful","and","resulted","in","high","compile","times","for","large","graphs",".","This","implementation","computes","the","data","structures","once","at","initialization","and","then","incrementally","updates","them",".","It","is","a","work","in","progress",".","The","following","data","structures","have","been","converted","to","use","the","incremental","strategy",":","<","none",">","The","following","data","structures","remain","to","be","converted",":","<","unknown",">"]
code: def unpickle(self, fgraph):
      def get_destroyers_of(r):
          droot, _, root_destroyer = self.refresh_droot_impact()
          try:
              return [root_destroyer[droot[r]]]
          except Exception:
              return []
  
      fgraph.destroyers = get_destroyers_of
  
      def has_destroyers(protected_list):
          if self.algo != "fast":
              droot, _, root_destroyer = self.refresh_droot_impact()
              for protected_var in protected_list:
                  try:
                      root_destroyer[droot[protected_var]]
                      return True
                  except KeyError:
                      pass
              return False
  
          def recursive_destroys_finder(protected_var):
              # protected_var is the idx'th input of app.
              for app, idx in fgraph.clients[protected_var]:
                  if app == "output":
                      continue
                  destroy_maps = app.op.destroy_map.values()
                  # If True means that the apply node, destroys the protected_var.
                  if idx in [
                      dmap
                      for sublist in destroy_maps
                      for dmap in sublist
                  ]:
                      return True
                  for var_idx in app.op.view_map.keys():
                      if idx in app.op.view_map[var_idx]:
                          # We need to recursively check the destroy_map of all the
                          # outputs that we have a view_map on.
                          if recursive_destroys_finder(
                              app.outputs[var_idx]
                          ):
                              return True
              return False
  
          for protected_var in protected_list:
              if recursive_destroys_finder(protected_var):
                  return True
          return False
  
      fgraph.has_destroyers = has_destroyers
  
imports: import itertools
  from collections import OrderedDict, deque
  import pytensor
  from pytensor.configdefaults import config
  from pytensor.graph.basic import Constant
  from pytensor.graph.features import AlreadyThere, Bookkeeper
  from pytensor.graph.utils import InconsistencyError
  from pytensor.misc.ordered_set import OrderedSet
label_desc: Use image node_id 4 for calling the DestroyHandler obj's underlying member method code with example usage: obj.unpickle(fgraph) and returns: False, list, False, False, list, True, True, True, True