unicode : add opt-in NFC normalization for Unigram tokenizers (#30390)
* unicode : add opt-in NFC normalization for Unigram tokenizers Note: used help from Codex, Sol 6.1 Medium * Fixing initial bot comments + linter error (using existing out instead of print) * removed test_nfc_tokenizer
This commit is contained in:
1 parent
eca5458ce0
commit
10da8c463f
12 files changed
+4165
-7
No files matched your search
@@ -1279,6 +1279,92 @@ std::vector<uint32_t> unicode_cpts_normalize_nfd(const std::vector<uint32_t> & c
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint8_t unicode_canonical_class(uint32_t cpt) {
|
||||
const auto it = unicode_combining_class.find(cpt);
|
||||
return it == unicode_combining_class.end() ? 0 : it->second;
|
||||
}
|
||||
|
||||
static void unicode_canonical_decompose(uint32_t cpt, std::vector<uint32_t> & result, std::vector<uint32_t> & pending) {
|
||||
pending.push_back(cpt);
|
||||
while (!pending.empty()) {
|
||||
cpt = pending.back();
|
||||
pending.pop_back();
|
||||
// Hangul syllables use algorithmic decomposition (UAX #15).
|
||||
if (cpt >= 0xAC00 && cpt < 0xAC00 + 11172) {
|
||||
const uint32_t index = cpt - 0xAC00;
|
||||
result.push_back(0x1100 + index / 588);
|
||||
result.push_back(0x1161 + (index % 588) / 28);
|
||||
if (index % 28) {
|
||||
result.push_back(0x11A7 + index % 28);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const auto it = unicode_decomposition.find(cpt);
|
||||
if (it == unicode_decomposition.end()) {
|
||||
result.push_back(cpt);
|
||||
continue;
|
||||
}
|
||||
if (it->second.second) {
|
||||
pending.push_back(it->second.second);
|
||||
}
|
||||
pending.push_back(it->second.first);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t unicode_canonical_compose(uint32_t first, uint32_t second) {
|
||||
if (first >= 0x1100 && first < 0x1100 + 19 && second >= 0x1161 && second < 0x1161 + 21) {
|
||||
return 0xAC00 + ((first - 0x1100) * 21 + second - 0x1161) * 28;
|
||||
}
|
||||
if (first >= 0xAC00 && first < 0xAC00 + 11172 && (first - 0xAC00) % 28 == 0 && second > 0x11A7 && second < 0x11A7 + 28) {
|
||||
return first + second - 0x11A7;
|
||||
}
|
||||
const auto it = unicode_composition.find((uint64_t(first) << 32) | second);
|
||||
return it == unicode_composition.end() ? 0 : it->second;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> unicode_cpts_normalize_nfc(const std::vector<uint32_t> & cpts) {
|
||||
std::vector<uint32_t> decomposed;
|
||||
decomposed.reserve(cpts.size());
|
||||
std::vector<uint32_t> pending;
|
||||
for (const uint32_t cpt : cpts) {
|
||||
unicode_canonical_decompose(cpt, decomposed, pending);
|
||||
}
|
||||
|
||||
// Sort each run of combining marks; equal classes retain their order.
|
||||
for (size_t i = 0; i < decomposed.size();) {
|
||||
if (unicode_canonical_class(decomposed[i]) == 0) {
|
||||
++i;
|
||||
}
|
||||
const size_t begin = i;
|
||||
while (i < decomposed.size() && unicode_canonical_class(decomposed[i]) != 0) {
|
||||
++i;
|
||||
}
|
||||
std::stable_sort(decomposed.begin() + begin, decomposed.begin() + i, [](uint32_t a, uint32_t b) {
|
||||
return unicode_canonical_class(a) < unicode_canonical_class(b);
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<uint32_t> result;
|
||||
result.reserve(decomposed.size());
|
||||
size_t starter = 0;
|
||||
uint8_t previous_class = 0;
|
||||
for (const uint32_t cpt : decomposed) {
|
||||
const uint8_t current_class = unicode_canonical_class(cpt);
|
||||
const uint32_t composite = !result.empty() && (previous_class == 0 || previous_class < current_class)
|
||||
? unicode_canonical_compose(result[starter], cpt) : 0;
|
||||
if (composite) {
|
||||
result[starter] = composite;
|
||||
} else {
|
||||
if (current_class == 0) {
|
||||
starter = result.size();
|
||||
}
|
||||
result.push_back(cpt);
|
||||
previous_class = current_class;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> unicode_cpts_from_utf8(const std::string & utf8) {
|
||||
std::vector<uint32_t> result;
|
||||
result.reserve(utf8.size());
|
||||
|
||||
Reference in new issue
Block a user