ZibinDong's picture
Publish audited ActionCodec2 release files
1bfc3f0 verified
Raw History Blame Contribute Delete
15.6 kB
"""Fitted ActionCodec2Sparse token layer: spatial Set-BPE per sparse frame plus temporal rules."""
from __future__ import annotations
import heapq
import json
from collections.abc import Sequence
from numbers import Integral
from pathlib import Path
import numpy as np
from ..native_backend import native_kernel, resolve_backend
from ..configuration import _integer
from ..setbpe.spatial import SpatialVocabulary
from .layout import SparseLayout
SPARSE_BPE_NAME = "sparse_bpe.json"
FORMAT = "actioncodec2_sparse_bpe"
FORMAT_VERSION = 1
MAX_LOOKAHEAD = 32
def _ctz(mask: int) -> int:
return (mask & -mask).bit_length() - 1
class TokenTables:
"""Per-token geometry shared by the encoder, decoder, trainer and grammar."""
def __init__(self, support: Sequence[int], e_dim: int, lookahead: int) -> None:
if not 1 <= lookahead <= MAX_LOOKAHEAD:
raise ValueError(f"lookahead must be in [1, {MAX_LOOKAHEAD}]")
self.e_dim, self.lookahead, self.base = e_dim, lookahead, len(support)
self.masks: list[tuple[int, ...]] = []
self.patterns: list[tuple[tuple[int, int], ...]] = []
self.span: list[int] = []
self.rdim: list[int] = []
self.emask: list[int] = []
self.first_from: list[tuple[int, ...]] = []
for token, mask in enumerate(support):
if not mask:
raise ValueError("spatial token without support")
self._push(((0, token),), (int(mask),))
def _push(self, pattern, masks) -> None:
span = max(o for o, m in enumerate(masks) if m) + 1
masks = tuple(masks[:span])
self.masks.append(masks)
self.patterns.append(tuple(pattern))
self.span.append(span)
self.rdim.append(_ctz(masks[0]))
self.emask.append(sum(1 << o for o, m in enumerate(masks) if m >> self.e_dim & 1))
first, following = [], -1
for o in range(span - 1, -1, -1):
if masks[o]:
following = o
first.append(following)
self.first_from.append(tuple(reversed(first)))
def __len__(self) -> int:
return len(self.span)
def mask(self, token: int, offset: int) -> int:
masks = self.masks[token]
return masks[offset] if offset < len(masks) else 0
def compose(self, left: int, right: int, offset: int) -> tuple[int, ...] | None:
"""Union support of ``right`` rooted ``offset`` frames after ``left``, or ``None``."""
if not 0 <= offset < self.lookahead:
return None
span = max(self.span[left], offset + self.span[right])
if span > self.lookahead:
return None
out = [self.mask(left, o) for o in range(span)]
for o in range(self.span[right]):
if out[o + offset] & self.mask(right, o):
return None
out[o + offset] |= self.mask(right, o)
return tuple(out)
def add_merge(self, left: int, right: int, offset: int) -> int:
masks = self.compose(left, right, offset)
if masks is None:
raise ValueError("illegal temporal merge")
pattern = sorted(
self.patterns[left] + tuple((o + offset, t) for o, t in self.patterns[right])
)
self._push(pattern, masks)
return len(self) - 1
def rule_key(left: int, right: int, offset: int) -> int:
return (left << 40) | (right << 16) | offset
class Instances:
"""Mutable rooted token instances over one frame axis (reference implementation)."""
def __init__(self, tables: TokenTables, frames: int, dims: int) -> None:
self.tb, self.frames, self.dims = tables, frames, dims
self.tok: list[int] = []
self.root: list[int] = []
self.alive: list[bool] = []
self.member = np.full((frames, dims), -1, dtype=np.int64)
def key(self, i: int) -> tuple[int, int, int, int]:
e = self.tb.emask[self.tok[i]]
root = self.root[i]
return (root, 1 if e else 0, root + _ctz(e) if e else 0, self.tb.rdim[self.tok[i]])
def add(self, token: int, root: int) -> int:
self.tok.append(token)
self.root.append(root)
self.alive.append(True)
index = len(self.tok) - 1
self._paint(index, index)
return index
def _paint(self, i: int, value: int) -> None:
token, root = self.tok[i], self.root[i]
for o, mask in enumerate(self.tb.masks[token]):
while mask:
d = _ctz(mask)
mask &= mask - 1
if value >= 0 and self.member[root + o, d] >= 0:
raise RuntimeError("instance overlap")
self.member[root + o, d] = value
def kill(self, i: int) -> None:
self.alive[i] = False
self._paint(i, -1)
def _first(self, a: int, b: int) -> bool:
return (self.root[a], self.tb.rdim[self.tok[a]]) < (self.root[b], self.tb.rdim[self.tok[b]])
def partners(self, i: int, allow_zero: bool):
"""Ordered candidate pairs ``(a, b, offset)`` involving ``i`` in the lookahead window."""
tb, L, root = self.tb, self.tb.lookahead, self.root[i]
lo, hi = max(0, root - L + 1), min(self.frames - 1, root + L - 1)
for f in range(lo, hi + 1):
for d in range(self.dims):
j = int(self.member[f, d])
if j < 0 or j == i:
continue
tj, o = self.tok[j], f - self.root[j]
k = max(0, lo - self.root[j])
if tb.first_from[tj][k] != o or _ctz(tb.mask(tj, o)) != d:
continue
a, b = (i, j) if self._first(i, j) else (j, i)
off = self.root[b] - self.root[a]
if off >= L or (off == 0 and not allow_zero):
continue
if max(tb.span[self.tok[a]], off + tb.span[self.tok[b]]) > L:
continue
yield a, b, off
def valid(self, a: int, b: int, off: int) -> bool:
"""Whether merging ``a`` (rooted first) with ``b`` keeps the stream decodable."""
tb, ta, tbb = self.tb, self.tok[a], self.tok[b]
span = max(tb.span[ta], off + tb.span[tbb])
emask = tb.emask[ta] | (tb.emask[tbb] << off)
root = self.root[a]
merged = (root, 1 if emask else 0, root + _ctz(emask) if emask else 0, tb.rdim[ta])
for o in range(span):
mask = tb.mask(ta, o) | (tb.mask(tbb, o - off) if o >= off else 0)
if not mask:
continue
u = root + o
h = int(self.member[u, tb.e_dim])
if h >= 0 and h not in (a, b) and not self.key(h) > merged:
return False
if emask >> o & 1:
for d in range(self.dims):
j = int(self.member[u, d])
if j >= 0 and j not in (a, b) and not self.key(j) < merged:
return False
return True
def serialize(self) -> list[int]:
live = sorted((i for i, alive in enumerate(self.alive) if alive), key=self.key)
return [self.tok[i] for i in live]
class SparseVocabulary:
"""Fitted sparse token layer over one physical profile's dense cells."""
def __init__(self, layout: SparseLayout, spatial_merges, temporal_merges, lookahead: int):
self.layout = layout
self.lookahead = _integer(lookahead, "lookahead", positive=True)
self.spatial = SpatialVocabulary(layout.sparse_bins, tuple(map(tuple, spatial_merges)))
self.temporal_merges = tuple(
tuple(_integer(v, "temporal merge value") for v in m) for m in temporal_merges
)
self.tables = TokenTables([int(s) for s in self.spatial.support], layout.e_dim, lookahead)
self._rank = {}
for rank, (left, right, offset) in enumerate(self.temporal_merges):
if max(left, right) >= len(self.tables):
raise ValueError("a temporal merge must reference already-created tokens")
self.tables.add_merge(left, right, offset)
if self._rank.setdefault(rule_key(left, right, offset), rank) != rank:
raise ValueError("duplicate temporal rule")
self._native = None
@property
def vocab_size(self) -> int:
return len(self.tables)
def __getstate__(self):
return {**vars(self), "_native": None}
def _codec(self):
constructor = native_kernel("SparseCodec")
if constructor is not None and self._native is None:
self._native = constructor(
list(self.layout.bins),
self.layout.native_zero,
[tuple(m) for m in self.spatial.merges],
self.lookahead,
list(self.temporal_merges),
)
return self._native if constructor is not None else None
def _cells(self, cells) -> np.ndarray:
values = np.asarray(cells)
if values.ndim != 3 or values.shape[0] < 1 or values.shape[1] < 1:
raise ValueError("cells must have shape (B, T, D) with B, T >= 1")
if values.shape[2] != self.layout.dims or values.dtype.kind not in "iu":
raise ValueError(f"cells must be integers with {self.layout.dims} slots")
if np.any(values < -1) or np.any(values >= np.asarray(self.layout.bins)):
raise ValueError("cell outside the primitive range")
return values
def encode_batch(self, cells, *, backend: str = "auto", threads: int = 0) -> list[list[int]]:
"""Encode independent dense chunks ``(B, T, D)`` (``-1`` = inactive) into token rows."""
values = self._cells(cells)
if resolve_backend(backend) == "native" and self._codec() is not None:
return self._codec().encode_batch(np.asarray(values, dtype=np.int32), threads)
return [self._encode_reference(chunk) for chunk in values]
def _encode_reference(self, cells: np.ndarray) -> list[int]:
sparse = self.layout.sparsify(cells)
state = Instances(self.tables, len(sparse) + self.lookahead, self.layout.dims + 1)
for t, row in enumerate(sparse):
if np.any(row >= 0):
for token in self.spatial.encode(row.tolist()):
state.add(int(token), t)
heap = []
def offer(i):
for a, b, off in state.partners(i, True):
rank = self._rank.get(rule_key(state.tok[a], state.tok[b], off))
if rank is not None:
item = (rank, state.root[a], self.tables.rdim[state.tok[a]], state.root[b])
heapq.heappush(heap, (*item, a, b, off))
for i in range(len(state.tok)):
offer(i)
while heap:
rank, *_, a, b, off = heapq.heappop(heap)
if not state.alive[a] or not state.alive[b] or not state.valid(a, b, off):
continue
root = state.root[a]
state.kill(a)
state.kill(b)
offer(state.add(self.tables.base + rank, root))
return state.serialize()
def decode_batch(
self, rows: Sequence[Sequence[int]], horizon: int, active: Sequence[bool]
) -> np.ndarray:
"""Token rows to dense cells ``(B, horizon, D)``; inactive slots are ``-1``."""
if isinstance(horizon, bool) or not isinstance(horizon, int) or horizon < 1:
raise ValueError("horizon must be a positive integer")
flags = [bool(a) for a in active]
if len(flags) != self.layout.dims:
raise ValueError(f"active must have {self.layout.dims} flags")
checked = []
for row in rows:
if any(
isinstance(t, (bool, np.bool_))
or not isinstance(t, Integral)
or not 0 <= t < self.vocab_size
for t in row
):
raise ValueError("token ids must be integers within the sparse vocabulary")
checked.append([int(t) for t in row])
codec = self._codec()
if codec is not None:
return np.asarray(codec.decode_batch(checked, horizon, flags), dtype=np.int64)
out = np.empty((len(checked), horizon, self.layout.dims), dtype=np.int64)
for index, row in enumerate(checked):
out[index] = self.layout.densify(self._frames_reference(row, horizon, flags), flags)
return out
def _frames_reference(self, row: Sequence[int], horizon: int, active) -> np.ndarray:
tb, e_bit = self.tables, 1 << self.layout.e_dim
allowed = e_bit | sum(1 << d for d, flag in enumerate(active) if flag)
cover = [0] * (horizon + self.lookahead)
sparse = np.full((horizon, self.layout.dims + 1), -1, dtype=np.int64)
front = 0
for token in row:
if not 0 <= token < len(tb):
raise ValueError("token outside vocab")
if front >= horizon:
raise ValueError("token after the last open frame")
own: dict[int, int] = {}
for offset, spatial in tb.patterns[token]:
u, mask = front + offset, int(self.spatial.support[spatial])
if u >= horizon:
raise ValueError("token exceeds horizon")
if mask & ~allowed:
raise ValueError("token touches an inactive slot")
if own.get(u, 0) & mask:
raise ValueError("overlapping token supports")
if mask & e_bit and u == horizon - 1:
raise ValueError("end-of-frame atom on the last frame")
own[u] = own.get(u, 0) | mask
expansion = self.spatial.expansion[spatial]
sparse[u] = np.where(expansion >= 0, expansion, sparse[u])
for u, mask in own.items():
if cover[u] & mask:
raise ValueError("overlapping token supports")
if cover[u] & e_bit:
raise ValueError("token touches a closed frame")
cover[u] |= mask
while front < horizon and cover[front] & e_bit:
front += 1
return sparse
def to_dict(self) -> dict:
return {
"format": FORMAT,
"format_version": FORMAT_VERSION,
"layout": {"bins": list(self.layout.bins), "zero": list(self.layout.zero)},
"lookahead": self.lookahead,
"spatial_merges": [list(m) for m in self.spatial.merges],
"temporal_merges": [list(m) for m in self.temporal_merges],
}
@classmethod
def from_dict(cls, payload: dict) -> SparseVocabulary:
if payload.get("format") != FORMAT or payload.get("format_version") != FORMAT_VERSION:
raise ValueError(f"not an {FORMAT} v{FORMAT_VERSION} vocabulary")
layout = SparseLayout(tuple(payload["layout"]["bins"]), tuple(payload["layout"]["zero"]))
return cls(
layout, payload["spatial_merges"], payload["temporal_merges"], payload["lookahead"]
)
def save_pretrained(self, directory: str | Path) -> Path:
path = Path(directory) / SPARSE_BPE_NAME
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(self.to_dict(), separators=(",", ":")) + "\n", encoding="utf-8")
return path
@classmethod
def from_pretrained(cls, directory: str | Path) -> SparseVocabulary:
path = Path(directory) / SPARSE_BPE_NAME
return cls.from_dict(json.loads(path.read_text(encoding="utf-8")))