def _quantize_weight(
        self,
        submod_name: str,
        submodule: _torch.nn.Module,
        submod_config: ModulePostTrainingQuantizerConfig,
        param_name: str,
    ) -> _Optional[_Tuple[_torch.Tensor, _torch.Tensor, _Optional[_torch.Tensor]]]:
        """
        Helper function to perform the quantization on a PyTorch submodule's parameter

        Args:
            submod_name (:obj:`str`): Name of the submodule
            submodule (:obj:`torch.nn.Module`) Submodule which is being quantized
            submod_config (:py:class:`ModulePostTrainingQuantizerConfig`): Config for the submodule
            param_name (:obj:`str`): Name of the parameter within the submodule to quantize

        .. note::
            This function extracts the numpy array out of the torch weight value and
            uses that for performing the quantization
        """

        torch_weight = submodule.get_parameter(param_name)
        weight = torch_weight.numpy()

        block_sizes = [0] * weight.ndim
        assert len(block_sizes) >= 2, "Weight matrix has to be at least 2D or greater"

        if submod_config.granularity == QuantizationGranularity.per_channel:
            blocking_axis = (
                1
                if isinstance(
                    submodule,
                    (
                        _nn.ConvTranspose1d,
                        _nn.ConvTranspose2d,
                        _nn.ConvTranspose3d,
                    ),
                )
                else 0
            )
            block_sizes[blocking_axis] = 1

        elif submod_config.granularity == QuantizationGranularity.per_block:
            updated_config = _validate_param_config(
                submod_name + "." + param_name,
                torch_weight,
                submodule,
                submod_config,
                ["quantization_block_size"],
            )
            if not updated_config:
                _logger.warning(f"Unable to quantize layer {submod_name} - skipping it.")
                return
            block_size_config = list(updated_config.block_size)
            if isinstance(
                submodule,
                (
                    _nn.ConvTranspose1d,
                    _nn.ConvTranspose2d,
                    _nn.ConvTranspose3d,
                ),
            ):
                block_sizes[: len(block_size_config)] = block_size_config[::-1]
            else:
                block_sizes[: len(block_size_config)] = block_size_config

        quantization_mode = self._get_quantization_mode(
            submod_config.weight_dtype, submod_config.quantization_scheme
        )

        ret = self._compute_quantization_params(
            weight=weight,
            nbits=submod_config.weight_n_bits,
            quantization_mode=quantization_mode,
            dtype=submod_config.weight_dtype,
            block_sizes=block_sizes,
            signed=_is_signed_dtype(submod_config.weight_dtype),
        )  # Always used signed dtype range

        if ret is None:
            _logger.warning(f"Unable to quantize layer {submod_name} - skipping it.")
            return

        quant_weight, scale, zp = ret

        dequant_weight = self._dequantize_weight(quant_weight, scale, zp, quantization_mode)

        # Convert back to torch tensors
        dequant_weight = _torch.from_numpy(dequant_weight)
        scale = _torch.from_numpy(scale)
        if zp is not None:
            zp = _torch.from_numpy(zp)

        # Replace the parameter's value
        submodule.get_parameter(param_name).data.copy_(dequant_weight)

        # Register compression metadata
        metadata = self._get_compression_metadata(param_name, submod_config, scale, zp)
        metadata.register(submodule)

        Helper function to perform the quantization on a PyTorch submodule's parameter

        Args:
            submod_name (:obj:`str`): Name of the submodule
            submodule (:obj:`torch.nn.Module`) Submodule which is being quantized
            submod_config (:py:class:`ModulePostTrainingQuantizerConfig`): Config for the submodule
            param_name (:obj:`str`): Name of the parameter within the submodule to quantize

        .. note::
            This function extracts the numpy array out of the torch weight value and
            uses that for performing the quantization
        
coremltools/optimize/torch/quantization/post_training_quantization.py