def _is_cast_ops_fusable(self, cast_1: Operation, cast_2: Operation):
        """
        Check if two cast ops can be fused by verifying the consistency between the range and resolution before and after fusion.

        Take the same example shown in ``_propagate_range_resolution``:

            input(fp32) -> cast(dtype="int8") -> cast(dtype="int32")

        The original pattern has output range and resolution ``[-128, 127]``, ``1``.

        However, if the two ``cast`` ops are fused:

            input(fp32) -> cast(dtype="int32")

        The output range becomes the range of int32, which is not ``[-128, 127]``.
        As the result, the fusion is prohibited.
        """
        x_dtype, cast_1_dtype, cast_2_dtype = (
            cast_1.x.dtype,
            string_to_builtin(cast_1.dtype.val),
            string_to_builtin(cast_2.dtype.val),
        )

        ref_range, ref_resolution = self._propagate_range_resolution(
            x_dtype, (cast_1_dtype, cast_2_dtype)
        )
        out_range, out_resolution = self._propagate_range_resolution(x_dtype, (cast_2_dtype,))

        return out_range == ref_range and out_resolution == ref_resolution

        Check if two cast ops can be fused by verifying the consistency between the range and resolution before and after fusion.

        Take the same example shown in ``_propagate_range_resolution``:

            input(fp32) -> cast(dtype="int8") -> cast(dtype="int32")

        The original pattern has output range and resolution ``[-128, 127]``, ``1``.

        However, if the two ``cast`` ops are fused:

            input(fp32) -> cast(dtype="int32")

        The output range becomes the range of int32, which is not ``[-128, 127]``.
        As the result, the fusion is prohibited.
        
coremltools/converters/mil/mil/passes/defs/optimize_repeat_ops.py