class_docstr: Graph optimizer for inserting Gemm operations.
class_docstr_tok: ["Graph","optimizer","for","inserting","Gemm","operations","."]
code: def apply(self, fgraph):
      did_something = True
      nb_iter = 0
      nb_replacement = 0
      nb_replacement_didn_t_remove = 0
      nb_inconsistency_make = 0
      nb_inconsistency_replace = 0
      time_canonicalize = 0
      time_factor_can = 0
      time_factor_list = 0
      time_toposort = 0
      if fgraph.profile:
          validate_before = fgraph.profile.validate_time
          callbacks_before = fgraph.execute_callbacks_times.copy()
          callback_before = fgraph.execute_callbacks_time
  
      def on_import(new_node):
          if new_node is not node:
              nodelist.append(new_node)
  
      u = pytensor.graph.rewriting.basic.DispatchingFeature(
          on_import, None, None, name="GemmOptimizer"
      )
      fgraph.attach_feature(u)
      while did_something:
          nb_iter += 1
          t0 = time.perf_counter()
          nodelist = pytensor.graph.basic.io_toposort(
              fgraph.inputs, fgraph.outputs
          )
          time_toposort += time.perf_counter() - t0
          did_something = False
          nodelist.reverse()
          for node in nodelist:
              if not (
                  isinstance(node.op, Elemwise)
                  and isinstance(
                      node.op.scalar_op,
                      (
                          pytensor.scalar.Add,
                          pytensor.scalar.Sub,
                          pytensor.scalar.Neg,
                          pytensor.scalar.Mul,
                      ),
                  )
              ):
                  continue
              if node not in fgraph.apply_nodes:
                  # This mean that we already removed this node from
                  # the graph
                  continue
              try:
                  new_outputs, time1, time2, time3 = _gemm_from_node2(
                      fgraph, node
                  )
                  time_canonicalize += time1
                  time_factor_can += time2
                  time_factor_list += time3
              except InconsistencyError:
                  nb_inconsistency_make += 1
                  continue
              if new_outputs:
                  new_outputs, old_dot22 = new_outputs
                  assert len(new_outputs) == len(node.outputs)
                  new_outputs[
                      0
                  ].tag.values_eq_approx = (
                      values_eq_approx_remove_inf_nan
                  )
                  try:
                      fgraph.replace_all_validate_remove(
                          list(zip(node.outputs, new_outputs)),
                          [old_dot22],
                          reason="GemmOptimizer",
                          # For now we disable the warning as we know case
                          # that we need to fix.
                          warn=False,  # warn=not self.warned
                      )
                      did_something = True
                      nb_replacement += 1
                  except InconsistencyError:
                      # TODO: retry other applications of gemm (see comment
                      # in _gemm_from_node)
                      nb_inconsistency_replace += 1
                  except ReplacementDidNotRemoveError:
                      nb_replacement_didn_t_remove += 1
                      self.warned = True
      fgraph.remove_feature(u)
      if fgraph.profile:
          validate_time = fgraph.profile.validate_time - validate_before
          callback_time = (
              fgraph.execute_callbacks_time - callback_before
          )
          callbacks_time = {}
          for k, v in fgraph.execute_callbacks_times.items():
              if k in callbacks_before:
                  callbacks_time[k] = v - callbacks_before[k]
              else:
                  callbacks_time[k] = v
      else:
          validate_time = None
          callback_time = None
          callbacks_time = {}
  
      return (
          self,
          nb_iter,
          nb_replacement,
          nb_replacement_didn_t_remove,
          nb_inconsistency_make,
          nb_inconsistency_replace,
          time_canonicalize,
          time_factor_can,
          time_factor_list,
          time_toposort,
          validate_time,
          callback_time,
          callbacks_time,
      )
  
imports: import copy
  import logging
  import time
  import numpy
  from pytensor.tensor.rewriting.basic import register_specialize
  import pytensor.scalar
  from pytensor.compile.mode import optdb
  from pytensor.configdefaults import config
  from pytensor.graph.features import ReplacementDidNotRemoveError, ReplaceValidate
  from pytensor.graph.rewriting.basic import EquilibriumGraphRewriter, GraphRewriter, copy_stack_trace, in2out, node_rewriter
  from pytensor.graph.rewriting.db import SequenceDB
  from pytensor.graph.utils import InconsistencyError
  from pytensor.tensor import basic
  from pytensor.tensor.blas import Dot22, _dot22, _dot22scalar, batched_dot, gemm_inplace, gemm_no_inplace, gemv_inplace, gemv_no_inplace, ger, ger_destructive
  from pytensor.tensor.elemwise import DimShuffle, Elemwise
  from pytensor.tensor.exceptions import NotScalarConstantError
  from pytensor.tensor.math import Dot, _matrix_matrix_matmul, add, mul, neg, sub
  from pytensor.tensor.rewriting.elemwise import local_dimshuffle_lift
  from pytensor.tensor.type import DenseTensorType, TensorType, integer_dtypes, values_eq_approx_remove_inf_nan
label_desc: Use image node_id 3 for calling the GemmOptimizer obj's underlying member method code with example usage: obj.apply(fgraph) and returns: self,  nb_iter,  nb_replacement,  nb_replacement_didn_t_remove,  nb_inconsistency_make,  nb_inconsistency_replace,  time_canonicalize,  time_factor_can,  time_factor_list,  time_toposort,  validate_time,  callback_time,  callbacks_time