Source code for mixle.data.partition

"""Structure-aware partitioning -- one place that splits a dataset into chunks while honoring its
:class:`~mixle.data.structure.SampleStructure`.

This replaces the inline striding in ``seq_encode`` (``data[i::C]``). For strideable structures
(IID / exchangeable / sequential-records) it reproduces that striding exactly, so the existing fast path
is bit-identical. For ``PARTIALLY_EXCHANGEABLE`` data it strides at the *group* level -- every record of
a group lands in the same partition -- so a hierarchical model never sees a group split across chunks.
"""

from __future__ import annotations

from collections.abc import Sequence
from typing import Any

from mixle.data.structure import EXCHANGEABLE, SampleStructure


[docs] def partition_records(records: Sequence[Any], structure: SampleStructure, n: int) -> list[list[Any]]: """Split ``records`` into ``n`` partitions respecting ``structure``. Strideable structures give ``records[k::n]`` (identical to the historical chunking); a partially-exchangeable structure groups by its key and round-robins whole groups across partitions. """ n = max(1, int(n)) if structure.strides_records: return [list(records[k::n]) for k in range(n)] groups: dict[Any, list[Any]] = {} order: list[Any] = [] for r in records: key = structure.group_key(r) if key not in groups: groups[key] = [] order.append(key) groups[key].append(r) parts: list[list[Any]] = [[] for _ in range(n)] for i, key in enumerate(order): parts[i % n].extend(groups[key]) return parts
[docs] def num_chunks_for(size: int, num_chunks: int = 1, chunk_size: int | None = None) -> int: """Resolve the chunk count from an explicit ``num_chunks`` or a target ``chunk_size`` (as seq_encode does).""" if chunk_size is not None: import math return max(1, int(math.ceil(float(size) / float(chunk_size)))) return max(1, int(num_chunks))
[docs] def encode_partitions( records: Sequence[Any], encoder: Any, structure: SampleStructure = EXCHANGEABLE, num_chunks: int = 1, chunk_size: int | None = None, ) -> list[tuple[int, Any]]: """Partition ``records`` by ``structure`` and ``encoder.seq_encode`` each part -> ``[(count, payload)]``.""" n = num_chunks_for(len(records), num_chunks, chunk_size) return [(len(part), encoder.seq_encode(part)) for part in partition_records(records, structure, n)]