from __future__ import annotations
def bpe_encode(text: str, merges: list[tuple[int, int]]) -> list[int]:
tokens = list(text.encode("utf-8"))
merge_rank: dict[tuple[int, int], int] = {}
for rank, pair in enumerate(merges):
merge_rank[pair] = rank
changed = True
while changed:
changed = False
i = 0
while i < len(tokens) - 1:
pair = (tokens[i], tokens[i + 1])
if pair in merge_rank:
new_id = 256 + merge_rank[pair]
tokens[i] = new_id
tokens.pop(i + 1)
changed = True
else:
i += 1
return tokens
def count_pairs(tokens: list[int]) -> dict[tuple[int, int], int]:
counts: dict[tuple[int, int], int] = {}
for i in range(len(tokens) - 1):
pair = (tokens[i], tokens[i + 1])
counts[pair] = counts.get(pair, 0) + 1
return counts
def build_vocab(text: str, num_merges: int) -> list[tuple[int, int]]:
tokens = list(text.encode("utf-8"))
merges: list[tuple[int, int]] = []
for _ in range(num_merges):
counts = count_pairs(tokens)
if not counts:
break
best = max(counts, key=lambda p: counts[p])
merges.append(best)
new_id = 256 + len(merges) - 1
i = 0
new_tokens: list[int] = []
while i < len(tokens):
if i < len(tokens) - 1 and (tokens[i], tokens[i + 1]) == best:
new_tokens.append(new_id)
i += 2
else:
new_tokens.append(tokens[i])
i += 1
tokens = new_tokens
return merges
if __name__ == "__main__":
import time
sample_text = (
"the quick brown fox jumps over the lazy dog. " * 20
)
print("Training BPE vocab (50 merges)...")
merges = build_vocab(sample_text, num_merges=50)
print(f" Learned {len(merges)} merge rules")
n_iters = 10_000
print(f"\nBenchmarking bpe_encode x{n_iters}...")
start = time.perf_counter()
for _ in range(n_iters):
result = bpe_encode(sample_text[:50], merges)
elapsed = time.perf_counter() - start
print(f" bpe_encode (50 chars): {elapsed:.3f}s for {n_iters} iters")
print(f" avg: {elapsed / n_iters * 1000:.4f}ms per call")
print(f" output tokens: {len(result)}")
tokens = list(sample_text.encode("utf-8"))
print(f"\nBenchmarking count_pairs x{n_iters}...")
start = time.perf_counter()
for _ in range(n_iters):
pairs = count_pairs(tokens)
elapsed = time.perf_counter() - start
print(f" count_pairs ({len(tokens)} tokens): {elapsed:.3f}s for {n_iters} iters")
print(f" unique pairs: {len(pairs)}")