def activate_int8_int8_matrix_multiplications(spec, selector=None):
    """
    Utility function that takes in either a full precision (float) spec or
    an nbit quantized spec to selectively enable int8 activation + weight quantization
    of matrix multiplication operations where the second matrix represents a constant weight.

    spec: MLModel.get_spec()
        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 modified.

    selector: (optional) MatrixMultiplyLayerSelector
        A MatrixMultiplyLayerSelector object that enables int8 activation + weight quantization
        only on those layers for which the user-specified criterion on the minimum/maximum number
        of size/channels in constant weight parameters is met.
        It can also be derived to provide custom selection.

    """

    # Recursively convert all pipeline models
    if spec.WhichOneof("Type") == "pipeline":
        for model_spec in spec.pipeline.models:
            activate_int8_int8_matrix_multiplications(model_spec, selector=selector)
        return spec

    elif spec.WhichOneof("Type") in ["pipelineClassifier", "pipelineRegressor"]:
        activate_int8_int8_matrix_multiplications(spec.pipeline, selector=selector)
        return spec

    # Neural network models
    elif spec.WhichOneof("Type") in [
        "neuralNetwork",
        "neuralNetworkClassifier",
        "neuralNetworkRegressor",
    ]:

        if selector is None:
            selector = MatrixMultiplyLayerSelector()

        # Dequantize all the selected matrix multiplication layers
        spec = _quantize_spec_weights(
            spec,
            nbits=None,
            quantization_mode=_ct.models._QUANTIZATION_MODE_DEQUANTIZE,
            selector=selector,
        )

        def _quantized_weight_and_scale(W):
            W_max = max(_np.abs(_np.min(W)), _np.abs(_np.max(W)))
            W_normalized = W / W_max  # [-1,1]
            W_quantized_int8 = 127.0 * W_normalized  # [-127, 127]
            W_quantized_int8 = W_quantized_int8.astype(_np.int8)
            quant_scale = W_max / 127.0
            return W_quantized_int8, quant_scale

        if spec.WhichOneof("Type") == "neuralNetwork":
            nn_spec = spec.neuralNetwork

        elif spec.WhichOneof("Type") in "neuralNetworkClassifier":
            nn_spec = spec.neuralNetworkClassifier

        elif spec.WhichOneof("Type") in "neuralNetworkRegressor":
            nn_spec = spec.neuralNetworkRegressor

        def _process_nn_layers(nn_spec):
            layers = nn_spec.layers

            # Replacing each matrix multiplication
            for layer in layers:
                layer_type = layer.WhichOneof("layer")
                if not selector.do_quantize(layer):
                    continue

                if layer_type == "branch":
                    _process_nn_layers(layer.branch.ifBranch)
                    _process_nn_layers(layer.branch.elseBranch)

                elif layer_type == "loop":
                    _process_nn_layers(layer.loop.conditionNetwork)
                    _process_nn_layers(layer.loop.bodyNetwork)

                elif layer_type in ["innerProduct", "batchedMatmul"]:
                    # Bump up to appropriate spec version if at least one replacement occurs
                    spec.specificationVersion = max(
                        _SPECIFICATION_VERSION_IOS_14, spec.specificationVersion,
                    )

                    # InnerProduct
                    if layer_type == "innerProduct":
                        matmul_layer = layer.innerProduct

                    # BatchedMatmul
                    elif layer_type == "batchedMatmul":
                        matmul_layer = layer.batchedMatmul

                    wp = matmul_layer.weights

                    if len(wp.floatValue) == 0:
                        continue
                    else:
                        qw, qs = _quantized_weight_and_scale(wp.floatValue)

                    print(
                        "Modifying layer {} with size of weights {}, to use Int8 * Int8 matrix multiplication".format(
                            layer.name, qw.size
                        )
                    )

                    matmul_layer.int8DynamicQuantize = True
                    wp.quantization.numberOfBits = 8
                    wp.quantization.linearQuantization.scale.extend(map(float, [qs]))
                    wp.int8RawValue = bytes()
                    wp.int8RawValue += qw.tobytes()
                    del wp.floatValue[:]

        _process_nn_layers(nn_spec)

        return spec

    else:
        raise ValueError("Model Type {} not supported.".format(spec.WhichOneof("Type")))

    Utility function that takes in either a full precision (float) spec or
    an nbit quantized spec to selectively enable int8 activation + weight quantization
    of matrix multiplication operations where the second matrix represents a constant weight.

    spec: MLModel.get_spec()
        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 modified.

    selector: (optional) MatrixMultiplyLayerSelector
        A MatrixMultiplyLayerSelector object that enables int8 activation + weight quantization
        only on those layers for which the user-specified criterion on the minimum/maximum number
        of size/channels in constant weight parameters is met.
        It can also be derived to provide custom selection.

    
coremltools/models/neural_network/quantization_utils.py