Skip to content

fix: engine/bpe: map byte 0xAD like GPT-2 byte-level BPE (not identity) - #805

Open
Rasaboun wants to merge 1 commit into
cactus-compute:mainfrom
Rasaboun:fix/bpe-byte-0xad-mapping
Open

Rasaboun wants to merge 1 commit into
cactus-compute:mainfrom
Rasaboun:fix/bpe-byte-0xad-mapping

Conversation

@Rasaboun

@Rasaboun Rasaboun commented Sep 2, 2026 •

Copy link
Copy Markdown

Fixes #806. (Found alongside #803, whose Python-binding half is #804.)

What

BPETokenizer::init_byte_mappings treats bytes 161..255 as identity code points. GPT-2 byte-level BPE (bytes_to_unicode()) excludes 173 (U+00AD, soft hyphen) from that range and assigns it the next free code point after the other non-printables (U+0143). This PR excludes 173 from both the identity byte list and the identity branch of the assignment loop — the branch is selected by byte value, so moving the byte between lists alone has no effect (that was my first, ineffective attempt; documented so nobody repeats it).

Symptom

Any UTF-8 sequence containing byte 0xAD, e.g. 歯 (E6 AD AF), tokenizes to <byte> <unk> <byte> instead of the reference token and decodes to U+FFFD. With LiquidAI/LFM2.5-230M:

input before after (= HF reference)
歯 [672, 0, 617] [33687]
歯医者 [672, 0, 617, 8773, 4046] [33687, 8773, 4046]
明日の朝9時に歯医者に電話することを思い出させて 17 tokens, 1 unk 15 tokens, identical to HF

Verification

  • The resulting 256-entry mapping equals HF GPT2Tokenizer bytes_to_unicode() byte-for-byte (checked against an exact replica of the loop's control flow).
  • Built apple/build.sh (device, simulator, macOS); cactus_tokenize via the macOS framework gives the table above; a React Native app on the iOS simulator round-trips 歯 intact in tool-call arguments where it previously produced �.

No other files touched; no performance impact (mapping is built once).

init_byte_mappings treated bytes 161..255 as identity code points. GPT-2's
bytes_to_unicode() excludes 173 (U+00AD, soft hyphen) from that range and
assigns it the next free code point after the other non-printables
(U+0143). Any UTF-8 sequence containing byte 0xAD -- e.g. 歯 (E6 AD AF) --
therefore tokenized to <byte> <unk> <byte> instead of the reference token,
and decoded to U+FFFD.

The fix must exclude 173 from BOTH the identity byte list and the
identity branch of the assignment loop (the branch is selected by byte
value, so moving the byte between lists alone has no effect).

Verified: the resulting 256-entry mapping equals HF GPT2
bytes_to_unicode() byte-for-byte; cactus_tokenize("歯") -> [33687] as in
the reference tokenizer.

Refs cactus-compute#803 (second item).

Signed-off-by: Rasaboun <40967731+Rasaboun@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Rasaboun Rasaboun changed the title engine/bpe: map byte 0xAD like GPT-2 byte-level BPE (not identity) fix: engine/bpe: map byte 0xAD like GPT-2 byte-level BPE (not identity) Sep 5, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BPE byte-level mapping: byte 0xAD mapped to identity instead of U+0143 (GPT-2 bytes_to_unicode) — CJK chars containing 0xAD tokenize to <unk>

2 participants