docs: def _create_w_objective(m, X, Z=None):
      """
      Creates an objective function and its derivative for W, given M and X (data)
  
      Args:
          m (array): genes x clusters
          X (array): genes x cells
          Z (array): zero-inflation parameters - genes x 1
      """
      genes, clusters = m.shape
      cells = X.shape[1]
      nonzeros = (X!=0)
      def objective(w):
          # convert w into a matrix first... because it's a vector for
          # optimization purposes
          w = w.reshape((m.shape[1], X.shape[1]))
          d = m.dot(w)+eps
          # derivative of objective wrt all elements of w
          # for w_{ij}, the derivative is... m_j1+...+m_jn sum over genes minus 
          # x_ij
          temp = X/d
          m_sum = m.T.dot(nonzeros)
          m2 = m.T.dot(temp)
          deriv = m_sum - m2
          return np.sum(nonzeros*(d - X*np.log(d)))/genes, deriv.flatten()/genes
      return objective