def _convert_to_sub_byte_dtype(data: np.ndarray, sub_byte_dtype: type) -> np.ndarray:
    """Convert data to a specific sub-byte dtype, including shift between signed and unsigned range."""
    if not np.issubdtype(data.dtype, np.integer):
        raise ValueError("Input data must be integer.")
    if not types.is_sub_byte(sub_byte_dtype):
        raise ValueError("Target dtype must be a sub-byte dtype.")

    original_signed = np.issubdtype(data.dtype, np.signedinteger)
    target_signed = not sub_byte_dtype.is_unsigned()
    if original_signed != target_signed:
        shift = 2 ** (sub_byte_dtype.get_bitwidth() - 1)
        if original_signed:
            data += shift
        else:
            data -= shift

    dtype_range = types.type_mapping.builtin_to_range(sub_byte_dtype)
    if np.max(data) > dtype_range.high:
        raise ValueError(
            f"Data has element {np.max(data)}, which is larger than the lower-bound {dtype_range.high}"
        )
    if np.min(data) < dtype_range.low:
        raise ValueError(
            f"Data has element {np.min(data)}, which is smaller than the lower-bound {dtype_range.low}"
        )

    return data.astype(types.nptype_from_builtin(sub_byte_dtype))
Convert data to a specific sub-byte dtype, including shift between signed and unsigned range.
coremltools/converters/mil/mil/ops/tests/iOS18/test_compression.py