def quantize_weights(
    full_precision_model, nbits, quantization_mode="linear", sample_data=None, **kwargs
):
    """
    Utility function to convert a full precision (float) MLModel to a
    nbit quantized MLModel (float16).

    full_precision_model: MLModel
        Model which will be converted to half precision. Currently conversion
        for only neural network models is supported. If a pipeline model is
        passed in then all embedded neural network models embedded within
        will be converted.

    nbits: int
        Number of bits per quantized weight. Only 16-bit float point and
            1-8 bit is supported

    quantization_mode: str
        One of the following:

        "linear":
            Linear quantization with scale and bias assuming the range of weight
            values is [A, B], where A = min(weight), B = max(weight)
        "linear_lut":
            Simple linear quantization represented as a lookup table
        "kmeans_lut":
            LUT based quantization, where LUT is generated by K-Means clustering
        "custom_lut":
            LUT quantization where LUT and quantized weight params are
            calculated using a custom function. If this mode is selected then
            a custom function must be passed in kwargs with key lut_function.
            The function must have input params (nbits, wp) where nbits is the
            number of quantization bits and wp is the list of weights for a
            given layer. The function should return two parameters (lut, qw)
            where lut is an array of length (2^n bits)containing LUT values and
            qw is the list of quantized weight parameters. See
            ``_get_linear_lookup_table_and_weight`` for a sample implementation.
        "linear_symmetric":
            Linear quantization with scale and bias assuming the range of weight
            values is [-A, A], where A = max(abs(weight)).

    sample_data: str | [dict]
        Data used to characterize performance of the quantized model in
        comparison to the full precision model. Either a list of sample input
        dictionaries or an absolute path to a directory containing images.
        Path to a directory containing images is only valid for models with
        one image input. For all other models a list of sample inputs must be
        provided.

    kwargs: keyword arguments
            *lut_function* : (``callable function``)
                A callable function provided when quantization mode is set to
                ``_ct.models._QUANTIZATION_MODE_CUSTOM_LOOKUP_TABLE``. See ``quantization_mode``
                for more details.
            *selector*: QuantizedLayerSelector
                A QuanatizedLayerSelector object that can be derived to provide
                custom quantization selection.

    Returns
    -------
    model: MLModel
        The quantized MLModel instance if running on macOS 10.14 or later,
        otherwise the quantized model specification is returned

    Examples
    --------
    .. sourcecode:: python

        import coremltools
        from coremltools.models.neural_network import quantization_utils

        model = coremltools.models.MLModel("my_model.mlmodel")
        quantized_model = quantization_utils.quantize_weights(model, 8, "linear")
    """

    qmode_mapping = {
        "linear": _ct.models._QUANTIZATION_MODE_LINEAR_QUANTIZATION,
        "kmeans": _ct.models._QUANTIZATION_MODE_LOOKUP_TABLE_KMEANS,
        "kmeans_lut": _ct.models._QUANTIZATION_MODE_LOOKUP_TABLE_KMEANS,
        "linear_lut": _ct.models._QUANTIZATION_MODE_LOOKUP_TABLE_LINEAR,
        "custom_lut": _ct.models._QUANTIZATION_MODE_CUSTOM_LOOKUP_TABLE,
        "dequantization": _ct.models._QUANTIZATION_MODE_DEQUANTIZE,
        "linear_symmetric": _ct.models._QUANTIZATION_MODE_LINEAR_SYMMETRIC,
    }
    try:
        qmode = qmode_mapping[quantization_mode]
    except KeyError:
        # kmeans is deprecated. Instead kmeans_lut is used. No need to show it.
        del qmode_mapping["kmeans"]
        raise Exception(
            "Invalid quantization mode. Quantization mode must be "
            "one of {}".format(qmode_mapping)
        )

    print("Quantizing using {} quantization".format(quantization_mode))
    spec = full_precision_model.get_spec()
    if nbits == 16 and spec.isUpdatable:
        raise Exception("updatable models cannot get quantized to FP16.")

    qspec = _quantize_spec_weights(spec, nbits, qmode, **kwargs)
    quantized_model = _get_model(qspec, compute_units=full_precision_model.compute_unit)

    if _macos_version() >= (10, 14) and sample_data:
        compare_models(full_precision_model, quantized_model, sample_data)

    return quantized_model

    Utility function to convert a full precision (float) MLModel to a
    nbit quantized MLModel (float16).

    full_precision_model: MLModel
        Model which will be converted to half precision. Currently conversion
        for only neural network models is supported. If a pipeline model is
        passed in then all embedded neural network models embedded within
        will be converted.

    nbits: int
        Number of bits per quantized weight. Only 16-bit float point and
            1-8 bit is supported

    quantization_mode: str
        One of the following:

        "linear":
            Linear quantization with scale and bias assuming the range of weight
            values is [A, B], where A = min(weight), B = max(weight)
        "linear_lut":
            Simple linear quantization represented as a lookup table
        "kmeans_lut":
            LUT based quantization, where LUT is generated by K-Means clustering
        "custom_lut":
            LUT quantization where LUT and quantized weight params are
            calculated using a custom function. If this mode is selected then
            a custom function must be passed in kwargs with key lut_function.
            The function must have input params (nbits, wp) where nbits is the
            number of quantization bits and wp is the list of weights for a
            given layer. The function should return two parameters (lut, qw)
            where lut is an array of length (2^n bits)containing LUT values and
            qw is the list of quantized weight parameters. See
            ``_get_linear_lookup_table_and_weight`` for a sample implementation.
        "linear_symmetric":
            Linear quantization with scale and bias assuming the range of weight
            values is [-A, A], where A = max(abs(weight)).

    sample_data: str | [dict]
        Data used to characterize performance of the quantized model in
        comparison to the full precision model. Either a list of sample input
        dictionaries or an absolute path to a directory containing images.
        Path to a directory containing images is only valid for models with
        one image input. For all other models a list of sample inputs must be
        provided.

    kwargs: keyword arguments
            *lut_function* : (``callable function``)
                A callable function provided when quantization mode is set to
                ``_ct.models._QUANTIZATION_MODE_CUSTOM_LOOKUP_TABLE``. See ``quantization_mode``
                for more details.
            *selector*: QuantizedLayerSelector
                A QuanatizedLayerSelector object that can be derived to provide
                custom quantization selection.

    Returns
    -------
    model: MLModel
        The quantized MLModel instance if running on macOS 10.14 or later,
        otherwise the quantized model specification is returned

    Examples
    --------
    .. sourcecode:: python

        import coremltools
        from coremltools.models.neural_network import quantization_utils

        model = coremltools.models.MLModel("my_model.mlmodel")
        quantized_model = quantization_utils.quantize_weights(model, 8, "linear")
    
coremltools/models/neural_network/quantization_utils.py