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 on_detach(self, fgraph):
      if fgraph is not self.fgraph:
          raise Exception("detaching wrong fgraph", fgraph)
      del self.destroyers
      del self.view_i
      del self.view_o
      del self.clients
      del self.stale_droot
      assert self.fgraph.destroyer_handler is self
      delattr(self.fgraph, "destroyers")
      delattr(self.fgraph, "has_destroyers")
      delattr(self.fgraph, "destroy_handler")
      self.fgraph = None
  
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 6 for calling the DestroyHandler obj's underlying member method code with example usage: obj.on_detach(fgraph) without return types