code: def local_specify_shape_lift(fgraph, node):
      """Lift SpecifyShape of Elemwise towards the inputs."""
      inp, *shape = node.inputs
      if inp.owner and isinstance(inp.owner.op, Elemwise):
          if len(inp.owner.outputs) != 1:
              return None
  
          elem_inps = inp.owner.inputs
          if len(elem_inps) == 1:
              new_elem_inps = [specify_shape(elem_inps[0], shape)]
          else:
              # Rewrite does not support case where specify_shape provides new broadcastable information,
              # As that may require a specify_shape for each input
              out_broadcastable = node.outputs[0].type.broadcastable
              if out_broadcastable != inp.type.broadcastable:
                  return None
  
              # All non-broadcastable dimensions of inputs must match the non-broadcastbale specify_shape dims
              # We look for a sufficient input to assign all the specify_shape dims
              # We could consider distributing the SpecifyShape across multiple inputs, when none is sufficient
  
              nonbcast_dims = {
                  i
                  for i, (dim, bcast) in enumerate(
                      zip(shape, out_broadcastable)
                  )
                  if (not bcast and not NoneConst.equals(dim))
              }
              new_elem_inps = elem_inps.copy()
              for i, elem_inp in enumerate(elem_inps):
                  if all(
                      bcast_dim is False
                      for dim, bcast_dim in enumerate(
                          elem_inp.type.broadcastable
                      )
                      if dim in nonbcast_dims
                  ):
                      new_elem_inps[i] = specify_shape(elem_inp, shape)
                      break
              else:  # no-break, no sufficient candidate found
                  return None
  
          new_out = inp.owner.op.make_node(*new_elem_inps).outputs
          copy_stack_trace(node.outputs, new_out)
          return new_out
  
imports: import traceback
  from io import StringIO
  from typing import Optional
  from typing import cast
  from warnings import warn
  import numpy
  import pytensor
  from pytensor.configdefaults import config
  from pytensor.graph.basic import Constant, Variable, ancestors, equal_computations
  from pytensor.graph.features import AlreadyThere, Feature
  from pytensor.graph.fg import FunctionGraph
  from pytensor.graph.rewriting.basic import GraphRewriter, check_chain, copy_stack_trace, node_rewriter
  from pytensor.graph.utils import InconsistencyError, get_variable_trace_string
  from pytensor.tensor.basic import MakeVector, as_tensor_variable, cast, constant, extract_constant, get_underlying_scalar_constant_value, register_infer_shape, stack
  from pytensor.tensor.elemwise import DimShuffle, Elemwise
  from pytensor.tensor.exceptions import NotScalarConstantError, ShapeError
  from pytensor.tensor.rewriting.basic import register_canonicalize, register_specialize, register_stabilize, register_useless, topo_constant_folding
  from pytensor.tensor.shape import Reshape, Shape, Shape_i, SpecifyShape, Unbroadcast, shape_i, specify_shape, unbroadcast
  from pytensor.tensor.subtensor import Subtensor, get_idx_list
  from pytensor.tensor.type import TensorType, discrete_dtypes, integer_dtypes
  from pytensor.tensor.type_other import NoneConst
label_desc: Use image node_id 8 for calling a global function with example usage: local_specify_shape_lift(fgraph, node) and returns: new_out, None, None, None