Livt packages
Eccelerators.Tokenization
Version 0.2.0
Bounded BPE and Unigram tokenization, text normalization, and decoding components for Livt hardware.
Published files (27)
tests/Unigram/UnigramTest.lvt
namespace Eccelerators.Tokenization.Tests.Unigram
using Eccelerators.Tokenization.Unigram
/**
* Synthetic eight-piece trie plus an unassigned slot for Unigram and decoding tests.
* Mutable cost modes exercise wide accumulation and a strictly cheaper split path.
*/
component ArrayVocabulary : IUnigramVocabulary
{
// Test-selected cost modes; restore them after a scenario.
wide: bool
split: bool
/**
* Enables or disables the wide-cost mode in which separate a/b tokens beat the ab piece.
*/
public fn PreferSplit(value: bool)
{
split = value
}
/**
* Enables or disables exact costs shifted into high bits for accumulation tests.
*/
public fn Wide(value: bool)
{
wide = value
}
// Base exact costs indexed by synthetic token ID.
const COSTS: int[8] = [0, 1, 1, 2, 1, 1, 0, 1]
/**
* Fixture implementation: returns the trie child reached by value from node, or -1 if no edge exists.
* Node 0 is the root. Report table/I/O failures through Failed so absence is distinguishable.
*/
override fn Next(node: int, value: byte) int
{
if (node == 0 && value == 60) {
return 1
}
if (node == 0 && value == 97) {
return 6
}
if (node == 0 && value == 98) {
return 7
}
if (node == 0 && value == 226) {
return 9
}
if (node == 0 && value == 195) {
return 12
}
if (node == 0 && value == 120) {
return 18
}
if (node == 1 && value == 117) {
return 2
}
if (node == 1 && value == 112) {
return 14
}
if (node == 2 && value == 110) {
return 3
}
if (node == 3 && value == 107) {
return 4
}
if (node == 4 && value == 62) {
return 5
}
if (node == 6 && value == 98) {
return 8
}
if (node == 9 && value == 150) {
return 10
}
if (node == 10 && value == 129) {
return 11
}
if (node == 12 && value == 169) {
return 13
}
if (node == 14 && value == 97) {
return 15
}
if (node == 15 && value == 100) {
return 16
}
if (node == 16 && value == 62) {
return 17
}
return -1
}
/**
* Fixture implementation: returns the nonnegative token ID ending at node, or -1 for a nonterminal.
* Report malformed records or I/O errors through Failed.
*/
override fn Terminal(node: int) int
{
if (node == 5) {
return 0
}
if (node == 6) {
return 1
}
if (node == 7) {
return 2
}
if (node == 8) {
return 3
}
if (node == 11) {
return 4
}
if (node == 13) {
return 5
}
if (node == 17) {
return 6
}
if (node == 18) {
return 7
}
return -1
}
/**
* Returns the configured synthetic cost for base token 0..7; callers supply a valid fixture ID.
*/
override fn Cost(token: int) logic[64]
{
if (split) {
if (token == 3) {
return 1 as logic[64] << 40
}
return COSTS[token] as logic[64] << 38
}
if (wide) {
return COSTS[token] as logic[64] << 39
}
return COSTS[token] as logic[64]
}
/**
* Fixture implementation: returns the nonnegative unknown-token ID used when no single-codepoint piece exists.
*/
override fn Unknown() int
{
return 0
}
/**
* Fixture implementation: returns the unknown-token cost in the same exact scale as Cost, at most 2^40.
*/
override fn UnknownCost() logic[64]
{
return 20
}
/**
* Always returns false: this deterministic fixture has no I/O failure mode.
*/
override fn Failed() bool
{
return false
}
/**
* Fixture implementation: returns flags 0..3: bit 0 marks special IDs, bit 1 unassigned IDs; either skips decoding.
* Invalid IDs must return a negative value or set Failed, not masquerade as unassigned.
*/
override fn TokenFlags(token: int) int
{
if (token < 0 || token > 8) {
return -1
}
if (token == 0 || token == 6) {
return 1
}
if (token == 8) {
return 2
}
return 0
}
/**
* Fixture implementation: returns the UTF-8 piece length in bytes for an ordinary token, or a negative value on failure.
* The decoder requires 1..MAX_PIECE bytes and checks Failed for provider errors.
*/
override fn TokenLength(token: int) int
{
if (token == 0) {
return 5
}
if (token == 1) {
return 1
}
if (token == 2) {
return 1
}
if (token == 3) {
return 2
}
if (token == 4) {
return 3
}
if (token == 5) {
return 2
}
if (token == 6) {
return 5
}
if (token == 7) {
return 1
}
return -1
}
/**
* Returns a byte of the synthetic piece for the requested token/index; unsupported pairs return zero.
*/
override fn TokenByte(token: int, index: int) byte
{
if (token == 0 && index == 0) {
return 60
}
if (token == 0 && index == 1) {
return 117
}
if (token == 0 && index == 2) {
return 110
}
if (token == 0 && index == 3) {
return 107
}
if (token == 0 && index == 4) {
return 62
}
if (token == 1 && index == 0) {
return 97
}
if (token == 2 && index == 0) {
return 98
}
if (token == 3 && index == 0) {
return 97
}
if (token == 3 && index == 1) {
return 98
}
if (token == 4 && index == 0) {
return 226
}
if (token == 4 && index == 1) {
return 150
}
if (token == 4 && index == 2) {
return 129
}
if (token == 5 && index == 0) {
return 195
}
if (token == 5 && index == 1) {
return 169
}
if (token == 6 && index == 0) {
return 60
}
if (token == 6 && index == 1) {
return 112
}
if (token == 6 && index == 2) {
return 97
}
if (token == 6 && index == 3) {
return 100
}
if (token == 6 && index == 4) {
return 62
}
if (token == 7 && index == 0) {
return 120
}
return 0x00
}
}
/**
* Branch-backed synthetic vocabulary providing the second Unigram test configuration.
* Shares piece bytes/IDs with ArrayVocabulary but keeps costs explicit.
*/
component BranchVocabulary : IUnigramVocabulary
{
/**
* Fixture implementation: returns the trie child reached by value from node, or -1 if no edge exists.
* Node 0 is the root. Report table/I/O failures through Failed so absence is distinguishable.
*/
override fn Next(node: int, value: byte) int
{
if (node == 0 && value == 60) {
return 1
}
if (node == 0 && value == 97) {
return 6
}
if (node == 0 && value == 98) {
return 7
}
if (node == 0 && value == 226) {
return 9
}
if (node == 0 && value == 195) {
return 12
}
if (node == 0 && value == 120) {
return 18
}
if (node == 1 && value == 117) {
return 2
}
if (node == 1 && value == 112) {
return 14
}
if (node == 2 && value == 110) {
return 3
}
if (node == 3 && value == 107) {
return 4
}
if (node == 4 && value == 62) {
return 5
}
if (node == 6 && value == 98) {
return 8
}
if (node == 9 && value == 150) {
return 10
}
if (node == 10 && value == 129) {
return 11
}
if (node == 12 && value == 169) {
return 13
}
if (node == 14 && value == 97) {
return 15
}
if (node == 15 && value == 100) {
return 16
}
if (node == 16 && value == 62) {
return 17
}
return -1
}
/**
* Fixture implementation: returns the nonnegative token ID ending at node, or -1 for a nonterminal.
* Report malformed records or I/O errors through Failed.
*/
override fn Terminal(node: int) int
{
if (node == 5) {
return 0
}
if (node == 6) {
return 1
}
if (node == 7) {
return 2
}
if (node == 8) {
return 3
}
if (node == 11) {
return 4
}
if (node == 13) {
return 5
}
if (node == 17) {
return 6
}
if (node == 18) {
return 7
}
return -1
}
/**
* Returns the configured synthetic cost for base token 0..7; callers supply a valid fixture ID.
*/
override fn Cost(token: int) logic[64]
{
if (token == 3) {
return 2
}
return 1
}
/**
* Fixture implementation: returns the nonnegative unknown-token ID used when no single-codepoint piece exists.
*/
override fn Unknown() int
{
return 0
}
/**
* Fixture implementation: returns the unknown-token cost in the same exact scale as Cost, at most 2^40.
*/
override fn UnknownCost() logic[64]
{
return 20
}
/**
* Always returns false: this deterministic fixture has no I/O failure mode.
*/
override fn Failed() bool
{
return false
}
/**
* Fixture implementation: returns flags 0..3: bit 0 marks special IDs, bit 1 unassigned IDs; either skips decoding.
* Invalid IDs must return a negative value or set Failed, not masquerade as unassigned.
*/
override fn TokenFlags(token: int) int
{
if (token < 0 || token > 8) {
return -1
}
if (token == 0 || token == 6) {
return 1
}
if (token == 8) {
return 2
}
return 0
}
/**
* Fixture implementation: returns the UTF-8 piece length in bytes for an ordinary token, or a negative value on failure.
* The decoder requires 1..MAX_PIECE bytes and checks Failed for provider errors.
*/
override fn TokenLength(token: int) int
{
if (token == 0) {
return 5
}
if (token == 1) {
return 1
}
if (token == 2) {
return 1
}
if (token == 3) {
return 2
}
if (token == 4) {
return 3
}
if (token == 5) {
return 2
}
if (token == 6) {
return 5
}
if (token == 7) {
return 1
}
return -1
}
/**
* Returns a byte of the synthetic piece for the requested token/index; unsupported pairs return zero.
*/
override fn TokenByte(token: int, index: int) byte
{
if (token == 0 && index == 0) {
return 60
}
if (token == 0 && index == 1) {
return 117
}
if (token == 0 && index == 2) {
return 110
}
if (token == 0 && index == 3) {
return 107
}
if (token == 0 && index == 4) {
return 62
}
if (token == 1 && index == 0) {
return 97
}
if (token == 2 && index == 0) {
return 98
}
if (token == 3 && index == 0) {
return 97
}
if (token == 3 && index == 1) {
return 98
}
if (token == 4 && index == 0) {
return 226
}
if (token == 4 && index == 1) {
return 150
}
if (token == 4 && index == 2) {
return 129
}
if (token == 5 && index == 0) {
return 195
}
if (token == 5 && index == 1) {
return 169
}
if (token == 6 && index == 0) {
return 60
}
if (token == 6 && index == 1) {
return 112
}
if (token == 6 && index == 2) {
return 97
}
if (token == 6 && index == 3) {
return 100
}
if (token == 6 && index == 4) {
return 62
}
if (token == 7 && index == 0) {
return 120
}
return 0x00
}
}
/**
* Test normalization provider that preserves each supplied byte as an int.
*/
component IdentityAscii : IAsciiNormalization
{
/**
* Returns value unchanged as an int; callers supply printable ASCII.
*/
override fn Map(value: byte) int
{
return value as int
}
}
/**
* Test normalization provider that lowercases ASCII A..Z and preserves other bytes.
*/
component LowerAscii : IAsciiNormalization
{
/**
* Lowercases ASCII A..Z, otherwise returns value unchanged as an int.
*/
override fn Map(value: byte) int
{
if (value >= 0x41 && value <= 0x5a) {
return (value as int) + 32
}
return value as int
}
}
/**
* Checks array-backed compatibility APIs with two capacity/vocabulary configurations.
* Covers exact costs, UTF-8, unknown fusion, failure invalidation and request reset.
*/
@Test
component UnigramTest
{
// Owned vocabulary fixtures and consumers; test methods share these instances.
a: ArrayVocabulary
b: BranchVocabulary
small: UnigramSegmenter<8, 4, ArrayVocabulary>
large: UnigramSegmenter<32, 16, BranchVocabulary>
norm: AsciiMetaspace<4, 15, IdentityAscii>
lower: AsciiMetaspace<16, 51, LowerAscii>
decoder: MetaspaceDecoder<8, 5, ArrayVocabulary>
other: MetaspaceDecoder<32, 5, BranchVocabulary>
/**
* Owns two vocabulary fixtures and bounded segmentation, normalization and decoding subjects.
* Each test resets the subjects it uses; tests share this component instance.
*/
new()
{
a = new ArrayVocabulary()
b = new BranchVocabulary()
small = new UnigramSegmenter<8, 4, ArrayVocabulary>(a)
large = new UnigramSegmenter<32, 16, BranchVocabulary>(b)
norm = new AsciiMetaspace<4, 15, IdentityAscii>(new IdentityAscii())
lower = new AsciiMetaspace<16, 51, LowerAscii>(new LowerAscii())
decoder = new MetaspaceDecoder<8, 5, ArrayVocabulary>(a)
other = new MetaspaceDecoder<32, 5, BranchVocabulary>(b)
}
/**
* Verifies stable Viterbi ties, within-segment unknown fusion and accumulated outputs for both configurations.
*/
@Test
fn ViterbiTiesUnknownFusionAndRepeatedSegments()
{
small.Reset()
assert small.Accept(0x61)
assert small.Accept(0x62)
assert small.EncodeSegment()
assert small.Count() == 1
assert small.Token(0) == 3
small.BeginSegment()
assert small.Accept(0x7e)
assert small.Accept(0x7e)
assert small.EncodeSegment()
assert small.Count() == 2
assert small.Token(1) == 0
small.BeginSegment()
assert small.Accept(0x7e)
assert small.EncodeSegment()
assert small.Count() == 3
large.Reset()
assert large.Accept(0x61)
assert large.Accept(0x62)
assert large.EncodeSegment()
assert large.Token(0) == 3
large.BeginSegment()
assert large.Accept(0xc3)
assert large.Accept(0xa9)
assert large.EncodeSegment()
assert large.Token(1) == 5
}
/**
* Verifies exact high-bit path costs over repeated pieces, then restores the fixture cost mode.
*/
@Test
fn ExactWideCostAccumulation()
{
a.Wide(true)
small.Reset()
assert small.Accept(0x61)
assert small.Accept(0x62)
assert small.Accept(0x61)
assert small.Accept(0x62)
assert small.EncodeSegment()
assert small.Count() == 2
assert small.Token(0) == 3
assert small.Token(1) == 3
a.Wide(false)
}
/**
* Verifies malformed UTF-8 and byte/token overflow invalidate output and Reset permits reuse.
*/
@Test
fn InvalidUtf8CapacityAndReset()
{
small.Reset()
assert small.Accept(0xc0)
assert !small.EncodeSegment()
assert small.Failed()
assert small.Count() == 0
small.Reset()
for (var i = 0; i < 8; i++) {
assert small.Accept(0x61)
}
assert !small.Accept(0x61)
assert !small.EncodeSegment()
small.Reset()
for (var i = 0; i < 5; i++) {
assert small.Accept(0x61)
}
assert !small.EncodeSegment()
assert small.Count() == 0
small.Reset()
assert small.Accept(0x61)
assert small.EncodeSegment()
assert small.Count() == 1
}
/**
* Verifies space normalization, byte/input bounds and an alternate lowercase mapping provider.
*/
@Test
fn NormalizationBoundsAndAlternateProvider()
{
norm.Reset()
assert norm.Accept(0x20)
assert norm.Accept(0x20)
assert norm.Accept(0x61)
assert norm.Accept(0x20)
assert norm.Length() == 7
assert norm.Read(0) == 0xe2
assert norm.Read(3) == 0x61
assert !norm.Accept(0x62)
assert norm.Length() == 0
norm.Reset()
assert !norm.Accept(0x09)
lower.Reset()
assert lower.Accept(0x41)
assert lower.Read(3) == 0x61
assert lower.Length() == 4
}
/**
* Verifies UTF-8 preservation, skipped pieces, first-piece rules and invalidation of all bytes on failure.
*/
@Test
fn Utf8DecodeSpecialSlotsAndFailureAtomicity()
{
decoder.Reset()
assert decoder.Append(6)
assert decoder.Append(8)
assert decoder.Append(4)
assert decoder.Length() == 0
assert decoder.Append(5)
assert decoder.Append(4)
assert decoder.Length() == 3
assert decoder.Read(0) == 0xc3
assert decoder.Read(1) == 0xa9
assert decoder.Read(2) == 0x20
assert !decoder.Append(-1)
assert decoder.Length() == 0
assert !decoder.Append(1)
decoder.Reset()
for (var i = 0; i < 8; i++) {
assert decoder.Append(1)
}
assert !decoder.Append(1)
assert decoder.Length() == 0
other.Reset()
assert other.Append(4)
assert other.Append(1)
assert other.Length() == 1
}
}