class_docstr: Quantizes the layer weights W with the standard Product Quantization
  technique. This learns a codebook of codewords or centroids of size
  block_size from W. For further reference on using PQ to quantize
  neural networks, see "And the Bit Goes Down: Revisiting the Quantization
  of Neural Networks", Stock et al., ICLR 2020.
  
  PQ is performed in two steps:
  (1) The matrix W (weights or fully-connected or convolutional layer)
      is reshaped to (block_size, -1).
          - If W is fully-connected (2D), its columns are split into
            blocks of size block_size.
          - If W is convolutional (4D), its filters are split along the
            spatial dimension.
  (2) We apply the standard EM/k-means algorithm to the resulting reshaped matrix.
  
  Args:
      - W: weight matrix to quantize of size (in_features x out_features)
      - block_size: size of the blocks (subvectors)
      - n_centroids: number of centroids
      - n_iter: number of k-means iterations
      - eps: for cluster reassignment when an empty cluster is found
      - max_tentatives for cluster reassignment when an empty cluster is found
      - verbose: print information after each iteration
  
  Remarks:
      - block_size be compatible with the shape of W
code: def __init__(
      self,
      W,
      block_size,
      n_centroids=256,
      n_iter=20,
      eps=1e-6,
      max_tentatives=30,
      verbose=True,
  ):
      self.block_size = block_size
      W_reshaped = self._reshape(W)
      super(PQ, self).__init__(
          W_reshaped,
          n_centroids=n_centroids,
          n_iter=n_iter,
          eps=eps,
          max_tentatives=max_tentatives,
          verbose=verbose,
      )
  
imports: from .em import EM, EmptyClusterResolveError
label_desc: Use image node_id 1 to create a new PQ object from inherited base classes: EM with example: obj = PQ(W, block_size, n_centroids, n_iter, eps, max_tentatives, verbose)