def add_upsample(
        self,
        name,
        scaling_factor_h,
        scaling_factor_w,
        input_name,
        output_name,
        mode="NN",
        linear_upsample_mode="DEFAULT",
    ):
        """
        Add an upsample layer to the model.
        Refer to the ``UpsampleLayerParams`` message in the specification
        (NeuralNetwork.proto) for more details.

        Parameters
        ----------
        name: str
            The name of this layer.
        scaling_factor_h: int or float
            Scaling factor on the vertical direction. Float values only
            supported with ``BILINEAR`` and ``ALIGN_CORNERS_*``.
        scaling_factor_w: int or float
            Scaling factor on the horizontal direction. Float values only
            supported with ``BILINEAR`` and ``ALIGN_CORNERS_*``.
        input_name: str
            The input blob name of this layer.
        output_name: str
            The output blob name of this layer.
        mode: str
            Overall interpolation mode. The following values are supported:

            * ``'NN'``: nearest neighbour
            * ``'BILINEAR'``: bilinear interpolation

        linear_upsample_mode: str
            Specifies the behavior for linear upsampling. Only valid when
            Interpolation Mode is ``BILINEAR``.

            If input grid is ``[0, Xin-1]`` (corresponding to an input size of
            ``Xin``), and if the output size is ``Xout``,
            then the grid points are sampled in the following manner:

            'DEFAULT':
                - ``spacing = (Xin-Xin/Xout) / (Xout-1)``
                - ``grid_point[i] = min(Xin-1, max(0, i * spacing)), for i = 0,1,2,..,Xout-1``

            'ALIGN_CORNERS_TRUE':
                - ``spacing = (Xin-1) / (Xout-1)``
                - ``grid_point[i] = min(Xin-1, max(0, i * spacing)), for i = 0,1,2,..,Xout-1``

            'ALIGN_CORNERS_FALSE':
                - ``spacing = Xin / Xout``
                - ``grid_point[i] = min(Xin-1, max(0, i * spacing + 0.5 * spacing - 0.5)), for i = 0,1,2,..,Xout-1``

        See Also
        --------
        add_resize_bilinear

        """

        mode = mode.upper() if isinstance(mode, str) else mode
        linear_upsample_mode = (
            linear_upsample_mode.upper()
            if isinstance(linear_upsample_mode, str)
            else linear_upsample_mode
        )
        if mode not in ["NN", "BILINEAR"]:
            raise ValueError("Unsupported upsampling mode %s" % mode)
        if linear_upsample_mode not in ["DEFAULT", "ALIGN_CORNERS_TRUE", "ALIGN_CORNERS_FALSE"]:
            raise ValueError(
                "Unsupported linear upsampling mode %s" % linear_upsample_mode
            )

        # Default linear upsample mode is backwards compatible, else set spec to iOS14
        if (
            linear_upsample_mode != "DEFAULT"
            and self.spec
            and (
                not self.spec.specificationVersion
                or self.spec.specificationVersion < _SPECIFICATION_VERSION_IOS_14
            )
        ):
            self.spec.specificationVersion = _SPECIFICATION_VERSION_IOS_14

        spec_layer = self._add_generic_layer(name, [input_name], [output_name])
        spec_layer_params = spec_layer.upsample
        if (
            scaling_factor_h - _math_floor(scaling_factor_h) > 0.001
            or scaling_factor_w - _math_floor(scaling_factor_w) > 0.001
        ):
            if mode != "BILINEAR" or linear_upsample_mode not in [
                "ALIGN_CORNERS_TRUE",
                "ALIGN_CORNERS_FALSE",
            ]:
                raise ValueError(
                    "Fractional upsampling only compatible with BILINEAR and ALIGN_CORNERS_TRUE or ALIGN_CORNERS_FALSE"
                )
            spec_layer_params.fractionalScalingFactor.append(float(scaling_factor_h))
            spec_layer_params.fractionalScalingFactor.append(float(scaling_factor_w))
        else:
            spec_layer_params.scalingFactor.append(int(scaling_factor_h))
            spec_layer_params.scalingFactor.append(int(scaling_factor_w))

        spec_layer_params.mode = (
            _proto.NeuralNetwork_pb2.UpsampleLayerParams.InterpolationMode.Value(mode)
        )
        spec_layer_params.linearUpsampleMode = (
            _proto.NeuralNetwork_pb2.UpsampleLayerParams.LinearUpsampleMode.Value(
                linear_upsample_mode
            )
        )

        return spec_layer

        Add an upsample layer to the model.
        Refer to the ``UpsampleLayerParams`` message in the specification
        (NeuralNetwork.proto) for more details.

        Parameters
        ----------
        name: str
            The name of this layer.
        scaling_factor_h: int or float
            Scaling factor on the vertical direction. Float values only
            supported with ``BILINEAR`` and ``ALIGN_CORNERS_*``.
        scaling_factor_w: int or float
            Scaling factor on the horizontal direction. Float values only
            supported with ``BILINEAR`` and ``ALIGN_CORNERS_*``.
        input_name: str
            The input blob name of this layer.
        output_name: str
            The output blob name of this layer.
        mode: str
            Overall interpolation mode. The following values are supported:

            * ``'NN'``: nearest neighbour
            * ``'BILINEAR'``: bilinear interpolation

        linear_upsample_mode: str
            Specifies the behavior for linear upsampling. Only valid when
            Interpolation Mode is ``BILINEAR``.

            If input grid is ``[0, Xin-1]`` (corresponding to an input size of
            ``Xin``), and if the output size is ``Xout``,
            then the grid points are sampled in the following manner:

            'DEFAULT':
                - ``spacing = (Xin-Xin/Xout) / (Xout-1)``
                - ``grid_point[i] = min(Xin-1, max(0, i * spacing)), for i = 0,1,2,..,Xout-1``

            'ALIGN_CORNERS_TRUE':
                - ``spacing = (Xin-1) / (Xout-1)``
                - ``grid_point[i] = min(Xin-1, max(0, i * spacing)), for i = 0,1,2,..,Xout-1``

            'ALIGN_CORNERS_FALSE':
                - ``spacing = Xin / Xout``
                - ``grid_point[i] = min(Xin-1, max(0, i * spacing + 0.5 * spacing - 0.5)), for i = 0,1,2,..,Xout-1``

        See Also
        --------
        add_resize_bilinear

        
coremltools/models/neural_network/builder.py