code: def spatial_dimensions_out_shape(
      pad_type,
      input_shape,
      kernel_shape,
      strides,
      dilations=None,
      custom_pad=None,
      ceil_mode=False,
  ):
      """
      Args
          pad_type: string. Must be one of ('same', 'same_lower', 'valid', 'custom')
  
          input_shape: [iH, iW, ...]: spatial input dims (excluding channels)
              Required iff pad_type in ['same', 'same_lower']
  
          kernel_shape: [kH, kW, ...]: spatial kernel dims (excluding channels)
  
          strides: [sH, sW, ...]: spatial strides (excluding channels)
              Required iff pad_type in ['same', 'same_lower']
  
          dilations: [dH, dW, ...]: dilations (excluding channels)
              If not provided, defaults to [1, 1, ...], effectively no dilation.
  
          custom_pad: Required iff pad_type == 'custom'.
              custom_pad[2*i], custom_pad[2*i+1] are before/after custom padding
              for spatial dim i.
  
          ceil_mode: determines the padding and output shape.
               When ceil mode is True:
                  out_dim = floor((in_dim + pad_l + pad_r - kernel_size + (stride-1)) / stride) + 1
                  if (out_dim-1) * stride >= in_dim + pad_l and (pad_l > 0 or pad_r > 0):
                      out_dim = out_dim - 1
              When ceil mode is False:
                  out_dim = floor((in_dim + pad_l + pad_r - kernel_size) / stride) + 1
  
      Returns:
          A list of spatial output sizes for each spatial dimension of kernel_shape.
  
      """
      num_spatial_dims = len(kernel_shape)
      if dilations is None:
          dilations = [1] * num_spatial_dims
      if custom_pad is None:
          custom_pad = np.array([0] * num_spatial_dims * 2)
      if not (
          len(input_shape)
          == len(kernel_shape)
          == len(strides)
          == len(dilations)
          == len(custom_pad) / 2
      ):
          raise ValueError(
              f"input_shape (length {len(input_shape)}), "
              f"kernel_shape (length {len(kernel_shape)}), "
              f"strides (length {len(strides)}), "
              f"dilations (length {len(dilations)}), "
              f"and custom_pad (length {len(custom_pad)}) divided by two "
              "must all be the same length"
          )
  
      effective_ks = effective_kernel(kernel_shape, dilations)
      if isinstance(strides, np.ndarray):
          strides = tuple(strides.tolist())
      if isinstance(custom_pad, np.ndarray):
          custom_pad = tuple(custom_pad.tolist())
      cache_key = CONV_POOLING_TYPE_INFERENCE_CACHE.get_cache_key(
          input_shape,
          pad_type,
          custom_pad,
          strides,
          effective_ks,
          ceil_mode,
      )
      if cache_key in CONV_POOLING_TYPE_INFERENCE_CACHE:
          return CONV_POOLING_TYPE_INFERENCE_CACHE[cache_key]
  
      pad = aggregated_pad(
          pad_type=pad_type,
          kernel_shape=kernel_shape,
          input_shape=input_shape,
          strides=strides,
          dilations=dilations,
          custom_pad=custom_pad,
      )
  
      out_shape = []
      for r in range(num_spatial_dims):
          # only check if `input_shape` (spatial part of the input image) is symbolic, because:
          # * `input_shape` can be symbolic
          # * `pad` (aggregated from `input_shape` + ...) is symbolic only if `input_shape` is symbolic
          # * `effective_ks` (effective kernel size, determined from kernel size + dilations) cannot be symbolic
          # * strides cannot be symbolic
          if is_symbolic(input_shape[r]):
              out_shape.append(get_new_symbol())
          else:
              out_dim = 0
              if not ceil_mode:
                  out_dim = math.floor(
                      (input_shape[r] + pad[r] - effective_ks[r])
                      / strides[r]
                      + 1
                  )
              else:
                  out_dim = math.floor(
                      (
                          input_shape[r]
                          + pad[r]
                          - effective_ks[r]
                          + strides[r]
                          - 1
                      )
                      / strides[r]
                      + 1
                  )
                  if (out_dim - 1) * strides[r] >= input_shape[r] + pad[
                      r
                  ] / 2 and pad[r] > 0:
                      out_dim = out_dim - 1
              if out_dim <= 0:
                  raise ValueError(
                      f"spatial dimension {r} has invalid output size {out_dim}"
                  )
              out_shape.append(out_dim)
      CONV_POOLING_TYPE_INFERENCE_CACHE[cache_key] = out_shape
      return out_shape
  
imports: import math
  import numbers
  from typing import List, Tuple
  import numpy
  from coremltools.converters.mil.mil import Builder
  from coremltools.converters.mil.mil import Var, get_new_symbol, types
  from coremltools.converters.mil.mil.ops.defs.iOS15.elementwise_unary import cast
  from coremltools.converters.mil.mil.types import builtin_to_string, promote_dtypes
  from coremltools.converters.mil.mil.types.symbolic import is_symbolic
label_desc: Use image node_id 6 for calling a global function with example usage: spatial_dimensions_out_shape(pad_type, input_shape, kernel_shape, strides, dilations, custom_pad, ceil_mode) and returns: out_shape, CONV_POOLING_TYPE_INFERENCE_CACHE