class_docstr: Class that satisfies the `Linker` interface by acting as a `VM` factory.
  
  Parameters
  ----------
  allow_gc
      Force the virtual machine to clean up unnecessary references, in order
      to allow garbage collection on intermediate values during computation
      of a function.  If ``None``, use as default the value of the PyTensor
      flag `allow_gc`.
  use_cloop
      Use the C-based virtual machine if possible
  callback
      A callable object to call after each call to a thunk within the virtual
      machine.  It will be called with four arguments: ``node``, ``thunk``,
      ``storage_map``, and ``compute_map``.
  callback_input
      A callable object to call on each input to the graph (variables with no
      owner).  This includes constants and shared variables values.  It will
      be called with two arguments: ``var``, ``value``.
  lazy
      Useful only when `use_cloop` is False. When `lazy` is ``None``, use the
      PyTensor flag ``vm__lazy`` value. Then if we have a ``None`` (default) we
      auto detect if lazy evaluation is needed and use the appropriate
      version. If `lazy` is ``True`` or ``False``, we force the version used
      between `Loop` and `Stack`.
  c_thunks
      If ``None`` or ``True``, don't change the default. If ``False``, don't
      compile C code for the thunks.
  allow_partial_eval
      If ``True``, enforces usage of `Stack` or `CVM`, to allow for partial
      evaluation of functions (calculating a subset of outputs).
class_docstr_tok: ["Class","that","satisfies","the","`","Linker","`","interface","by","acting","as","a","`","VM","`","factory",".","Parameters","--","--","--","--","--","allow_gc","Force","the","virtual","machine","to","clean","up","unnecessary","references",",","in","order","to","allow","garbage","collection","on","intermediate","values","during","computation","of","a","function",".","If","``","None","``",",","use","as","default","the","value","of","the","PyTensor","flag","`","allow_gc","`",".","use_cloop","Use","the","C-based","virtual","machine","if","possible","callback","A","callable","object","to","call","after","each","call","to","a","thunk","within","the","virtual","machine",".","It","will","be","called","with","four","arguments",":","``","node","``",",","``","thunk","``",",","``","storage_map","``",",","and","``","compute_map","``",".","callback_input","A","callable","object","to","call","on","each","input","to","the","graph","(","variables","with","no","owner",")",".","This","includes","constants","and","shared","variables","values",".","It","will","be","called","with","two","arguments",":","``","var","``",",","``","value","``",".","lazy","Useful","only","when","`","use_cloop","`","is","False",".","When","`","lazy","`","is","``","None","``",",","use","the","PyTensor","flag","``","vm__lazy","``","value",".","Then","if","we","have","a","``","None","``","(","default",")","we","auto","detect","if","lazy","evaluation","is","needed","and","use","the","appropriate","version",".","If","`","lazy","`","is","``","True","``","or","``","False","``",",","we","force","the","version","used","between","`","Loop","`","and","`","Stack","`",".","c_thunks","If","``","None","``","or","``","True","``",",","do","n't","change","the","default",".","If","``","False","``",",","don't","compile","C","code","for","the","thunks",".","allow_partial_eval","If","``","True","``",",","enforces","usage","of","`","Stack","`","or","`","CVM","`",",","to","allow","for","partial","evaluation","of","functions","(","calculating","a","subset","of","outputs",")","."]
code: def make_all(
      self,
      profiler=None,
      input_storage=None,
      output_storage=None,
      storage_map=None,
  ):
      fgraph = self.fgraph
      order = self.schedule(fgraph)
  
      input_storage, output_storage, storage_map = map_storage(
          fgraph, order, input_storage, output_storage, storage_map
      )
      compute_map = {}
      for k in storage_map:
          compute_map[k] = [k.owner is None]
  
      thunks = []
  
      t0 = time.perf_counter()
      linker_make_thunk_time = {}
      impl = None
      if self.c_thunks is False:
          impl = "py"
      for node in order:
          try:
              thunk_start = time.perf_counter()
              # no-recycling is done at each VM.__call__ So there is
              # no need to cause duplicate c code by passing
              # no_recycling here.
              thunks.append(
                  node.op.make_thunk(
                      node, storage_map, compute_map, [], impl=impl
                  )
              )
              linker_make_thunk_time[node] = (
                  time.perf_counter() - thunk_start
              )
              if not hasattr(thunks[-1], "lazy"):
                  # We don't want all ops maker to think about lazy Ops.
                  # So if they didn't specify that its lazy or not, it isn't.
                  # If this member isn't present, it will crash later.
                  thunks[-1].lazy = False
          except Exception:
              raise_with_op(fgraph, node)
  
      t1 = time.perf_counter()
  
      if self.profile:
          self.profile.linker_node_make_thunks += t1 - t0
          self.profile.linker_make_thunk_time = linker_make_thunk_time
  
      for node, thunk in zip(order, thunks):
          thunk.inputs = [storage_map[v] for v in node.inputs]
          thunk.outputs = [storage_map[v] for v in node.outputs]
  
      lazy = self.lazy
      if lazy is None:
          lazy = config.vm__lazy
      if lazy is None:
          lazy = any(th.lazy for th in thunks)
      if not (
          lazy
          or (
              (config.profile or config.print_global_stats)
              and config.profile_memory
          )
          or self.use_cloop
          or self.callback
          or self.callback_input
      ):
          reallocated_vars = self.reduce_storage_allocations(
              storage_map, order
          )
      else:
          reallocated_vars = ()
  
      computed, last_user = gc_helper(order)
      if self.allow_gc:
          post_thunk_clear = []
          for node in order:
              clear_after_this_thunk = []
              for input in node.inputs:
                  if (
                      input in computed
                      and input not in fgraph.outputs
                      and node == last_user[input]
                      and input not in reallocated_vars
                  ):
                      clear_after_this_thunk.append(storage_map[input])
              post_thunk_clear.append(clear_after_this_thunk)
      else:
          post_thunk_clear = None
  
      vm = self.make_vm(
          order,
          thunks,
          input_storage,
          output_storage,
          storage_map,
          post_thunk_clear,
          computed,
          compute_map,
          self.updated_vars,
      )
  
      vm.storage_map = storage_map
      vm.compute_map = compute_map
  
      return (
          vm,
          [
              Container(input, storage)
              for input, storage in zip(fgraph.inputs, input_storage)
          ],
          [
              Container(output, storage, readonly=True)
              for output, storage in zip(fgraph.outputs, output_storage)
          ],
          thunks,
          order,
      )
  
imports: import platform
  import sys
  import time
  import warnings
  from abc import ABC, abstractmethod
  from collections import defaultdict
  from collections.abc import Sequence
  from itertools import zip_longest
  from typing import TYPE_CHECKING, Any, Optional
  from pytensor.configdefaults import config
  from pytensor.graph.basic import Apply, Constant, Variable
  from pytensor.link.basic import Container, LocalLinker
  from pytensor.link.c.exceptions import MissingGXX
  from pytensor.link.utils import gc_helper, get_destroy_dependencies, map_storage, raise_with_op
label_desc: Use image node_id 7 for calling the VMLinker obj's underlying member method code with example usage: obj.make_all(profiler, input_storage, output_storage, storage_map) and returns: vm,  unknown,  unknown,  thunks,  order