Skip to content

Tree Metric

The normalized branch-score metric: d^2 = (1-w) CBS^2/C_ref + w RF/R_ref, combining the clade branch score with the Robinson-Foulds count. The chart owns a metric instance built from its anchor set.

CBSMetric dataclass

Normalized branch-score implementation of the TreeMetric protocol.

distance = d = sqrt((1 - w) * CBS^2 / c_ref + w * RF / r_ref): the Kuhner-Felsenstein (1994) clade branch score combined with the Robinson-Foulds topological distance, weighted by w in [0, 1] (0 = pure branch score, 1 = pure topology). c_ref and r_ref normalize the two terms; the defaults 1.0 give the raw branch score at w = 0. Use :meth:for_anchors to derive the normalizers from an anchor set. quality = B_frac in [0, 1]; the length-overlap fraction. Near 0 = branch- score saturation; near 1 = similar trees.

kuhner1994branch, robinson1981rf.

Source code in src/hifuku/metric/branch_score.py
@dataclass
class CBSMetric:
    """
    Normalized branch-score implementation of the TreeMetric protocol.

    ``distance`` = d = sqrt((1 - w) * CBS^2 / c_ref + w * RF / r_ref): the
    Kuhner-Felsenstein (1994) clade branch score combined with the Robinson-Foulds
    topological distance, weighted by ``w`` in [0, 1] (0 = pure branch score,
    1 = pure topology).  ``c_ref`` and ``r_ref`` normalize the two terms; the
    defaults 1.0 give the raw branch score at ``w = 0``.  Use :meth:`for_anchors`
    to derive the normalizers from an anchor set.
    ``quality`` = B_frac in [0, 1]; the length-overlap fraction.  Near 0 = branch-
    score saturation; near 1 = similar trees.

    kuhner1994branch, robinson1981rf.
    """

    w: float = METRIC_W
    c_ref: float = 1.0
    r_ref: float = 1.0

    def __call__(
        self, t1: HifukuTree, t2: HifukuTree
    ) -> tuple[float, float]:
        return _cbs_dist_quality(t1, t2, self.w, self.c_ref, self.r_ref)

    @classmethod
    def for_anchors(cls, anchors, taxon_table=None, w: float = METRIC_W) -> "CBSMetric":
        """
        Build a metric whose normalizers are the mean CBS^2 and mean RF over the
        anchor set.

        ``w`` then means the same across datasets, and every chart built on these
        anchors shares one metric, so the charts stitch into one common frame.
        The anchors must have ``global_leaf_indices`` set.
        """
        c_ref, r_ref = compute_normalizers(anchors, taxon_table)
        return cls(w=w, c_ref=c_ref, r_ref=r_ref)

for_anchors classmethod

for_anchors(anchors, taxon_table=None, w: float = METRIC_W) -> 'CBSMetric'

Build a metric whose normalizers are the mean CBS^2 and mean RF over the anchor set.

w then means the same across datasets, and every chart built on these anchors shares one metric, so the charts stitch into one common frame. The anchors must have global_leaf_indices set.

Source code in src/hifuku/metric/branch_score.py
@classmethod
def for_anchors(cls, anchors, taxon_table=None, w: float = METRIC_W) -> "CBSMetric":
    """
    Build a metric whose normalizers are the mean CBS^2 and mean RF over the
    anchor set.

    ``w`` then means the same across datasets, and every chart built on these
    anchors shares one metric, so the charts stitch into one common frame.
    The anchors must have ``global_leaf_indices`` set.
    """
    c_ref, r_ref = compute_normalizers(anchors, taxon_table)
    return cls(w=w, c_ref=c_ref, r_ref=r_ref)

compute_normalizers

compute_normalizers(anchors, taxon_table=None) -> tuple[float, float]

Metric normalization scales from an anchor set: (C_ref, R_ref).

C_ref is the mean raw CBS^2 over all anchor pairs; R_ref is the mean RF over all anchor pairs, floored at 1. Computed over the whole anchor set, not one triplet, so every chart built on these anchors uses the same metric and the charts stitch into one frame. The anchors must have global_leaf_indices set. taxon_table is accepted for interface symmetry; the anchors are expected to already share a namespace.

Source code in src/hifuku/metric/branch_score.py
def compute_normalizers(anchors, taxon_table=None) -> tuple[float, float]:
    """
    Metric normalization scales from an anchor set: ``(C_ref, R_ref)``.

    ``C_ref`` is the mean raw CBS^2 over all anchor pairs; ``R_ref`` is the mean
    RF over all anchor pairs, floored at 1.  Computed over the whole anchor set,
    not one triplet, so every chart built on these anchors uses the same metric
    and the charts stitch into one frame.  The anchors must have
    ``global_leaf_indices`` set.  ``taxon_table`` is accepted for interface
    symmetry; the anchors are expected to already share a namespace.
    """
    anchors = list(anchors)
    cbs_sqs = []
    rfs = []
    for a, b in itertools.combinations(anchors, 2):
        cbs_sq, rf, _ = _raw_branch_score(a, b)
        cbs_sqs.append(cbs_sq)
        rfs.append(rf)
    if not cbs_sqs:
        return 1.0, 1.0
    c_ref = sum(cbs_sqs) / len(cbs_sqs)
    r_ref = max(sum(rfs) / len(rfs), 1.0)
    if c_ref <= 0.0:
        c_ref = 1.0
    return float(c_ref), float(r_ref)