Skip to content

Taxa & Namespace

Global taxon namespace shared across all alignments and trees.

TaxonTable

Global taxon namespace: maps taxon label (str) to a stable integer index.

Indices are assigned in order of first registration and never change. Backed by a dendropy.TaxonNamespace so trees parsed into this namespace share the same Taxon objects.

Attributes:

Name Type Description
n_taxa int

Number of registered taxa.

labels list[str]

Taxon labels in registration order (index 0, 1, …).

dendropy_namespace TaxonNamespace

Underlying DendroPy namespace; pass to DendroPy tree-reading calls.

Notes
Source code in src/hifuku/taxa.py
class TaxonTable:
    """
    Global taxon namespace: maps taxon label (str) to a stable integer index.

    Indices are assigned in order of first registration and never change.
    Backed by a ``dendropy.TaxonNamespace`` so trees parsed into this
    namespace share the same ``Taxon`` objects.

    Attributes
    ----------
    n_taxa : int
        Number of registered taxa.
    labels : list[str]
        Taxon labels in registration order (index 0, 1, …).
    dendropy_namespace : dendropy.TaxonNamespace
        Underlying DendroPy namespace; pass to DendroPy tree-reading calls.

    Notes
    -----
    - Sukumaran & Holder (2010) : TaxonNamespace API; taxon objects backed by DendroPy.
    """

    def __init__(self) -> None:
        self._ns = dendropy.TaxonNamespace()

    # ------------------------------------------------------------------
    # Registration & lookup

    def require(self, label: str) -> int:
        """Register *label* if absent; return its global index."""
        # dendropy.TaxonNamespace.require_taxon: add-if-absent, return Taxon
        # sukumaran2010dendropy
        taxon = self._ns.require_taxon(label)
        return self._ns.accession_index(taxon)

    def index_of(self, label: str) -> int:
        """Return the global index of *label*; raise KeyError if absent."""
        taxon = self._ns.get_taxon(label)
        if taxon is None:
            raise KeyError(label)
        return self._ns.accession_index(taxon)

    def label_of(self, idx: int) -> str:
        return self._ns[idx].label

    # ------------------------------------------------------------------
    # Properties

    @property
    def n_taxa(self) -> int:
        return len(self._ns)

    @property
    def labels(self) -> list[str]:
        return [t.label for t in self._ns]

    @property
    def dendropy_namespace(self) -> dendropy.TaxonNamespace:
        """Underlying DendroPy namespace; use when reading trees."""
        return self._ns

    # ------------------------------------------------------------------

    def __len__(self) -> int:
        return len(self._ns)

    def __contains__(self, label: str) -> bool:
        return self._ns.get_taxon(label) is not None

    def __repr__(self) -> str:
        return f"TaxonTable({self.n_taxa} taxa)"

dendropy_namespace property

dendropy_namespace: TaxonNamespace

Underlying DendroPy namespace; use when reading trees.

require

require(label: str) -> int

Register label if absent; return its global index.

Source code in src/hifuku/taxa.py
def require(self, label: str) -> int:
    """Register *label* if absent; return its global index."""
    # dendropy.TaxonNamespace.require_taxon: add-if-absent, return Taxon
    # sukumaran2010dendropy
    taxon = self._ns.require_taxon(label)
    return self._ns.accession_index(taxon)

index_of

index_of(label: str) -> int

Return the global index of label; raise KeyError if absent.

Source code in src/hifuku/taxa.py
def index_of(self, label: str) -> int:
    """Return the global index of *label*; raise KeyError if absent."""
    taxon = self._ns.get_taxon(label)
    if taxon is None:
        raise KeyError(label)
    return self._ns.accession_index(taxon)

AlignmentIndex

Per-alignment local ↔ global index map and presence mask.

Parameters:

Name Type Description Default
taxon_table TaxonTable

The shared global TaxonTable. Taxa in labels are registered (added if absent) during construction.

required
labels list[str]

Ordered list of taxon labels for this alignment. The order defines the local index (0 … n_local-1).

required
Notes

The presence mask depends on the total number of global taxa, which may grow after construction. Use :meth:build_mask and pass the current taxon_table.n_taxa to get a correctly-sized mask.

Source code in src/hifuku/taxa.py
class AlignmentIndex:
    """
    Per-alignment local ↔ global index map and presence mask.

    Parameters
    ----------
    taxon_table:
        The shared global TaxonTable. Taxa in *labels* are registered
        (added if absent) during construction.
    labels:
        Ordered list of taxon labels for this alignment. The order
        defines the local index (0 … n_local-1).

    Notes
    -----
    The presence mask depends on the total number of global taxa, which
    may grow after construction. Use :meth:`build_mask` and pass the
    current ``taxon_table.n_taxa`` to get a correctly-sized mask.

    - Sukumaran & Holder (2010) : taxon namespace restriction semantics.
    """

    def __init__(self, taxon_table: TaxonTable, labels: list[str]) -> None:
        if len(labels) != len(set(labels)):
            raise ValueError("Duplicate taxon labels in alignment")

        # Register all taxa; collect global indices in local order
        global_indices = [taxon_table.require(lbl) for lbl in labels]

        self._labels: list[str] = list(labels)
        self._local_to_global: np.ndarray = np.array(global_indices, dtype=np.int32)
        # Reverse mapping: global_idx → local_idx (only for present taxa)
        self._global_to_local: dict[int, int] = {
            g: loc for loc, g in enumerate(global_indices)
        }

    # ------------------------------------------------------------------
    # Mappings

    @property
    def local_to_global(self) -> np.ndarray:
        """Shape (n_local,) int32: maps local index to global index."""
        return self._local_to_global

    def global_to_local(self, global_idx: int) -> int:
        """Return local index for *global_idx*; raise KeyError if absent."""
        return self._global_to_local[global_idx]

    @property
    def n_local(self) -> int:
        return len(self._local_to_global)

    @property
    def labels(self) -> list[str]:
        """Taxon labels in local (alignment) order."""
        return list(self._labels)

    # ------------------------------------------------------------------
    # Mask

    def build_mask(self, n_global: int) -> np.ndarray:
        """
        Return a boolean mask of shape (n_global,) where True = taxon present.

        Pass ``taxon_table.n_taxa`` after all alignments have been registered
        so that the mask has the correct final length.
        """
        mask = np.zeros(n_global, dtype=bool)
        for g in self._local_to_global:
            mask[g] = True
        return mask

    # ------------------------------------------------------------------

    def __len__(self) -> int:
        return self.n_local

    def __repr__(self) -> str:
        return f"AlignmentIndex(n_local={self.n_local}, labels={self._labels})"

local_to_global property

local_to_global: ndarray

Shape (n_local,) int32: maps local index to global index.

labels property

labels: list[str]

Taxon labels in local (alignment) order.

global_to_local

global_to_local(global_idx: int) -> int

Return local index for global_idx; raise KeyError if absent.

Source code in src/hifuku/taxa.py
def global_to_local(self, global_idx: int) -> int:
    """Return local index for *global_idx*; raise KeyError if absent."""
    return self._global_to_local[global_idx]

build_mask

build_mask(n_global: int) -> np.ndarray

Return a boolean mask of shape (n_global,) where True = taxon present.

Pass taxon_table.n_taxa after all alignments have been registered so that the mask has the correct final length.

Source code in src/hifuku/taxa.py
def build_mask(self, n_global: int) -> np.ndarray:
    """
    Return a boolean mask of shape (n_global,) where True = taxon present.

    Pass ``taxon_table.n_taxa`` after all alignments have been registered
    so that the mask has the correct final length.
    """
    mask = np.zeros(n_global, dtype=bool)
    for g in self._local_to_global:
        mask[g] = True
    return mask