code: def local_subtensor_lift(fgraph, node):
      """
      unary(x)[idx] -> unary(x[idx])#any broadcast pattern.
  
      Handles the following unary ops:
      elemwise(x,...)[idx] -> elemwise(x[idx],...)
        when x,... are broadcasted scalar or not broadcasted at all
      Unbroadcast(x)[idx] => Unbroadcast(x[idx])
  
      """
      if isinstance(node.op, Subtensor):
          u = node.inputs[0]
          if not u.owner or len(fgraph.clients[u]) > 1:
              return False
  
          if (
              isinstance(u.owner.op, Elemwise)
              and len(u.owner.inputs) == 1
          ):
              idx = node.inputs[1:]
              x_idx = node.op(u.owner.inputs[0], *idx)
              # Copy over previous output stacktrace
              copy_stack_trace(node.outputs, x_idx)
              ret = u.owner.op(x_idx)
              # Copy over previous output stacktrace
              # and stacktrace from previous unary operation
              copy_stack_trace([node.outputs[0], node.inputs[0]], ret)
              return [ret]
  
          if isinstance(u.owner.op, Elemwise):
              new_inputs = []
              if all(
                  sum(i.type.broadcastable) == 0 for i in u.owner.inputs
              ):
                  # There is no broadcastable in the inputs
                  idx = node.inputs[1:]
                  new_inputs = [
                      node.op(i, *idx) for i in u.owner.inputs
                  ]
                  # Copy over previous output stacktrace
                  copy_stack_trace(node.outputs[0], new_inputs)
  
                  ret = u.owner.op(*new_inputs)
                  # Copy over previous output stacktrace
                  # and stacktrace from previous unary operation
                  copy_stack_trace(
                      [node.outputs[0], node.inputs[0]], ret
                  )
                  return [ret]
              elif all(
                  sum(i.type.broadcastable) in [i.ndim, 0]
                  for i in u.owner.inputs
              ):
                  # There is no broadcastable in the inputs or it is scalar
                  idx = node.inputs[1:]
                  new_inputs = []
                  for i in u.owner.inputs:
                      if sum(i.type.broadcastable) == 0:
                          new_inputs.append(node.op(i, *idx))
                      else:
                          # If the subtensor remove some dims, we must
                          # lower the number of dimensions of this scalar.
                          if node.outputs[0].ndim == i.ndim:
                              new_inputs.append(i)
                          else:
                              new_inputs.append(
                                  i.dimshuffle(
                                      ["x"] * node.outputs[0].ndim
                                  )
                              )
  
                  # Copy over previous output stacktrace
                  copy_stack_trace(node.outputs[0], new_inputs)
  
                  ret = u.owner.op(*new_inputs)
                  # Copy over previous output stacktrace
                  # and stacktrace from previous unary operation
                  copy_stack_trace(
                      [node.outputs[0], node.inputs[0]], ret
                  )
                  return [ret]
  
          if isinstance(u.owner.op, Unbroadcast):
              # Subtensor might reduce dim., adapt broadcast pattern accordingly
              old_axes = u.owner.op.axes
              new_axes = []
  
              # loop through indices being subtensor-ed
              # i indexes broadcastable pattern before subtensor
              # j indexes broadcastable pattern after subtensor
              j = 0
              for i, x in enumerate(node.op.idx_list):
                  # if it is not a slice, it will reduce the dimension, should
                  # not appear in the broascastable dimensions
                  if isinstance(x, slice):
                      if i in old_axes:
                          new_axes.append(j)
                      j += 1
              # now keep the broadcastable pattern of all
              # items not appearing in subtensor list
              for i in range(
                  len(node.op.idx_list), len(u.broadcastable)
              ):
                  if i in old_axes:
                      new_axes.append(j)
                  j += 1
  
              subt_x = node.op(u.owner.inputs[0], *node.inputs[1:])
              # Copy over previous output stacktrace
              copy_stack_trace(node.outputs[0], subt_x)
  
              rbcast_subt_x = unbroadcast(subt_x, *new_axes)
              # Copy over previous output stacktrace
              # and stacktrace from previous unary operation
              copy_stack_trace(
                  [node.outputs[0], node.inputs[0]], rbcast_subt_x
              )
  
              return [rbcast_subt_x]
  
imports: import sys
  from collections.abc import Iterable
  import numpy
  import pytensor
  import pytensor.scalar.basic
  from pytensor import compile
  from pytensor.graph.basic import Constant, Variable
  from pytensor.graph.rewriting.basic import WalkingGraphRewriter, copy_stack_trace, in2out, node_rewriter
  from pytensor.raise_op import Assert
  from pytensor.tensor.basic import Alloc, Join, MakeVector, ScalarFromTensor, TensorFromScalar, alloc, as_tensor, cast, concatenate, extract_constant, get_underlying_scalar_constant_value, register_infer_shape, switch
  from pytensor.tensor.blockwise import Blockwise
  from pytensor.tensor.elemwise import Elemwise
  from pytensor.tensor.exceptions import NotScalarConstantError
  from pytensor.tensor.math import Dot, add, and_, ceil_intdiv, dot, eq, ge, gt, le, lt, maximum, minimum, or_
  from pytensor.tensor.math import all
  from pytensor.tensor.rewriting.basic import register_canonicalize, register_specialize, register_stabilize
  from pytensor.tensor.shape import Shape, SpecifyShape, Unbroadcast, shape_padleft, shape_tuple, specify_shape, unbroadcast
  from pytensor.tensor.sharedvar import TensorSharedVariable
  from pytensor.tensor.subtensor import AdvancedIncSubtensor, AdvancedIncSubtensor1, AdvancedSubtensor, AdvancedSubtensor1, IncSubtensor, Subtensor, advanced_inc_subtensor1, advanced_subtensor, advanced_subtensor1, as_index_constant, as_index_literal, get_canonical_form_slice, get_constant_idx, get_idx_list, get_slice_elements, inc_subtensor, indices_from_subtensor
  from pytensor.tensor.type import TensorType
  from pytensor.tensor.type_other import NoneTypeT, SliceConstant, SliceType
  from pytensor.tensor.variable import TensorConstant, TensorVariable
label_desc: Use image node_id 9 for calling a global function with example usage: local_subtensor_lift(fgraph, node) and returns: False, list, list, list, list