code: def summary_function(self, file):
      # RP: every time we compile a function a ProfileStats is created for
      # that function. This means that every time a optimization replaces
      # some scan op, some orphane ProfileStats remains in the air ..
      # also even without any optimization, scan compiles a dummy function
      # that will produce a ProfileStats that will correspond to a
      # function that will never be called. Printing several empty
      # Function profiling is just extremely confusing
      if self.callcount == 0:
          return
      print("", file=file)
  
      if self.name is not None:
          print("Scan Op profiling (", self.name, ")", file=file)
      else:
          print("Scan Op profiling", file=file)
      print("==================", file=file)
      print(f"  Message: {self.message}", file=file)
  
      print(
          (
              f"  Time in {self.callcount} calls of the op (for a total of {self.nbsteps} "
              f"steps) {self.call_time:3}s"
          ),
          file=file,
      )
      print("", file=file)
      val = 0
      if self.call_time > 0:
          val = self.vm_call_time * 100 / self.call_time
      print(
          f"  Total time spent in calling the VM {self.vm_call_time:e}s ({val:.3f}%)",
          file=file,
      )
      val = 100
      if self.call_time > 0:
          val = 100.0 - self.vm_call_time * 100 / self.call_time
      print(
          f"  Total overhead (computing slices..) {self.call_time - self.vm_call_time:e}s ({val:.3f}%)",
          file=file,
      )
      print("", file=file)
  
imports: import copy
  import dataclasses
  import logging
  from collections import OrderedDict, namedtuple
  from collections.abc import Sequence
  from itertools import chain
  from typing import TYPE_CHECKING, Callable, Optional, Union
  from typing import cast
  import numpy
  from pytensor import scalar
  from pytensor import tensor
  from pytensor.compile.profiling import ProfileStats
  from pytensor.configdefaults import config
  from pytensor.graph.basic import Constant, Variable, equal_computations, graph_inputs
  from pytensor.graph.op import get_test_value
  from pytensor.graph.replace import clone_replace
  from pytensor.graph.type import HasDataType
  from pytensor.graph.utils import TestValueError
  from pytensor.tensor.basic import AllocEmpty, cast
  from pytensor.tensor.subtensor import set_subtensor
  from pytensor.tensor.variable import TensorConstant
label_desc: Use image node_id 3 for calling the ScanProfileStats obj's underlying member method code with example usage: obj.summary_function(file) without return types