docs: def _maximization(X, posterior, force_weights=None):
      """Estimate new centers, weights, and concentrations from
  
      Parameters
      ----------
      posterior : array, [n_centers, n_examples]
          The posterior matrix from the expectation step.
  
      force_weights : None or array, [n_centers, ]
          If None is passed, will estimate weights.
          If an array is passed, will use instead of estimating.
  
      Returns
      ----------
      centers (mu) : array, [n_centers x n_features]
      weights (alpha) : array, [n_centers, ] (alpha)
      concentrations (kappa) : array, [n_centers, ]
      """
      n_examples, n_features = X.shape
      n_clusters, n_examples = posterior.shape
      concentrations = np.zeros((n_clusters,))
      centers = np.zeros((n_clusters, n_features))
      if force_weights is None:
          weights = np.zeros((n_clusters,))
  
      for cc in range(n_clusters):
          # update weights (alpha)
          if force_weights is None:
              weights[cc] = np.mean(posterior[cc, :])
          else:
              weights = force_weights
  
          # update centers (mu)
          X_scaled = X.copy()
          if sp.issparse(X):
              X_scaled.data *= posterior[cc, :].repeat(np.diff(X_scaled.indptr))
          else:
              for ee in range(n_examples):
                  X_scaled[ee, :] *= posterior[cc, ee]
  
          centers[cc, :] = X_scaled.sum(axis=0)
  
          # normalize centers
          center_norm = np.linalg.norm(centers[cc, :])
          if center_norm > 1e-8:
              centers[cc, :] = centers[cc, :] / center_norm
  
          # update concentration (kappa) [TODO: add other kappa approximations]
          rbar = center_norm / (n_examples * weights[cc])
          concentrations[cc] = rbar * n_features - np.power(rbar, 3.)
          if np.abs(rbar - 1.0) < 1e-10:
              concentrations[cc] = MAX_CONTENTRATION
          else:
              concentrations[cc] /= 1. - np.power(rbar, 2.)
  
          # let python know we can free this (good for large dense X)
          del X_scaled
  
      return centers, weights, concentrations