code: def _split_stages(
      node, duplicates=None, aliases=None, stages=None, parents=None
  ):
      """
      Split out all reductions and post reduction scalar operations into seperate
      stacks (stages)
      This leaves remaining in the tree anything not in these categories.
      """
  
      # init data structures
      if duplicates is None:
          duplicates = dict()
          aliases = set()
          stages = list()
          parents = list()
  
      if type(node) is list:
          # don't count assignment node as a parent,
          # it will always exist in the final stage which is processed outside of
          # this function
          if node[0][0] != "assign":
              parents.append(node)
  
          # post order traversal (pulls the stages deepest in the tree first)
          if len(node) > 3:
              _split_stages(
                  node[3], duplicates, aliases, stages, parents
              )
          if len(node) > 4:
              _split_stages(
                  node[4], duplicates, aliases, stages, parents
              )
  
          if len(parents) > 0:
              parents.pop()
  
          if node[0][0] in _reduction_ops:
              red_stack = _process_node(node, aliases, duplicates)
              if red_stack:
                  # add this reduction stack to the stages
                  stages.append(("reduction", red_stack))
  
              # decrement reduction count for all parents
              for parent in parents:
                  parent[2] -= 1
  
              # walk up the parent list
              # TODO: potentially do this iteratively to find longest common set
              # of operations
              scalar_parent = None
              for parent in parents[::-1]:
                  # find the highest parent that is both scalar and has no other
                  # child reductions
                  if parent[1] and parent[2] == 0:
                      scalar_parent = parent
                  else:
                      break
  
              # if there are any scalar operations over this reduction, remove
              # them from the tree as well
              if scalar_parent is not None:
                  scalar_stack = _process_node(
                      scalar_parent, aliases, duplicates
                  )
                  if scalar_stack:
                      # add this scalar stack to the stages
                      stages.append(("scalar", scalar_stack))
  
      return stages
  
imports: from __future__ import division
  from future.utils import native_str
  import os.path
  import re
  import traceback
  import numpy
  from pycuda.tools import context_dependent_memoize
  from pytools import memoize
  from neon import logger
  import neon.backends.nervanagpu
  from neon.backends.util.source_module import SourceModule
  from neon.backends.cuda_templates import _ew_template, _stage_template, _fin_template, _init_rand_func, _init_rand_round_func, _finish_rand_func, _common_kepler, _common_urand_gen, _common_frand, _common_round, _common_fp16_to_fp32, _ew_types, _ew_strings, _is_finite, _float_ops, _reduction_ops
  from neon.backends.cuda_batchnorm import _get_bn_fprop_kernel, _get_bn_bprop_kernel
  from neon.backends.kernels.cuda.lookuptable import _get_lut_bprop_kernel, _get_sorting_kernel
label_desc: Use image node_id 5 for calling a global function with example usage: _split_stages(node, duplicates, aliases, stages, parents) and returns: stages