docs: def reconstruct_ancestral_states(tree, character, states, prediction_method=MPPA, model=F81,
                                   params=None, avg_br_len=None, num_nodes=None, num_tips=None,
                                   force_joint=True):
      """
      Reconstructs ancestral states for the given character on the given tree.
  
      :param character: character whose ancestral states are to be reconstructed.
      :type character: str
      :param tree: tree whose ancestral state are to be reconstructed,
          annotated with the feature specified as `character` containing node states when known.
      :type tree: ete3.Tree
      :param states: possible character states.
      :type states: numpy.array
      :param avg_br_len: (optional) average non-zero branch length for this tree. If not specified, will be calculated.
      :type avg_br_len: float
      :param model: (optional, default is F81) state evolution model to be used by PASTML.
      :type model: str
      :param prediction_method: (optional, default is MPPA) ancestral state prediction method to be used by PASTML.
      :type prediction_method: str
      :param num_nodes: (optional) total number of nodes in the given tree (including tips).
          If not specified, will be calculated.
      :type num_nodes: int
      :param num_tips: (optional) total number of tips in the given tree.
          If not specified, will be calculated.
      :type num_tips: int
      :param params: an optional way to fix some parameters,
          must be in a form {param: value},
          where param can be a state (then the value should specify its frequency between 0 and 1),
          or "scaling factor" (then the value should be the scaling factor for three branches,
          e.g. set to 1 to keep the original branches). Could also be in a form path_to_param_file.
          Only makes sense for ML methods.
      :type params: dict or str
  
      :return: ACR result dictionary whose values depend on the prediction method.
      :rtype: dict
      """
  
      logging.getLogger('pastml').debug('ACR settings for {}:\n\tMethod:\t{}{}.'
                                        .format(character, prediction_method,
                                                '\n\tModel:\t{}'.format(model)
                                                if model and is_ml(prediction_method) else ''))
      if COPY == prediction_method:
          return {CHARACTER: character, STATES: states, METHOD: prediction_method}
      if not num_nodes:
          num_nodes = sum(1 for _ in tree.traverse())
      if not num_tips:
          num_tips = len(tree)
      if is_ml(prediction_method):
          if avg_br_len is None:
              avg_br_len = np.mean(n.dist for n in tree.traverse() if n.dist)
          freqs, sf, kappa = None, None, None
          if params is not None:
              freqs, sf, kappa = _parse_pastml_parameters(params, states)
          return ml_acr(tree=tree, character=character, prediction_method=prediction_method, model=model, states=states,
                        avg_br_len=avg_br_len, num_nodes=num_nodes, num_tips=num_tips, freqs=freqs, sf=sf, kappa=kappa,
                        force_joint=force_joint)
      if is_parsimonious(prediction_method):
          return parsimonious_acr(tree, character, prediction_method, states, num_nodes, num_tips)
  
      raise ValueError('Method {} is unknown, should be one of ML ({}), one of MP ({}) or {}'
                       .format(prediction_method, ', '.join(ML_METHODS), ', '.join(MP_METHODS), COPY))