docs: def fit(self, vecs, pq_iter=20, rotation_iter=10, seed=123):
          """Given training vectors, this function alternatively trains
          (a) codewords and (b) a rotation matrix.
          The procedure of training codewords is same as :func:`PQ.fit`.
          The rotation matrix is computed so as to minimize the quantization error
          given codewords (Orthogonal Procrustes problem)
  
          This function is a translation from the original MATLAB implementation to that of python
          http://kaiminghe.com/cvpr13/index.html
  
          If you find the error message is messy, please turn off the verbose flag, then
          you can see the reduction of error for each iteration clearly
  
          Args:
              vecs: (np.ndarray): Training vectors with shape=(N, D) and dtype=np.float32.
              pq_iter (int): The number of iteration for k-means
              rotation_iter (int): The number of iteration for leraning rotation
              seed (int): The seed for random process
  
          Returns:
              object: self
  
          """
          assert vecs.dtype == np.float32
          assert vecs.ndim == 2
          _, D = vecs.shape
          self.R = np.eye(D, dtype=np.float32)
  
          for i in range(rotation_iter):
              print("OPQ rotation training: {} / {}".format(i, rotation_iter))
              X = vecs @ self.R
  
              # (a) Train codewords
              pq_tmp = PQ(M=self.M, Ks=self.Ks, verbose=self.verbose)
              if i == rotation_iter - 1:
                  # In the final loop, run the full training
                  pq_tmp.fit(X, iter=pq_iter, seed=seed)
              else:
                  # During the training for OPQ, just run one-pass (iter=1) PQ training
                  pq_tmp.fit(X, iter=1, seed=seed)
  
              # (b) Update a rotation matrix R
              X_ = pq_tmp.decode(pq_tmp.encode(X))
              U, s, V = np.linalg.svd(vecs.T @ X_)
              print("==== Reconstruction error:", np.linalg.norm(X - X_, 'fro'), "====")
              if i == rotation_iter - 1:
                  self.pq = pq_tmp
                  break
              else:
                  self.R = U @ V
  
          return self