ZibinDong's picture
Publish audited ActionCodec2 release files
1bfc3f0 verified
Raw History Blame Contribute Delete
9.58 kB
"""Constrained-generation grammars for the two ActionCodec2 BPE schemes."""
from __future__ import annotations
from collections.abc import Sequence
from numbers import Integral
import numpy as np
from ._validation import active_dimensions
from .bpe import BPESerializer, SetBPESerializer
from .cache import BoundedCache, cached_prefix_state
from .native_backend import native_kernel
def _coverage_metadata(serializer, dims):
cache = getattr(serializer, "_coverage_metadata", None)
if cache is None:
cache = serializer._coverage_metadata = BoundedCache(32)
metadata = cache.get(dims)
if metadata is not None:
return metadata
metadata = {"compiled": None}
if serializer.scheme == "set_bpe":
full = sum(1 << d for d in dims)
metadata["roots"] = np.asarray(
[serializer.spatial.anchor[serializer.root[t]] for t in range(serializer.vocab_size)],
dtype=np.int64,
)
patterns = tuple(
tuple((offset, int(serializer.spatial.support[token])) for offset, token in pattern)
for pattern in serializer.patterns
)
metadata["patterns"] = patterns
metadata["scope"] = np.asarray(
[all(support & ~full == 0 for _, support in pattern) for pattern in patterns],
dtype=bool,
)
else:
phase = np.zeros((len(dims), serializer.vocab_size), dtype=bool)
for index in range(len(dims)):
for token, expansion in enumerate(serializer.vocabulary.token_dims):
phase[index, token] = all(
d == dims[(index + offset) % len(dims)] for offset, d in enumerate(expansion)
)
metadata["phase"] = phase
metadata["lengths"] = np.asarray(serializer.token_cell_counts, dtype=np.int64)
cache[dims] = metadata
return metadata
class _PrefixGrammar:
def _state(self, tokens: Sequence[int]) -> tuple[int, ...] | None:
checked = []
for token in tokens:
if isinstance(token, bool) or not isinstance(token, Integral):
return None
checked.append(int(token))
return cached_prefix_state(
tuple(checked), self._state_cache, self._empty_state, self._advance
)
def _next_token_mask_from_state(self, state: tuple[int, ...]) -> np.ndarray:
cached = self._mask_cache.get(state)
if cached is None:
compiled = self._compiled()
cached = compiled.mask(state) if compiled is not None else self._python_mask(state)
self._mask_cache[state] = cached
return cached.copy()
def next_token_mask(self, tokens: Sequence[int]) -> np.ndarray:
"""Return a boolean vocabulary mask for the next generated token."""
state = self._state(tokens)
if state is None:
raise ValueError("token prefix is not a legal ActionCodec2 stream")
return self._next_token_mask_from_state(state)
def is_complete(self, tokens: Sequence[int]) -> bool:
"""Whether ``tokens`` is exactly one complete trajectory of this horizon."""
state = self._state(tokens)
return state is not None and self._state_is_complete(state)
class ActionCodec2Grammar(_PrefixGrammar):
'Prefix grammar for one exact, fixed-horizon rooted token stream.'
def __init__(
self,
serializer: SetBPESerializer,
*,
horizon: int,
active_dims: Sequence[int] | None = None,
) -> None:
if isinstance(horizon, bool) or not isinstance(horizon, int) or horizon < 1:
raise ValueError("horizon must be a positive integer")
self.horizon = horizon
self.vocab_size = serializer.vocab_size
dims = active_dimensions(active_dims, serializer.spatial.dimension)
self._full = sum(1 << dimension for dimension in dims)
self._metadata = _coverage_metadata(serializer, dims)
self._root_anchor = self._metadata["roots"]
self._patterns = self._metadata["patterns"]
self._in_scope = self._metadata["scope"]
self._mask_cache = BoundedCache(64)
self._state_cache = BoundedCache(256)
self._empty_state = self._initial_state()
self._native = None
self._compiled()
def _compiled(self):
constructor = native_kernel("SetGrammar")
if constructor is None:
return None
if self._native is None:
prototype = self._metadata["compiled"]
if prototype is None:
prototype = self._metadata["compiled"] = constructor(
1, self._full, self._root_anchor.tolist(), self._patterns
)
self._native = prototype.clone(self.horizon)
return self._native
def _advance(self, state: tuple[int, ...], token: int) -> tuple[int, ...] | None:
compiled = self._compiled()
if compiled is not None:
return compiled.advance(state, token)
if not 0 <= token < self.vocab_size or not self._in_scope[token]:
return None
coverage = list(state)
front = next(
(index for index, mask in enumerate(coverage) if mask != self._full), self.horizon
)
if front == self.horizon:
return None
uncovered = self._full & ~coverage[front]
expected_anchor = int(uncovered & -uncovered).bit_length() - 1
if int(self._root_anchor[token]) != expected_anchor:
return None
for offset, support in self._patterns[token]:
step = front + offset
if step >= self.horizon or coverage[step] & support:
return None
coverage[step] |= support
return tuple(coverage)
def _initial_state(self) -> tuple[int, ...]:
return (0,) * self.horizon
def _covered_steps(self, state: tuple[int, ...]) -> int:
return next((i for i, mask in enumerate(state) if mask != self._full), self.horizon)
def _state_is_complete(self, state: tuple[int, ...]) -> bool:
return all(mask == self._full for mask in state)
def _python_mask(self, state: tuple[int, ...]) -> np.ndarray:
if self._state_is_complete(state):
return np.zeros(self.vocab_size, dtype=bool)
front = next(index for index, mask in enumerate(state) if mask != self._full)
uncovered = self._full & ~state[front]
expected_anchor = int(uncovered & -uncovered).bit_length() - 1
mask = (self._root_anchor == expected_anchor) & self._in_scope
for token in np.flatnonzero(mask):
for offset, support in self._patterns[int(token)]:
step = front + offset
if step >= self.horizon or state[step] & support:
mask[token] = False
break
return mask
class ActionCodec2BPEGrammar(_PrefixGrammar):
'Prefix grammar for one exact, fixed-horizon plain-BPE token stream.'
def __init__(
self,
serializer: BPESerializer,
*,
horizon: int,
active_dims: Sequence[int] | None = None,
) -> None:
if isinstance(horizon, bool) or not isinstance(horizon, int) or horizon < 1:
raise ValueError("horizon must be a positive integer")
self.horizon = horizon
self.vocab_size = serializer.vocab_size
dims = active_dimensions(active_dims, serializer.vocabulary.dimension)
self._width = len(dims)
self._cells = horizon * self._width
self._metadata = _coverage_metadata(serializer, dims)
self._token_cells = self._metadata["lengths"]
self._phase = self._metadata["phase"]
self._mask_cache = BoundedCache(64)
self._state_cache = BoundedCache(256)
self._empty_state = self._initial_state()
self._active_dims = dims
self._native = None
self._compiled()
def _compiled(self):
constructor = native_kernel("PlainGrammar")
if constructor is None:
return None
if self._native is None:
prototype = self._metadata["compiled"]
if prototype is None:
prototype = self._metadata["compiled"] = constructor(
1, self._token_cells.tolist(), self._active_dims, self._phase
)
self._native = prototype.clone(self.horizon)
return self._native
def _initial_state(self) -> tuple[int, ...]:
return (0,)
def _covered_steps(self, state: tuple[int, ...]) -> int:
return state[0] // self._width
def _state_is_complete(self, state: tuple[int, ...]) -> bool:
return state[0] == self._cells
def _advance(self, state: tuple[int, ...], token: int) -> tuple[int, ...] | None:
compiled = self._compiled()
if compiled is not None:
return compiled.advance(state, token)
if not 0 <= token < self.vocab_size:
return None
covered = state[0]
length = int(self._token_cells[token])
if covered + length > self._cells:
return None
if not self._phase[covered % self._width, token]:
return None
return (covered + length,)
def _python_mask(self, state: tuple[int, ...]) -> np.ndarray:
covered = state[0]
if covered == self._cells:
return np.zeros(self.vocab_size, dtype=bool)
return self._phase[covered % self._width] & (self._token_cells <= self._cells - covered)