Skip to content

registry Module

Index registry mapping genotypes and haplotypes to integer indices.

Overview

The registry subpackage provides IndexRegistry, the bridge between high-level genetic objects and low-level numerical arrays in the simulation engine.

Complete Module Reference

natal.registry

Registry subpackage for stable integer indexing of population entities.

Provides :class:~natal.registry.index.IndexRegistry which assigns and maintains stable integer indices for genotypes, haploid genotypes, and gamete/somatic labels used by the simulation engine.

IndexRegistry

IndexRegistry()

Registry providing stable integer indices for population entities.

The IndexRegistry assigns and stores stable integer indices for entities that occur in the population. Internally it uses flat dict-based lookups: ZType = (genotype, slab_label) for the diploid layer, and GType = (haplogenotype, glab_label) for the gamete layer.

Examples:

ic = IndexRegistry() gid = ic.register_genotype('g1') hid = ic.register_haplogenotype('h1') glid = ic.register_gamete_label('gl1')

Attributes:

Name Type Description
slab_labels List[str]

List of registered somatic (slab) label strings.

glab_labels List[str]

List of registered gamete (glab) label strings.

Initialize an empty IndexRegistry.

All internal mapping dicts and lists start empty; entries are added lazily via the registration methods.

Source code in src/natal/registry/index.py
def __init__(self) -> None:
    """Initialize an empty IndexRegistry.

    All internal mapping dicts and lists start empty; entries are
    added lazily via the registration methods.
    """
    # ---- ZType space (diploid layer primary index) ----
    self._ztype_to_index: Dict[Tuple[Genotype, str], int] = {}
    self._index_to_ztype: List[Tuple[Genotype, str]] = []

    # ---- GType space (gamete layer primary index) ----
    self._gtype_to_index: Dict[Tuple[HaploidGenotype, str], int] = {}
    self._index_to_gtype: List[Tuple[HaploidGenotype, str]] = []

    # ---- Label metadata (ordered lists, replaces old label dicts) ----
    self.slab_labels: List[str] = []
    self.glab_labels: List[str] = []
n_ztypes property
n_ztypes: int

Number of active ZType entries (computed from flat index list).

n_gtypes property
n_gtypes: int

Number of active GType entries (computed from flat index list).

index_to_genotype property
index_to_genotype: List[Genotype]

Computed list of unique genotypes (in registration order) from ZType space.

index_to_ztype property
index_to_ztype: List[Tuple[Genotype, str]]

Computed list of (genotype, slab_label) pairs from ZType space.

The index in this list is the ZType index — the same index consumed by the engine's individual_count arrays on the last axis.

haplo_to_index property
haplo_to_index: Dict[HaploidGenotype, int]

Computed dict of unique haplotypes from the GType space.

Derived from _index_to_gtype so that after compression only surviving haplotypes appear.

index_to_haplo property
index_to_haplo: List[HaploidGenotype]

Computed list of unique haplotypes (in registration order) from GType space.

index_to_gtype property
index_to_gtype: List[Tuple[HaploidGenotype, str]]

Computed list of (haplogenotype, glab_label) pairs from GType space.

The index in this list is the GType index — the same index returned by gtype_index() and consumed by the engine's compressed arrays.

glab_to_index property
glab_to_index: Dict[str, int]

Computed dict mapping gamete label strings to their index in glab_labels.

index_to_glab property
index_to_glab: List[str]

Alias for glab_labels (backward compat).

slab_to_index property
slab_to_index: Dict[str, int]

Computed dict mapping somatic label strings to their index in slab_labels.

index_to_slab property
index_to_slab: List[str]

Alias for slab_labels (backward compat).

register_ztype
register_ztype(genotype: Genotype, slab_label: str) -> int

Register a ZType (genotype, slab) pair and return its index.

O(1) dict lookup for duplicates; appends to flat list for new entries. Automatically tracks slab_labels.

Parameters:

Name Type Description Default
genotype Genotype

A Genotype instance.

required
slab_label str

Somatic label string for this ZType variant.

required

Returns:

Name Type Description
int int

The assigned ZType index. Stable until compression.

Source code in src/natal/registry/index.py
def register_ztype(self, genotype: Genotype, slab_label: str) -> int:
    """Register a ZType (genotype, slab) pair and return its index.

    O(1) dict lookup for duplicates; appends to flat list for new entries.
    Automatically tracks ``slab_labels``.

    Args:
        genotype: A ``Genotype`` instance.
        slab_label: Somatic label string for this ZType variant.

    Returns:
        int: The assigned ZType index.  Stable until compression.
    """
    key = (genotype, slab_label)
    if key in self._ztype_to_index:
        return self._ztype_to_index[key]
    idx = len(self._index_to_ztype)
    self._index_to_ztype.append(key)
    self._ztype_to_index[key] = idx
    if slab_label not in self.slab_labels:
        self.slab_labels.append(slab_label)
    return idx
register_gtype
register_gtype(haplo: HaploidGenotype, glab_label: str) -> int

Register a GType (haplogenotype, glab) pair and return its index.

O(1) dict lookup for duplicates; appends to flat list for new entries. Automatically tracks glab_labels.

Parameters:

Name Type Description Default
haplo HaploidGenotype

A HaploidGenotype instance.

required
glab_label str

Gamete label string for this GType variant.

required

Returns:

Name Type Description
int int

The assigned GType index. Stable until compression.

Source code in src/natal/registry/index.py
def register_gtype(self, haplo: HaploidGenotype, glab_label: str) -> int:
    """Register a GType (haplogenotype, glab) pair and return its index.

    O(1) dict lookup for duplicates; appends to flat list for new entries.
    Automatically tracks ``glab_labels``.

    Args:
        haplo: A ``HaploidGenotype`` instance.
        glab_label: Gamete label string for this GType variant.

    Returns:
        int: The assigned GType index.  Stable until compression.
    """
    key = (haplo, glab_label)
    if key in self._gtype_to_index:
        return self._gtype_to_index[key]
    idx = len(self._index_to_gtype)
    self._index_to_gtype.append(key)
    self._gtype_to_index[key] = idx
    if glab_label not in self.glab_labels:
        self.glab_labels.append(glab_label)
    return idx
register_genotype
register_genotype(genotype_id: Genotype) -> List[int]

Register a genotype and auto-cross-product with all slab labels.

If slab_labels is empty it is auto-initialised to ["default"]. Each (genotype, slab) pair becomes a ZType entry.

Parameters:

Name Type Description Default
genotype_id Genotype

A Genotype instance to register.

required

Returns:

Type Description
List[int]

list[int]: ZType indices for this genotype (one per slab label).

Source code in src/natal/registry/index.py
def register_genotype(self, genotype_id: Genotype) -> List[int]:
    """Register a genotype and auto-cross-product with all slab labels.

    If ``slab_labels`` is empty it is auto-initialised to ``["default"]``.
    Each (genotype, slab) pair becomes a ZType entry.

    Args:
        genotype_id: A ``Genotype`` instance to register.

    Returns:
        list[int]: ZType indices for this genotype (one per slab label).
    """
    if not self.slab_labels:
        self.slab_labels = ["default"]
    indices: list[int] = []
    for slab in self.slab_labels:
        idx = self.register_ztype(genotype_id, slab)
        indices.append(idx)
    return indices
register_haplogenotype
register_haplogenotype(haplo_id: HaploidGenotype) -> list[int]

Register a haplogenotype and auto-cross-product with all glab labels.

If glab_labels is empty it is auto-initialised to ["default"]. Each (haplogenotype, glab) pair becomes a GType entry.

Parameters:

Name Type Description Default
haplo_id HaploidGenotype

A HaploidGenotype instance or opaque key.

required

Returns:

Type Description
list[int]

list[int]: GType indices for this haplotype (one per glab label).

Source code in src/natal/registry/index.py
def register_haplogenotype(self, haplo_id: HaploidGenotype) -> list[int]:
    """Register a haplogenotype and auto-cross-product with all glab labels.

    If ``glab_labels`` is empty it is auto-initialised to ``["default"]``.
    Each (haplogenotype, glab) pair becomes a GType entry.

    Args:
        haplo_id: A ``HaploidGenotype`` instance or opaque key.

    Returns:
        list[int]: GType indices for this haplotype (one per glab label).
    """
    if not self.glab_labels:
        self.glab_labels = ["default"]
    indices: list[int] = []
    for glab in self.glab_labels:
        idx = self.register_gtype(haplo_id, glab)
        indices.append(idx)
    return indices
register_gamete_label
register_gamete_label(gamete_label: str) -> int

Register a gamete label and return its index.

Adapted to use the new glab_labels list.

Parameters:

Name Type Description Default
gamete_label str

String label for gamete origin.

required

Returns:

Name Type Description
int int

Assigned integer index for the gamete label.

Source code in src/natal/registry/index.py
def register_gamete_label(self, gamete_label: str) -> int:
    """Register a gamete label and return its index.

    Adapted to use the new ``glab_labels`` list.

    Args:
        gamete_label: String label for gamete origin.

    Returns:
        int: Assigned integer index for the gamete label.
    """
    if gamete_label not in self.glab_labels:
        self.glab_labels.append(gamete_label)
    return self.glab_labels.index(gamete_label)
register_somatic_label
register_somatic_label(somatic_label: str) -> int

Register a somatic label (slab) and return its index.

Adapted to use the new slab_labels list.

Parameters:

Name Type Description Default
somatic_label str

String label for somatic state.

required

Returns:

Name Type Description
int int

Assigned integer index for the somatic label.

Source code in src/natal/registry/index.py
def register_somatic_label(self, somatic_label: str) -> int:
    """Register a somatic label (slab) and return its index.

    Adapted to use the new ``slab_labels`` list.

    Args:
        somatic_label: String label for somatic state.

    Returns:
        int: Assigned integer index for the somatic label.
    """
    if somatic_label not in self.slab_labels:
        self.slab_labels.append(somatic_label)
    return self.slab_labels.index(somatic_label)
num_genotypes
num_genotypes() -> int

Return the number of unique diploid genotypes (derived from ZTypes).

Returns:

Name Type Description
int int

Count of unique diploid genotypes in the ZType space.

Source code in src/natal/registry/index.py
def num_genotypes(self) -> int:
    """Return the number of unique diploid genotypes (derived from ZTypes).

    Returns:
        int: Count of unique diploid genotypes in the ZType space.
    """
    return len(self.index_to_genotype)
num_haplogenotypes
num_haplogenotypes() -> int

Return the number of unique haploid genotypes (derived from GTypes).

Returns:

Name Type Description
int int

Count of unique haploid genotypes in the GType space.

Source code in src/natal/registry/index.py
def num_haplogenotypes(self) -> int:
    """Return the number of unique haploid genotypes (derived from GTypes).

    Returns:
        int: Count of unique haploid genotypes in the GType space.
    """
    return len(self.index_to_haplo)
ztype_index
ztype_index(genotype: Genotype, slab_label: str) -> int

O(1) dict lookup for a ZType index.

Replaces the formula g * n_slabs + slab.

Parameters:

Name Type Description Default
genotype Genotype

A Genotype instance.

required
slab_label str

Somatic label string.

required

Returns:

Name Type Description
int int

The ZType index.

Raises:

Type Description
KeyError

If the (genotype, slab_label) pair is not registered.

Source code in src/natal/registry/index.py
def ztype_index(self, genotype: Genotype, slab_label: str) -> int:
    """O(1) dict lookup for a ZType index.

    Replaces the formula ``g * n_slabs + slab``.

    Args:
        genotype: A ``Genotype`` instance.
        slab_label: Somatic label string.

    Returns:
        int: The ZType index.

    Raises:
        KeyError: If the (genotype, slab_label) pair is not registered.
    """
    return self._ztype_to_index[(genotype, slab_label)]
resolve_ztype_indices
resolve_ztype_indices(pattern: ZygoteTypePattern) -> list[int]

Return all ZType indices matching a ZygoteTypePattern.

Iterates _index_to_ztype and tests each (genotype, slab_label) pair against pattern. When pattern has no slab constraint (slab is None), all slab variants of matching genotypes match.

Source code in src/natal/registry/index.py
def resolve_ztype_indices(self, pattern: ZygoteTypePattern) -> list[int]:
    """Return all ZType indices matching a ZygoteTypePattern.

    Iterates ``_index_to_ztype`` and tests each (genotype, slab_label)
    pair against *pattern*.  When *pattern* has no slab constraint
    (``slab is None``), all slab variants of matching genotypes match.
    """
    indices: list[int] = []
    for i, (gt, slab) in enumerate(self._index_to_ztype):
        if pattern.matches(gt, slab):
            indices.append(i)
    return indices
resolve_default_ztype_index
resolve_default_ztype_index(pattern: ZygoteTypePattern) -> int

Return the first ZType index matching a ZygoteTypePattern.

Used by initial_state which places individuals in the first (default) slab when no @slab is specified.

Source code in src/natal/registry/index.py
def resolve_default_ztype_index(self, pattern: ZygoteTypePattern) -> int:
    """Return the first ZType index matching a ZygoteTypePattern.

    Used by ``initial_state`` which places individuals in the first
    (default) slab when no ``@slab`` is specified.
    """
    for i, (gt, slab) in enumerate(self._index_to_ztype):
        if pattern.matches(gt, slab):
            return i
    raise KeyError(f"No ZType matches pattern {pattern}")
ztype_indices_for
ztype_indices_for(genotype: Genotype) -> list[int]

Return all ZType indices for a given Genotype object.

Scans _index_to_ztype — does NOT require species.unordered_genotype() because both the stored and input genotypes are already canonicalized via Genotype.__new__ cache key normalization.

Source code in src/natal/registry/index.py
def ztype_indices_for(self, genotype: Genotype) -> list[int]:
    """Return all ZType indices for a given Genotype object.

    Scans ``_index_to_ztype`` — does NOT require ``species.unordered_genotype()``
    because both the stored and input genotypes are already canonicalized
    via ``Genotype.__new__`` cache key normalization.
    """
    return [i for i, (gt, _) in enumerate(self._index_to_ztype) if gt == genotype]
gtype_indices_for
gtype_indices_for(haplo: HaploidGenotype) -> list[int]

Return all GType indices for a given HaploidGenotype object.

Parameters:

Name Type Description Default
haplo HaploidGenotype

A HaploidGenotype instance.

required

Returns:

Type Description
list[int]

List of GType indices for this haplotype (one per glab label).

Source code in src/natal/registry/index.py
def gtype_indices_for(self, haplo: HaploidGenotype) -> list[int]:
    """Return all GType indices for a given HaploidGenotype object.

    Args:
        haplo: A HaploidGenotype instance.

    Returns:
        List of GType indices for this haplotype (one per glab label).
    """
    return [i for i, (hg, _) in enumerate(self._index_to_gtype) if hg == haplo]
gtype_index
gtype_index(haplo: HaploidGenotype, glab_label: str) -> int

O(1) dict lookup for a GType index.

Replaces the formula compress_hg_glab(hg, glab, n_glabs).

Parameters:

Name Type Description Default
haplo HaploidGenotype

A HaploidGenotype instance.

required
glab_label str

Gamete label string.

required

Returns:

Name Type Description
int int

The GType index.

Raises:

Type Description
KeyError

If the (haplo, glab_label) pair is not registered.

Source code in src/natal/registry/index.py
def gtype_index(self, haplo: HaploidGenotype, glab_label: str) -> int:
    """O(1) dict lookup for a GType index.

    Replaces the formula ``compress_hg_glab(hg, glab, n_glabs)``.

    Args:
        haplo: A ``HaploidGenotype`` instance.
        glab_label: Gamete label string.

    Returns:
        int: The GType index.

    Raises:
        KeyError: If the (haplo, glab_label) pair is not registered.
    """
    return self._gtype_to_index[(haplo, glab_label)]
compress
compress(ztype_mask: NDArray[int32], gtype_mask: NDArray[int32]) -> None

Permanently remove pruned ZType and GType entries from the registry.

Both masks use -1 for pruned entries and >=0 for surviving entries (the value is the new compressed index).

Unlike the old formula-based compress, this operates directly on the flat ZType/GType spaces — individual (genotype, slab) ZTypes can be pruned independently.

Parameters:

Name Type Description Default
ztype_mask NDArray[int32]

(old_n_ztypes,) int32 array — ZType-level compression mask (-1 = pruned).

required
gtype_mask NDArray[int32]

(old_n_gtypes,) int32 array — GType-level compression mask (-1 = pruned).

required
Source code in src/natal/registry/index.py
def compress(
    self,
    ztype_mask: NDArray[np.int32],
    gtype_mask: NDArray[np.int32],
) -> None:
    """Permanently remove pruned ZType and GType entries from the registry.

    Both masks use -1 for pruned entries and >=0 for surviving entries
    (the value is the new compressed index).

    Unlike the old formula-based compress, this operates directly on the
    flat ZType/GType spaces — individual (genotype, slab) ZTypes can be
    pruned independently.

    Args:
        ztype_mask: ``(old_n_ztypes,)`` int32 array — ZType-level
            compression mask (-1 = pruned).
        gtype_mask: ``(old_n_gtypes,)`` int32 array — GType-level
            compression mask (-1 = pruned).
    """
    self._compress_ztypes(ztype_mask)
    self._compress_gtypes(gtype_mask)