code: def set_multiarray_ndshape_range(
      spec, feature_name, lower_bounds, upper_bounds
  ):
      """
      Annotate an input or output MLMultiArray feature in a Neural Network spec
      to accommodate a range of shapes.
      This is different from "update_multiarray_shape_range", which works with rank 5
      SBCHW mapping.
  
      :param spec: MLModel
          The MLModel spec containing the feature
  
      :param feature_name: str
          The name of the feature for which to add shape range
          information. If the feature is not found in the input or output
          descriptions then an exception is thrown
  
      :param lower_bounds: List[int]
          list of integers specifying the lower bounds of each dimension.
          Length must be same as the rank (length of shape) of the feature_name.
  
      :param upper_bounds: List[int]
          list of integers specifying the upper bounds of each dimension.
          -1 corresponds to unbounded range.
          Length must be same as the rank (length of shape) of the feature_name.
  
  
      Examples
      --------
      .. sourcecode:: python
  
          >>> import coremltools
          >>> from coremltools.models.neural_network import flexible_shape_utils
          >>> spec = coremltools.utils.load_spec('mymodel.mlmodel')
          >>> # say, the default shape of "my_multiarray_featurename" is (2,3)
          >>> flexible_shape_utils.set_multiarray_ndshape_range(spec, feature_name='my_multiarray_featurename', lower_bounds=[1,2], upper_bounds=[10,-1])
  
      :return:
          None. The spec is updated
      """
      if not isinstance(lower_bounds, list):
          raise Exception("lower_bounds must be a list")
      if not isinstance(upper_bounds, list):
          raise Exception("upper_bounds must be a list")
  
      feature = _get_feature(spec, feature_name)
  
      if feature.type.WhichOneof("Type") != "multiArrayType":
          raise Exception(
              "Trying to update shape range for "
              "a non-multiArray feature type"
          )
  
      shape = feature.type.multiArrayType.shape
  
      if len(shape) != len(lower_bounds):
          raise Exception(
              "Length of lower_bounds is not equal to the number of dimensions in the default shape"
          )
      if len(shape) != len(upper_bounds):
          raise Exception(
              "Length of upper_bounds is not equal to the number of dimensions in the default shape"
          )
  
      feature.type.multiArrayType.ClearField("ShapeFlexibility")
  
      for i in range(len(lower_bounds)):
          if shape[i] < lower_bounds[i]:
              raise Exception(
                  "Default shape in %d-th dimension, which is %d, is smaller"
                  " than the lower bound of %d"
                  % (i, int(shape[i]), lower_bounds[i])
              )
          if upper_bounds[i] != -1:
              if shape[i] > upper_bounds[i]:
                  raise Exception(
                      "Default shape in %d-th dimension, which is %d, is greater"
                      " than the upper bound of %d"
                      % (i, int(shape[i]), upper_bounds[i])
                  )
  
          s = feature.type.multiArrayType.shapeRange.sizeRanges.add()
          s.lowerBound = lower_bounds[i]
          s.upperBound = upper_bounds[i]
  
      # Bump up specification version
      spec.specificationVersion = max(
          _MINIMUM_NDARRAY_SPEC_VERSION, spec.specificationVersion
      )
  
imports: from ..utils import _get_feature, _get_nn_layers, _get_input_names
  from ...None import _MINIMUM_FLEXIBLE_SHAPES_SPEC_VERSION
  from ...None import _MINIMUM_NDARRAY_SPEC_VERSION
label_desc: Use image node_id 5 for calling a global function with example usage: set_multiarray_ndshape_range(spec, feature_name, lower_bounds, upper_bounds) without return types