docs: def lightlda_estimate_state(data, k, input_folder="data1/LightLDA_input", threads=8, max_iters=250, prepare_data=True, init_means=None, init_weights=None, lightlda_folder=None, data_capacity=1000):
      """
      Runs LDA on the given dataset (can be an 2-D array of any form - sparse
      or dense, as long as it can be indexed). If the data has not already been
      prepared into LDA format, set "prepare_data" to TRUE. If "prepare_data" is
      FALSE, the method assumes that the data has already been preprocessed into
      LightLDA format and is located at the given "input_folder".
      """
      if lightlda_folder is None:
          lightlda_folder = LIGHTLDA_FOLDER
      if prepare_data:
          prepare_lightlda_data(data, input_folder, lightlda_folder)
  
      # Check if initializations for M/W were provided.
      if ((init_means is not None) and (init_weights is None)) or ((init_means is None) and (init_weights is not None)):
          raise ValueError("LightLDA requires that either both M and W be initialized, or neither. You initialized one but not the other.")
      
      warm_start = False
  
      # If we have initial M/W matrices, write to the model and doc-topic files
      if (init_means is not None) and (init_weights is not None):
          warm_start = True
          init_means = init_means/init_means.sum(0)
          init_weights = init_weights/init_weights.sum(0)
          create_model_file("server_0_table_0.model", init_means)
          create_model_file("doc_topic.0", init_weights.T)
          print(init_means)
          print("init_means")
  
      # Run LightLDA
      print("TRAINING")
      # TODO: argument for data capacity
      train_args = (os.path.join(lightlda_folder, "bin/lightlda"), "-num_vocabs", str(data.shape[0]), "-num_topics",
                    str(k), "-num_iterations", str(max_iters), "-alpha", "0.05", "-beta", "0.01", "-mh_steps", "2",
                    "-num_local_workers", str(threads), "-num_blocks", "1", "-max_num_document", str(data.shape[1]),
                    "-input_dir", input_folder, "-data_capacity", str(data_capacity))
      if warm_start:
          print("warm start")
          train_args = train_args + ("-warm_start",)
  
      # Call LightLDA
      subprocess.call(train_args)
      
      # Parse final model and doc-topic files to obtain M/W
      print("data shape")
      print(data.shape)
  
      M = parse_model_file("server_0_table_0.model", k, data.shape[0])
      W = parse_result_file("doc_topic.0", k)
      
      # Not sure if normalization is correct
      M = M * (np.mean(data) / np.mean(M))
      W = W/W.sum(0)
      print("shapes")
      print(M.shape)
      print(W.shape)
      # TODO: poisson_objective doesn't work for sparse matrices
      if sparse.issparse(data):
          ll = 0
      else:
          ll = poisson_objective(data, M, W)
      #M = M * (5./np.mean(M))
      return M, W, ll