Livt packages
Eccelerators.Tokenization
Version 0.2.0
Bounded BPE and Unigram tokenization, text normalization, and decoding components for Livt hardware.
Published files (27)
src/Unigram/StoredUnigramSegmenter.lvt
namespace Eccelerators.Tokenization.Unigram
/**
* Runs exact Viterbi on one pre-tokenized UTF-8 segment at a time.
* Borrows vocabulary and exclusive workspace of 5*MAX_BYTES+3+2*MAX_TOKENS 64-bit words.
* All reachable cells are written before reading; physical storage need not start cleared.
* Strict improvement preserves the first path on ties; unknown runs fuse within a segment.
* Output accumulates until Reset; failure invalidates all output. Logical Reset changes metadata;
* hardware reset must cancel outstanding consumer/provider transactions together.
* All public calls are serialized by the caller.
*/
component StoredUnigramSegmenter<MAX_BYTES: int, MAX_TOKENS: int, VOCAB: IUnigramVocabulary, STORAGE: IUnigramStorage>
{
static assert MAX_BYTES > 0 && MAX_BYTES <= 4096, "invalid segment capacity"
static assert MAX_TOKENS > 0 && MAX_TOKENS <= 4096, "invalid token capacity"
// One dynamic-programming boundary per byte plus the final boundary.
const SLOTS: int = MAX_BYTES + 1
// Word offsets for input, path costs, signed predecessors, IDs and UTF-8 widths.
const DATA: int = 0
const COSTS: int = MAX_BYTES
const PREVIOUS: int = COSTS + SLOTS
const IDS: int = PREVIOUS + SLOTS
const WIDTHS: int = IDS + SLOTS
// Bounded backtracking scratch and accumulated output IDs.
const REVERSE: int = WIDTHS + MAX_BYTES
const OUTPUT: int = REVERSE + MAX_TOKENS
// Minimum contiguous workspace capacity in 64-bit words.
public const STORAGE_WORDS: int = OUTPUT + MAX_TOKENS
// Borrowed vocabulary and exclusively held 64-bit workspace.
vocab: VOCAB
storage: STORAGE
// Current segment bytes, accumulated output IDs and sticky request failure.
length: int
count: int
error: bool
/**
* Borrows vocabulary and exclusive 64-bit workspace in one clock/reset context.
* Storage must cover 0..STORAGE_WORDS-1; cells may be uninitialized. Reset starts a request.
*/
new(vocab: VOCAB, storage: STORAGE)
{
this.vocab = vocab
this.storage = storage
}
/**
* Reads the low 32 bits of one workspace word as a signed int.
* The explicit intermediate width preserves negative predecessor sentinels.
*/
fn ReadInt(address: int) int
{
var raw: logic[64] = storage.Read(address)
var word: logic[32] = raw as logic[32]
return word as int
}
/**
* Stores the exact low 32-bit representation of a signed int in a workspace word.
* ReadInt restores signed interpretation; upper bits are zero.
*/
fn WriteInt(address: int, value: int)
{
storage.Write(address, (value as uint) as logic[64])
}
/**
* Discards both the current segment and all accumulated token IDs, and clears sticky failure.
* Does not clear workspace or reset vocabulary/provider state. Calls must be serialized;
* hardware cancellation requires shared context reset with the storage provider.
*/
public fn Reset()
{
length = 0
count = 0
error = false
}
/**
* Discards current input bytes while retaining accumulated tokens and any sticky failure.
* Use at pre-tokenizer boundaries; this does not recover a failed request.
*/
public fn BeginSegment()
{
length = 0
}
/**
* Latches failure and invalidates accumulated token IDs; returns false for propagation.
*/
fn Fail() bool
{
error = true
count = 0
return false
}
/**
* Waits for one byte to be stored in the current segment, up to MAX_BYTES.
* Checks required contiguous workspace capacity before access. Overflow or short storage
* invalidates all accumulated tokens and returns false; UTF-8 validation occurs in EncodeSegment.
*/
public fn Accept(value: byte) bool
{
if (error || !storage.IsValidAddress(STORAGE_WORDS - 1) || length >= MAX_BYTES) {
return Fail()
}
storage.Write(DATA + length, value as logic[64])
length++
return true
}
/**
* Considers an edge from initialized start to end with a nonnegative token ID and cost <= 2^40.
* Accumulates four unsigned 16-bit limbs exactly; bounded paths fit within 2^52.
* Updates cost/predecessor/ID only on strict improvement, preserving the first path on ties.
* Returns false for an invalid ID/cost; callers supply reachable codepoint boundaries.
*/
fn Candidate(start: int, end: int, id: int, cost: logic[64]) bool
{
if (id < 0 || cost > (1 as logic[64] << 40)) {
return false
}
var total: logic[64] = 0
var carry = 0
var prior = storage.Read(COSTS + start)
for (var limb = 0; limb < 4; limb++) {
var shift = limb * 16
var priorLimb: logic[16] = (prior >> shift) as logic[16]
var costLimb: logic[16] = (cost >> shift) as logic[16]
var sum = priorLimb as int + costLimb as int + carry
total = total | ((sum & 65535) as logic[64] << shift)
carry = sum >> 16
}
var better = true
if (ReadInt(PREVIOUS + end) >= 0) {
better = total < storage.Read(COSTS + end)
}
if (better) {
storage.Write(COSTS + end, total)
WriteInt(PREVIOUS + end, start)
WriteInt(IDS + end, id)
}
return true
}
/**
* Validates a nonempty UTF-8 segment and waits for exact-cost Viterbi/backtracking to finish.
* Appends IDs to the request output, fusing adjacent unknowns only within this segment.
* True clears segment bytes for reuse; false invalidates every output and requires Reset.
* The caller owns pre-tokenizer boundaries and must not run concurrent provider operations.
*/
public fn EncodeSegment() bool
{
if (error || length == 0) {
return Fail()
}
for (var i = 0; i <= length; i++) {
WriteInt(PREVIOUS + i, -1)
}
var p = 0
while (p < length) {
var b = storage.Read(DATA + p) as int
var n = 0
if (b < 128) {
n = 1
}
if (b >= 194 && b <= 223) {
n = 2
}
if (b >= 224 && b <= 239) {
n = 3
}
if (b >= 240 && b <= 244) {
n = 4
}
if (n == 0) {
return Fail()
}
if (p + n > length) {
return Fail()
}
for (var j = 1; j < n; j++) {
var c = storage.Read(DATA + p + j) as int
if (c < 128 || c > 191) {
return Fail()
}
}
if (n >= 3) {
var c = storage.Read(DATA + p + 1) as int
if ((b == 224 && c < 160) || (b == 237 && c >= 160) || (b == 240 && c < 144) || (b == 244 && c >= 144)) {
return Fail()
}
}
WriteInt(WIDTHS + p, n)
for (var j = 1; j < n; j++) {
WriteInt(WIDTHS + p + j, 0)
}
p += n
}
storage.Write(COSTS, 0)
WriteInt(PREVIOUS, 0)
p = 0
while (p < length) {
var n = ReadInt(WIDTHS + p)
var node = 0
var single = false
var end = p
while (end < length && node >= 0) {
node = vocab.Next(node, (storage.Read(DATA + end) as byte))
end++
if (node >= 0) {
var id = vocab.Terminal(node)
if (id >= 0 && (end == length || ReadInt(WIDTHS + end) != 0)) {
if (!Candidate(p, end, id, vocab.Cost(id))) {
return Fail()
}
if (end - p == n) {
single = true
}
}
}
}
if (!single) {
if (!Candidate(p, p + n, vocab.Unknown(), vocab.UnknownCost())) {
return Fail()
}
}
p += n
}
if (vocab.Failed()) {
return Fail()
}
p = length
var size = 0
var last = -1
var unknown = vocab.Unknown()
while (p > 0) {
var id = ReadInt(IDS + p)
if (id != unknown || last != unknown) {
if (size >= MAX_TOKENS - count) {
return Fail()
}
WriteInt(REVERSE + size, id)
size++
}
last = id
var next = ReadInt(PREVIOUS + p)
if (next < 0 || next >= p) {
return Fail()
}
p = next
}
if (size > MAX_TOKENS - count) {
return Fail()
}
for (var i = size - 1; i >= 0; i--) {
WriteInt(OUTPUT + count, ReadInt(REVERSE + i))
count++
}
length = 0
return true
}
/**
* Returns the number of accumulated output IDs, or zero after Reset or failure.
*/
public fn Count() int
{
return count
}
/**
* Returns the accumulated token ID at index 0..Count()-1, waiting for workspace access.
* Returns -1 for an invalid index or failed request.
*/
public fn Token(index: int) int
{
if (error || index < 0 || index >= count) {
return -1
}
return ReadInt(OUTPUT + index)
}
/**
* Reports sticky capacity, UTF-8, vocabulary or path-validation failure since Reset.
*/
public fn Failed() bool
{
return error
}
}