docs: def analyze(self, M_c, T, X_L, X_D, seed, kernel_list=(), n_steps=1, c=(),
                  r=(),
                  max_iterations=-1, max_time=-1, do_diagnostics=False,
                  diagnostics_every_N=1,
                  ROW_CRP_ALPHA_GRID=(),
                  COLUMN_CRP_ALPHA_GRID=(),
                  S_GRID=(), MU_GRID=(),
                  N_GRID=31,
                  do_timing=False,
                  CT_KERNEL=0,
                  progress=None,
                  ):
          """Evolve the latent state by running MCMC transition kernels.
  
          :param seed: The random seed
          :type seed: int
          :param M_c: The column metadata
          :type M_c: dict
          :param T: The data table in mapped representation (all floats, generated
              by data_utils.read_data_objects)
          :param X_L: the latent variables associated with the latent state
          :type X_L: dict
          :param X_D: the particular cluster assignments of each row in each view
          :type X_D: list of lists
          :param kernel_list: names of the MCMC transition kernels to run
          :type kernel_list: list of strings
          :param n_steps: the number of times to run each MCMC transition kernel
          :type n_steps: int
          :param c: the (global) column indices to run MCMC transition kernels on
          :type c: list of ints
          :param r: the (global) row indices to run MCMC transition kernels on
          :type r: list of ints
          :param max_iterations: the maximum number of times ot run each MCMC
              transition kernel. Applicable only if max_time != -1.
          :type max_iterations: int
          :param max_time: the maximum amount of time (seconds) to run MCMC
              transition kernels for before stopping to return progress
          :type max_time: float
          :param progress: a function accepting
              (n_steps, max_time, step_idx, elapsed_secs, end=None) where
              `n_steps` is the total number of transition steps, `max_time` is the
              timeout in secods, `step_idx` is number of transitions so far,
              `elapsed_secs` is the amount of time so far, and `end=None` is an
              optional kwarg for indicating the analysis has been completed.
              For example, `progress` may be used to print a progress bar
              to standard out.
          :type progress: function pointer.
          :returns: X_L, X_D -- the evolved latent state
          """
          if n_steps <= 0:
              raise ValueError("You must do at least one analyze step.")
  
          if CT_KERNEL not in [0, 1]:
              raise ValueError("CT_KERNEL must be 0 (Gibbs) or 1 (MH)")
  
          if do_timing:
              # Diagnostics and timing are exclusive.
              do_diagnostics = False
  
          diagnostic_func_dict, reprocess_diagnostics_func = \
              do_diagnostics_to_func_dict(do_diagnostics)
  
          X_L_list, X_D_list, was_multistate = su.ensure_multistate(X_L, X_D)
  
          arg_tuples = self.get_analyze_arg_tuples(
              M_c,
              T,
              X_L_list,
              X_D_list,
              kernel_list,
              n_steps,
              c,
              r,
              max_iterations,
              max_time,
              diagnostic_func_dict,
              diagnostics_every_N,
              ROW_CRP_ALPHA_GRID,
              COLUMN_CRP_ALPHA_GRID,
              S_GRID,
              MU_GRID,
              N_GRID,
              do_timing,
              CT_KERNEL,
              progress,
              make_get_next_seed(seed))
  
          chain_tuples = self.mapper(self.do_analyze, arg_tuples)
  
          X_L_list, X_D_list, diagnostics_dict_list = zip(*chain_tuples)
  
          if do_timing:
              timing_list = diagnostics_dict_list
  
          if not was_multistate:
              X_L_list, X_D_list = X_L_list[0], X_D_list[0]
          ret_tuple = X_L_list, X_D_list
  
          if diagnostic_func_dict is not None:
              diagnostics_dict = munge_diagnostics(diagnostics_dict_list)
              if reprocess_diagnostics_func is not None:
                  diagnostics_dict = reprocess_diagnostics_func(diagnostics_dict)
              ret_tuple = ret_tuple + (diagnostics_dict, )
  
          if do_timing:
              ret_tuple = ret_tuple + (timing_list, )
  
          return ret_tuple