def _construct_constexpr_dequant_op(
    quantized_weights: np.ndarray,
    zero_point: Optional[Union[Var, np.ndarray, np.generic]],
    scale: Union[Var, np.ndarray, np.generic],
    axis: Optional[Union[Var, int]] = None,
    name: Optional[str] = None,
    before_op: Optional[Operation] = None,
) -> Var:
    """
    Constructs the constexpr op to represent the quantized weight.

    Use constexpr_affine_dequantize for pre-iOS18 and constexpr_blockwise_shift_scale for others.
    """
    if not is_current_opset_version_compatible_with(target.iOS18):
        # The constexpr_affine_dequantize op requires axis.
        if axis is None:
            # Infer the axis based on scale's shape.
            non_single_dim = [dim for dim, dim_size in enumerate(scale.shape) if dim_size > 1]
            if len(non_single_dim) > 2:
                raise ValueError(
                    "The constexpr_affine_dequantize op doesn't support scale which "
                    "have more than one non-single dimensions. Got scale with shape "
                    f"{scale.shape}"
                )
            # Empty non_single_dim means per-tensor quantization, just use a dummy axis.
            axis = 0 if len(non_single_dim) == 0 else non_single_dim[0]
        if isinstance(axis, int):
            axis = np.int32(axis)

        # The constexpr_affine_dequantize op requires zero_point.
        if zero_point is None:
            zero_point = np.zeros_like(scale).astype(quantized_weights.dtype)

        # The constexpr_affine_dequantize op requires scale and zero_point to have rank 0 or 1.
        if isinstance(scale, (np.ndarray, np.generic)):
            scale = np.squeeze(scale)
        if isinstance(zero_point, (np.ndarray, np.generic)):
            zero_point = np.squeeze(zero_point)
        if len(scale.shape) > 1 or len(zero_point.shape) > 1:
            raise ValueError(
                "The more fine-grained quantization (such as blockwise) is only supported since iOS18."
                "Please set minimum_deployment_target to iOS18 for using it."
            )

        kwargs = {
            "quantized_data": quantized_weights,
            "zero_point": zero_point,
            "scale": scale,
            "axis": axis,
        }
        if name is not None:
            kwargs["name"] = name
        if before_op is not None:
            kwargs["before_op"] = before_op
        return mb.constexpr_affine_dequantize(**kwargs)

    # For iOS18 constexpr_blockwise_shift_scale op, the data/scale/offset need to have same rank.
    if len(quantized_weights.shape) != len(scale.shape):
        if axis is not None:
            target_shape = [1] * len(quantized_weights.shape)
            target_shape[axis] = quantized_weights.shape[axis]
        else:
            target_shape = list(scale.shape) + [1] * (
                len(quantized_weights.shape) - len(scale.shape)
            )
        if np.prod(scale.shape) != np.prod(target_shape):
            raise ValueError(
                "Unable to infer scale's shape. Please provide a scale that has the "
                "same rank as the weight."
            )
        scale = scale.reshape(target_shape)

    # Check the value range to determine the true data type (such as int4/uint4).
    sub_byte_type = (
        types.uint4
        if types.numpy_type_to_builtin_type(quantized_weights.dtype).is_unsigned()
        else types.int4
    )
    sub_byte_range = types.type_mapping._TYPES_TO_RANGE[sub_byte_type]
    if (
        np.max(quantized_weights) <= sub_byte_range.high
        and np.min(quantized_weights) >= sub_byte_range.low
    ):
        quantized_weights = quantized_weights.astype(types.nptype_from_builtin(sub_byte_type))

    kwargs = {
        "data": quantized_weights,
        "scale": scale,
    }
    if zero_point is not None and np.any(zero_point):
        # Only pass the offset parameter when not all elements in `zero_point` are zeroes.
        zero_point = zero_point.reshape(scale.shape)
        # When zero_point is integer, it's required to have the same dtype as the quantized weight.
        if np.issubdtype(zero_point.dtype, np.integer):
            zero_point = zero_point.astype(quantized_weights.dtype)
        kwargs["offset"] = zero_point
    if name is not None:
        kwargs["name"] = name
    if before_op is not None:
        kwargs["before_op"] = before_op
    return mb.constexpr_blockwise_shift_scale(**kwargs)

    Constructs the constexpr op to represent the quantized weight.

    Use constexpr_affine_dequantize for pre-iOS18 and constexpr_blockwise_shift_scale for others.
    
coremltools/converters/mil/frontend/_utils.py