docs: def parsimonious_acr(tree, character, prediction_method, states, num_nodes, num_tips):
      """
      Calculates parsimonious states on the tree and stores them in the corresponding feature.
  
      :param states: numpy array of possible states
      :param prediction_method: str, ACCTRAN (accelerated transformation), DELTRAN (delayed transformation) or DOWNPASS
      :param tree: ete3.Tree, the tree of interest
      :param character: str, character for which the parsimonious states are reconstructed
      :return: dict, mapping between reconstruction parameters and values
      """
      initialise_parsimonious_states(tree, character, states)
      uppass(tree, character)
  
      results = []
      result = {STATES: states, NUM_NODES: num_nodes, NUM_TIPS: num_tips}
  
      logger = logging.getLogger('pastml')
  
      def process_result(method, feature):
          out_feature = get_personalized_feature_name(character, method) if prediction_method != method else character
          res = result.copy()
          res[NUM_SCENARIOS], res[NUM_UNRESOLVED_NODES], res[NUM_STATES_PER_NODE] \
              = choose_parsimonious_states(tree, feature, out_feature)
          res[NUM_STATES_PER_NODE] /= num_nodes
          res[PERC_UNRESOLVED] = res[NUM_UNRESOLVED_NODES] * 100 / num_nodes
          logger.debug('{} node{} unresolved ({:.2f}%) for {} by {}, '
                       'i.e. {:.4f} state{} per node in average.'
                       .format(res[NUM_UNRESOLVED_NODES], 's are' if res[NUM_UNRESOLVED_NODES] != 1 else ' is',
                               res[PERC_UNRESOLVED], character, method,
                               res[NUM_STATES_PER_NODE], 's' if res[NUM_STATES_PER_NODE] > 1 else ''))
          res[CHARACTER] = out_feature
          res[METHOD] = method
          results.append(res)
  
      if prediction_method in {ACCTRAN, MP}:
          feature = get_personalized_feature_name(character, PARS_STATES)
          if prediction_method == MP:
              feature = get_personalized_feature_name(feature, ACCTRAN)
          acctran(tree, character, feature)
          result[STEPS] = get_num_parsimonious_steps(tree, feature)
          process_result(ACCTRAN, feature)
  
          bu_feature = get_personalized_feature_name(character, BU_PARS_STATES)
          for node in tree.traverse():
              if prediction_method == ACCTRAN:
                  node.del_feature(bu_feature)
              node.del_feature(feature)
  
      if prediction_method != ACCTRAN:
          downpass(tree, character, states)
          feature = get_personalized_feature_name(character, PARS_STATES)
          if prediction_method == DOWNPASS:
              result[STEPS] = get_num_parsimonious_steps(tree, feature)
          if prediction_method in {DOWNPASS, MP}:
              process_result(DOWNPASS, feature)
          if prediction_method in {DELTRAN, MP}:
              deltran(tree, character)
              if prediction_method == DELTRAN:
                  result[STEPS] = get_num_parsimonious_steps(tree, feature)
              process_result(DELTRAN, feature)
          for node in tree.traverse():
              node.del_feature(feature)
  
      logger.debug("Parsimonious reconstruction for {} requires {} state changes."
                   .format(character, result[STEPS]))
      return results