docs: def get_conditional_probs(self, source=None):
          """Returns the full conditional probabilities table as a numpy array,
          where row i*(k+1) + ly is the conditional probabilities of source i
          emmiting label ly (including abstains 0), conditioned on different
          values of Y, i.e.:
  
              c_probs[i*(k+1) + ly, y] = P(\lambda_i = ly | Y = y)
  
          Note that this simply involves inferring the kth row by law of total
          probability and adding in to mu.
  
          If `source` is not None, returns only the corresponding block.
          """
          c_probs = np.zeros((self.m * (self.k + 1), self.k))
          mu = self.mu.detach().clone().numpy()
  
          for i in range(self.m):
              # si = self.c_data[(i,)]['start_index']
              # ei = self.c_data[(i,)]['end_index']
              # mu_i = mu[si:ei, :]
              mu_i = mu[i * self.k : (i + 1) * self.k, :]
              c_probs[i * (self.k + 1) + 1 : (i + 1) * (self.k + 1), :] = mu_i
  
              # The 0th row (corresponding to abstains) is the difference between
              # the sums of the other rows and one, by law of total prob
              c_probs[i * (self.k + 1), :] = 1 - mu_i.sum(axis=0)
          c_probs = np.clip(c_probs, 0.01, 0.99)
  
          if source is not None:
              return c_probs[source * (self.k + 1) : (source + 1) * (self.k + 1)]
          else:
              return c_probs