code: def calculate_marginal_likelihoods(tree, feature, frequencies):
      
      bu_lh_feature = get_personalized_feature_name(feature, BU_LH)
      bu_lh_sf_feature = get_personalized_feature_name(feature, BU_LH_SF)
      td_lh_feature = get_personalized_feature_name(feature, TD_LH)
      td_lh_sf_feature = get_personalized_feature_name(feature, TD_LH_SF)
      lh_feature = get_personalized_feature_name(feature, LH)
      lh_sf_feature = get_personalized_feature_name(feature, LH_SF)
      allowed_state_feature = get_personalized_feature_name(feature, ALLOWED_STATES)
  
      for node in tree.traverse('preorder'):
          likelihood = getattr(node, bu_lh_feature) * getattr(node, td_lh_feature) * frequencies \
                       * getattr(node, allowed_state_feature)
          node.add_feature(lh_feature, likelihood)
          node.add_feature(lh_sf_feature, getattr(node, td_lh_sf_feature) + getattr(node, bu_lh_sf_feature))
  
          node.del_feature(bu_lh_feature)
          node.del_feature(bu_lh_sf_feature)
          node.del_feature(td_lh_feature)
          node.del_feature(td_lh_sf_feature)
docs: Calculates marginal likelihoods for each tree node
      by multiplying state frequencies with their bottom-up and top-down likelihoods.
  
      :param tree: ete3.Tree, the tree of interest
      :param feature: str, character for which the likelihood is calculated
      :param frequencies: numpy array of state frequencies
      :return: void, stores the node marginal likelihoods in the get_personalised_feature_name(feature, LH) feature.