code: def local_join_subtensors(fgraph, node):
      r"""Simplify contiguous :class:`Subtensor`\s inside a :class:`Join`.
  
      `join((x[:3], x[3:5]), axis=0) -> x[:5]`
      """
      # TODO: Generalize to AdvancedSubtensors
  
      axis, tensors = node.inputs[0], node.inputs[1:]
  
      try:
          axis = get_underlying_scalar_constant_value(axis)
      except NotScalarConstantError:
          return
  
      for subtensor1_idx, (subtensor1, subtensor2) in enumerate(
          zip(tensors[:-1], tensors[1:])
      ):
          # Check that two consecutive Subtensors are operating on the same base tensor
          if not (
              (
                  subtensor1.owner is not None
                  and isinstance(subtensor1.owner.op, Subtensor)
              )
              and (
                  subtensor2.owner is not None
                  and isinstance(subtensor2.owner.op, Subtensor)
              )
              and (
                  subtensor1.owner.inputs[0]
                  is subtensor2.owner.inputs[0]
              )
          ):
              continue
  
          # Check that subtensors have consecutive indexes across the join axis
          idxs_subtensor1 = indices_from_subtensor(
              subtensor1.owner.inputs[1:], subtensor1.owner.op.idx_list
          )
          idxs_subtensor2 = indices_from_subtensor(
              subtensor2.owner.inputs[1:], subtensor2.owner.op.idx_list
          )
          try:
              idxs_axis_subtensor1 = idxs_subtensor1[axis]
              idxs_axis_subtensor2 = idxs_subtensor2[axis]
          except IndexError:
              continue
          if not (
              isinstance(idxs_axis_subtensor1, slice)
              and isinstance(idxs_axis_subtensor2, slice)
          ):
              continue
          start_subtensor1, stop_subtensor1, step_subtensor1 = (
              idxs_axis_subtensor1.start,
              idxs_axis_subtensor1.stop,
              idxs_axis_subtensor1.step,
          )
          start_subtensor2, stop_subtensor2, step_subtensor2 = (
              idxs_axis_subtensor2.start,
              idxs_axis_subtensor2.stop,
              idxs_axis_subtensor2.step,
          )
          if not (
              (
                  stop_subtensor1 is not None
                  and start_subtensor2 is not None
              )
              and (stop_subtensor1 == start_subtensor2)
          ):
              continue
  
          # Check that step is None or 1
          # For non-unit steps (perhaps except for -1) we would need to know the
          # exact values of start and stop to know if they can be merged
          for step in (step_subtensor1, step_subtensor2):
              if step is None:
                  continue
              try:
                  if (
                      get_underlying_scalar_constant_value(
                          step, only_process_constants=True
                      )
                      != 1
                  ):
                      return None
              except NotScalarConstantError:
                  return None
  
          # Check that all other idxs of subtensor are the same
          if all(
              idxs_nonaxis_subtensor1 == idxs_nonaxis_subtensor2
              for i, (
                  idxs_nonaxis_subtensor1,
                  idxs_nonaxis_subtensor2,
              ) in enumerate(zip(idxs_subtensor1, idxs_subtensor2))
              if i != axis
          ):
              base_tensor = subtensor1.owner.inputs[0]
              new_idxs = list(idxs_subtensor1)
              new_idxs[axis] = slice(
                  start_subtensor1, stop_subtensor2, step_subtensor1
              )
              merged_subtensors = base_tensor[new_idxs]
  
              new_joined_tensors = [
                  *tensors[:subtensor1_idx],
                  merged_subtensors,
                  *tensors[subtensor1_idx + 2 :],
              ]
              if len(new_joined_tensors) > 1:
                  return [concatenate(new_joined_tensors, axis=axis)]
              else:
                  return [merged_subtensors]
  
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 31 for calling a global function with example usage: local_join_subtensors(fgraph, node) and returns: list, list, None, None