docs: def computeOverlap(self):
          """
          Compute estimate of overlap matrix between the states.
  
          Returns
          -------
          result_vals : dictonary
  
          Possible keys in the result_vals dictionary:
  
          'scalar' : np.ndarray, float, shape=(K, K)
              One minus the largest nontrival eigenvalue (largest is 1 or -1)
          'eigenvalues' : np.ndarray, float, shape=(K)
              The sorted (descending) eigenvalues of the overlap matrix.
          'O' : np.ndarray, float, shape=(K, K)
              Estimated state overlap matrix: O[i,j] is an estimate
              of the probability of observing a sample from state i in state j
  
          Notes
          -----
  
          .. code-block:: none
  
              W.T * W \approx \int (p_i p_j /\sum_k N_k p_k)^2 \sum_k N_k p_k dq^N
                  = \int (p_i p_j /\sum_k N_k p_k) dq^N
  
          Multiplying elementwise by N_i, the elements of row i give the probability
          for a sample from state i being observed in state j.
  
  
          Examples
          --------
  
          >>> from pymbar import testsystems
          >>> (x_kn, u_kn, N_k, s_n) = testsystems.HarmonicOscillatorsTestCase().sample(mode='u_kn')
          >>> mbar = MBAR(u_kn, N_k)
          >>> results = mbar.computeOverlap()
  
          """
  
          W = np.matrix(self.getWeights(), np.float64)
          O = np.multiply(self.N_k, W.T * W)
          (eigenvals, eigevec) = linalg.eig(O)
          # sort in descending order
          eigenvals = np.sort(eigenvals)[::-1]
          overlap_scalar = 1 - eigenvals[1] # 1 minus the second largest eigenvalue
  
          results_vals = dict()
          results_vals['scalar'] = overlap_scalar
          results_vals['eigenvalues'] = eigenvals
          results_vals['matrix'] = O
  
          return results_vals