Team Ai
Datasetpublic

Brunobkr/llama.cpp_AlgMor24_github

ΩFFFΣLLIa • llama.cpp • AlgMor24 ██████╗ ███████╗███████╗███████╗██╗ ██╗ ██╗ █████╗ ██╔═══██╗██╔════╝██╔════╝██╔════╝██║ ██║ ██║██╔══██╗ ██║ ██║█████╗ █████╗ █████╗ ██║ ██║ ██║███████║ ██║ ██║██╔══╝ ██╔══╝ ██╔══╝ ██║ ██║ ██║██╔══██║ ╚██████╔╝██║ ██║ ███████╗███████╗███████╗██║██║ ██║ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝╚══════╝╚═╝╚═╝ ╚═╝ High-Performance LLM / VLM Inference & Autonomous Agentic Ecosystem… See the full description on the dataset page: https://huggingface.co/datasets/Brunobkr/llama.cpp_AlgMor24_github.

sourceHugging Faceupdated 2mo agoView on Hugging Face
0likes3.1kdownloads
llama-vocab.cpp4398 linesDownload Raw Back to src
1#include "llama-vocab.h"2 3#include "ggml.h"4#include "gguf.h"5#include "llama-impl.h"6#include "llama-model-loader.h"7 8#include "unicode.h"9 10#include <algorithm>11#include <cassert>12#include <cctype>13#include <cfloat>14#include <cmath>15#include <cstdarg>16#include <cstring>17#include <forward_list>18#include <limits>19#include <map>20#include <queue>21#include <set>22#include <unordered_map>23 24//25// helpers26//27 28struct naive_trie {29    naive_trie() : has_value(false), value(0) {30    }31    void insert(const char * key, size_t len, int32_t value = 0) {32        if (len == 0) {33            this->has_value = true;34            this->value = value;35            return;36        }37        char c = key[0];38        auto res = children.find(c);39        if (res != children.end()) {40            res->second.insert(key + 1, len - 1, value);41        } else {42            auto res = children.insert(std::make_pair(c, naive_trie()));43            res.first->second.insert(key + 1, len - 1, value);44        }45    }46    std::pair<const char *, size_t> get_longest_prefix(const char * key, size_t len, size_t offset = 0) const {47        if (len == 0 || offset == len) {48            return std::make_pair(key, offset);49        }50        char c = key[offset];51        auto res = children.find(c);52        if (res != children.end()) {53            return res->second.get_longest_prefix(key, len, offset + 1);54        }55 56        return std::make_pair(key, offset);57    }58    const struct naive_trie * traverse(const char c) const {59        auto res = children.find(c);60        if (res != children.end()) {61            return &res->second;62        }63 64        return NULL;65    }66    std::map<char, struct naive_trie> children;67    bool has_value;68    llama_token value;69};70 71//72// tokenizers73//74 75struct llm_tokenizer {76    llm_tokenizer() {}77    virtual ~llm_tokenizer() = default;78};79 80struct llm_symbol {81    using index = int;82    index prev;83    index next;84    const char * text;85    size_t n;86};87 88static_assert(std::is_trivially_copyable<llm_symbol>::value, "llm_symbol is not trivially copyable");89 90//91// SPM tokenizer92// original implementation:93// https://github.com/ggml-org/llama.cpp/commit/074bea2eb1f1349a0118239c4152914aecaa1be494//95 96struct llm_bigram_spm {97    struct comparator {98        bool operator()(llm_bigram_spm & l, llm_bigram_spm & r) {99            return (l.score < r.score) || (l.score == r.score && l.left > r.left);100        }101    };102    using queue_storage = std::vector<llm_bigram_spm>;103    using queue = std::priority_queue<llm_bigram_spm, queue_storage, comparator>;104    llm_symbol::index left;105    llm_symbol::index right;106    float score;107    size_t size;108};109 110struct llm_tokenizer_spm : llm_tokenizer {111    llm_tokenizer_spm(const llama_vocab & /*vocab*/) {}112};113 114struct llm_tokenizer_spm_session {115    llm_tokenizer_spm_session(const llama_vocab & vocab) : vocab(vocab) {}116 117    void tokenize(const std::string & text, std::vector<llama_token> & output) {118        // split string into utf8 chars119        int index = 0;120        size_t offs = 0;121        while (offs < text.size()) {122            llm_symbol sym;123            size_t len = unicode_len_utf8(text[offs]);124            sym.text = text.c_str() + offs;125            sym.n = std::min(len, text.size() - offs);126            offs += sym.n;127            sym.prev = index - 1;128            sym.next = offs == text.size() ? -1 : index + 1;129            index++;130            symbols.emplace_back(sym);131        }132 133        // seed the work queue with all possible 2-character tokens.134        for (int i = 1; i < (int) symbols.size(); ++i) {135            try_add_bigram(i - 1, i);136        }137 138        // keep substituting the highest frequency pairs for as long as we can.139        while (!work_queue.empty()) {140            auto bigram = work_queue.top();141            work_queue.pop();142 143            auto & left_sym = symbols[bigram.left];144            auto & right_sym = symbols[bigram.right];145 146            // if one of the symbols already got merged, skip it.147            if (left_sym.n == 0 || right_sym.n == 0 ||148                left_sym.n + right_sym.n != bigram.size) {149                continue;150            }151 152            // merge the right sym into the left one153            left_sym.n += right_sym.n;154            right_sym.n = 0;155 156            //LLAMA_LOG_INFO("left = '%*s' size = %zu\n", (int) left_sym.n, left_sym.text, bigram.size);157 158            // remove the right sym from the chain159            left_sym.next = right_sym.next;160            if (right_sym.next >= 0) {161                symbols[right_sym.next].prev = bigram.left;162            }163 164            // find more substitutions165            try_add_bigram(left_sym.prev, bigram.left);166            try_add_bigram(bigram.left, left_sym.next);167        }168 169        for (int i = 0; i != -1; i = symbols[i].next) {170            auto & symbol = symbols[i];171            resegment(symbol, output);172        }173    }174 175private:176    void resegment(llm_symbol & symbol, std::vector<llama_token> & output) {177        auto text = std::string(symbol.text, symbol.n);178        auto token = vocab.text_to_token(text);179 180        // Do we need to support is_unused?181        if (token != LLAMA_TOKEN_NULL) {182            output.push_back(token);183            return;184        }185 186        const auto p = rev_merge.find(text);187 188        if (p == rev_merge.end()) {189            // output any symbols that did not form tokens as bytes.190            output.reserve(output.size() + symbol.n);191            for (int j = 0; j < (int)symbol.n; ++j) {192                llama_token id = vocab.byte_to_token(symbol.text[j]);193                output.push_back(id);194            }195            return;196        }197 198        resegment(symbols[p->second.first], output);199        resegment(symbols[p->second.second], output);200    }201 202    void try_add_bigram(int left, int right) {203        if (left == -1 || right == -1) {204            return;205        }206        const std::string text = std::string(symbols[left].text, symbols[left].n + symbols[right].n);207        auto token = vocab.text_to_token(text);208 209        if (token == LLAMA_TOKEN_NULL) {210            return;211        }212 213        if (static_cast<uint32_t>(token) >= vocab.n_tokens()) {214            return;215        }216 217        const auto & tok_data = vocab.get_token_data(token);218 219        llm_bigram_spm bigram;220        bigram.left  = left;221        bigram.right = right;222        bigram.score = tok_data.score;223        bigram.size  = text.size();224 225        work_queue.push(bigram);226 227        // Do we need to support is_unused?228        rev_merge[text] = std::make_pair(left, right);229    }230 231    const llama_vocab & vocab;232    // currently unused233    // const llm_tokenizer_spm * spm_tokenizer;234 235    std::vector<llm_symbol> symbols;236    llm_bigram_spm::queue work_queue;237    std::map<std::string, std::pair<int, int>> rev_merge;238};239 240//241// BPE tokenizer242// adapted from https://github.com/cmp-nct/ggllm.cpp [MIT License]243// tried to simplify unicode stuff, so most likely does not work 100% correctly!244//245 246// TODO: there are a lot of common parts between spm and bpe tokenizers, should be refactored and reused247 248template<typename T, typename Container = std::vector<T>, typename Compare = std::less<typename Container::value_type>>249class llama_priority_queue : public std::priority_queue<T, Container, Compare> {250public:251    using std::priority_queue<T, Container, Compare>::priority_queue;252 253    T pop_move() {254        T item = std::move(this->c.front());255        std::pop_heap(this->c.begin(), this->c.end(), this->comp);256        this->c.pop_back();257        return item;258    }259 260    void pop() =  delete;261};262 263struct llm_bigram_bpe {264    struct comparator {265        bool operator()(const llm_bigram_bpe & l, const llm_bigram_bpe & r) const {266            return l.rank > r.rank || (l.rank == r.rank && l.left > r.left);267        }268    };269 270    using queue_storage = std::vector<llm_bigram_bpe>;271    using queue = llama_priority_queue<llm_bigram_bpe, queue_storage, comparator>;272    llm_symbol::index left;273    llm_symbol::index right;274    std::string text;275    int rank;276    size_t size;277};278 279struct llm_tokenizer_bpe : llm_tokenizer {280    llm_tokenizer_bpe(const llama_vocab & vocab) {281        GGML_ASSERT(vocab.get_type() == LLAMA_VOCAB_TYPE_BPE);282        switch (vocab.get_pre_type()) {283            case LLAMA_VOCAB_PRE_TYPE_LLAMA3:284                regex_exprs = {285                    // original regex from tokenizer.json286                    //"(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\\r\\n\\p{L}\\p{N}]?\\p{L}+|\\p{N}{1,3}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",287 288                    // adapted: https://github.com/ggml-org/llama.cpp/pull/6920#issuecomment-2080233989289                    "(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])|[^\\r\\n\\p{L}\\p{N}]?\\p{L}+|\\p{N}{1,3}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",290                };291                break;292            case LLAMA_VOCAB_PRE_TYPE_JAIS2:293                regex_exprs = {294                    // original regex from tokenizer.json295                    //"(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\\r\\n\\p{L}\\p{N}]?\\p{L}+|\\p{N}{1,3}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s{512}(?!\\S)|\\s{256}(?!\\S)|\\s{128}(?!\\S)|\\s{64}(?!\\S)|\\s{32}(?!\\S)|\\s{16}(?!\\S)|\\s{8}(?!\\S)|\\s{4}(?!\\S)|\\s{1,2}(?!\\S)|\\s{1}",296 297                    // adapted: same as llama3 but with cascading whitespace pattern298                    "(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])|[^\\r\\n\\p{L}\\p{N}]?\\p{L}+|\\p{N}{1,3}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s{512}(?!\\S)|\\s{256}(?!\\S)|\\s{128}(?!\\S)|\\s{64}(?!\\S)|\\s{32}(?!\\S)|\\s{16}(?!\\S)|\\s{8}(?!\\S)|\\s{4}(?!\\S)|\\s{1,2}(?!\\S)|\\s{1}",299                };300                break;301            case LLAMA_VOCAB_PRE_TYPE_DBRX:302            case LLAMA_VOCAB_PRE_TYPE_SMAUG:303                regex_exprs = {304                    // same as llama3305                    "(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])|[^\\r\\n\\p{L}\\p{N}]?\\p{L}+|\\p{N}{1,3}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",306                };307                break;308            case LLAMA_VOCAB_PRE_TYPE_DEEPSEEK_LLM:309                regex_exprs = {310                    "[\r\n]",311                    "\\s?[A-Za-zµÀ-ÖØ-öø-ƺƼ-ƿDŽ-ʓʕ-ʯͰ-ͳͶͷͻ-ͽͿΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-ՖႠ-ჅᎠ-Ᏽᏸ-ᏽᲐ-ᲺᲽ-Ჿᴀ-ᴫᵫ-ᵷᵹ-ᶚḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼℂℇℊ-ℓℕℙ-ℝℤΩℨK-ℭℯ-ℴℹℼ-ℿⅅ-ⅉⅎↃↄⰀ-ⱻⱾ-ⳤⳫ-ⳮⳲⳳꙀ-ꙭꚀ-ꚛꜢ-ꝯꝱ-ꞇꞋ-ꞎꭰ-ꮿff-stﬓ-ﬗA-Za-z𐐀-𐑏𐒰-𐓓𐓘-𐓻𐲀-𐲲𐳀-𐳲𑢠-𑣟𞤀-𞥃]+",312                    "\\s?[!-/:-~!-/:-~‘-‟ -。]+",313                    "\\s+$",314                    "[一-龥ࠀ-一가-퟿]+",315                    "\\p{N}+",316                };317                break;318            case LLAMA_VOCAB_PRE_TYPE_DEEPSEEK3_LLM:319            case LLAMA_VOCAB_PRE_TYPE_HUNYUAN_DENSE:320            case LLAMA_VOCAB_PRE_TYPE_JOYAI_LLM:321                regex_exprs = {322                    "\\p{N}{1,3}",323                    "[一-龥぀-ゟ゠-ヿ]+",324                    "[!\"#$%&'()*+,\\-./:;<=>?@\\[\\\\\\]^_`{|}~][A-Za-z]+|[^\r\n\\p{L}\\p{P}\\p{S}]?[\\p{L}\\p{M}]+| ?[\\p{P}\\p{S}]+[\r\n]*|\\s*[\r\n]+|\\s+(?!\\S)|\\s+",325                };326                break;327            case LLAMA_VOCAB_PRE_TYPE_YOUTU:328                regex_exprs = {329                    "[가-힣ㄱ-ㆎ]+|[!…“”‘’—:;,、-〿︰-﹏]+|[ㄅ-ㄯ]+|[一-龥぀-ゟ゠-ヿ]+",330                    "[^\\r\\n\\p{L}\\p{N}]?[\\p{Lu}\\p{Lt}\\p{Lm}\\p{Lo}\\p{M}]*[\\p{Ll}\\p{Lm}\\p{Lo}\\p{M}]+(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])?|[^\\r\\n\\p{L}\\p{N}]?[\\p{Lu}\\p{Lt}\\p{Lm}\\p{Lo}\\p{M}]+[\\p{Ll}\\p{Lm}\\p{Lo}\\p{M}]*(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])?|\\p{N}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n/]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",331                };332                break;333            case LLAMA_VOCAB_PRE_TYPE_DEEPSEEK_CODER:334                regex_exprs = {335                    "[\r\n]",336                    "\\s?\\p{L}+",337                    "\\s?\\p{P}+",338                    "[一-龥ࠀ-一가-퟿]+",339                    "\\p{N}",340                };341                break;342            case LLAMA_VOCAB_PRE_TYPE_FALCON:343                regex_exprs = {344                    "[\\p{P}\\$\\+<=>\\^~\\|`]+",345                    "'s|'t|'re|'ve|'m|'ll|'d| ?\\p{L}+| ?\\p{N}+| ?[^\\s\\p{L}\\p{N}]+|\\s+(?!\\S)",346                    "[0-9][0-9][0-9]",347                };348                break;349            case LLAMA_VOCAB_PRE_TYPE_STARCODER:350            case LLAMA_VOCAB_PRE_TYPE_REFACT:351            case LLAMA_VOCAB_PRE_TYPE_COMMAND_R:352            case LLAMA_VOCAB_PRE_TYPE_SMOLLM:353            case LLAMA_VOCAB_PRE_TYPE_CODESHELL:354            case LLAMA_VOCAB_PRE_TYPE_EXAONE:355            case LLAMA_VOCAB_PRE_TYPE_MINERVA:356            case LLAMA_VOCAB_PRE_TYPE_MELLUM2:357                regex_exprs = {358                    "\\p{N}",359                    "'s|'t|'re|'ve|'m|'ll|'d| ?\\p{L}+| ?\\p{N}+| ?[^\\s\\p{L}\\p{N}]+|\\s+(?!\\S)",360                };361                break;362            case LLAMA_VOCAB_PRE_TYPE_GPT2:363            case LLAMA_VOCAB_PRE_TYPE_MPT:364            case LLAMA_VOCAB_PRE_TYPE_OLMO:365            case LLAMA_VOCAB_PRE_TYPE_JAIS:366            case LLAMA_VOCAB_PRE_TYPE_TRILLION:367            case LLAMA_VOCAB_PRE_TYPE_GRANITE_DOCLING:368                regex_exprs = {369                    "'s|'t|'re|'ve|'m|'ll|'d| ?\\p{L}+| ?\\p{N}+| ?[^\\s\\p{L}\\p{N}]+|\\s+(?!\\S)",370                };371                break;372            case LLAMA_VOCAB_PRE_TYPE_STABLELM2:373            case LLAMA_VOCAB_PRE_TYPE_QWEN2:374            case LLAMA_VOCAB_PRE_TYPE_HUNYUAN:375            case LLAMA_VOCAB_PRE_TYPE_SOLAR_OPEN:376                regex_exprs = {377                    // original regex from tokenizer.json378                    // "(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\\r\\n\\p{L}\\p{N}]?\\p{L}+|\\p{N}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+"379                    "(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])|[^\\r\\n\\p{L}\\p{N}]?\\p{L}+|\\p{N}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",380                };381                break;382            case LLAMA_VOCAB_PRE_TYPE_QWEN35:383                regex_exprs = {384                    // original regex from tokenizer.json385                    // "(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\\r\\n\\p{L}\\p{N}]?[\\p{L}\\p{M}]+|\\p{N}| ?[^\\s\\p{L}\\p{M}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+"386                    "(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])|[^\\r\\n\\p{L}\\p{N}]?[\\p{L}\\p{M}]+|\\p{N}| ?[^\\s\\p{L}\\p{M}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",387                };388                break;389            case LLAMA_VOCAB_PRE_TYPE_PORO:390            case LLAMA_VOCAB_PRE_TYPE_BLOOM:391            case LLAMA_VOCAB_PRE_TYPE_GPT3_FINNISH:392                regex_exprs = {393                    " ?[^(\\s|.,!?…。,、।۔،)]+",394                };395                break;396            case LLAMA_VOCAB_PRE_TYPE_CHATGLM4:397                regex_exprs = {398                    "(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])|[^\\r\\n\\p{L}\\p{N}]?\\p{L}+|\\p{N}{1,3}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",399                };400                break;401            case LLAMA_VOCAB_PRE_TYPE_VIKING:402                regex_exprs = {403                    " ?[^(\\s|.,!?…。,、।۔،)]+",404                    "\\p{N}",405                };406                break;407            case LLAMA_VOCAB_PRE_TYPE_TEKKEN:408                // original regex from tokenizer.json409                // "[^\\r\\n\\p{L}\\p{N}]?[\\p{Lu}\\p{Lt}\\p{Lm}\\p{Lo}\\p{M}]*[\\p{Ll}\\p{Lm}\\p{Lo}\\p{M}]+|[^\\r\\n\\p{L}\\p{N}]?[\\p{Lu}\\p{Lt}\\p{Lm}\\p{Lo}\\p{M}]+[\\p{Ll}\\p{Lm}\\p{Lo}\\p{M}]*|\\p{N}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n/]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+"410                regex_exprs = {411                    "[^\\r\\n\\p{L}\\p{N}]?((?=[\\p{L}])([^a-z]))*((?=[\\p{L}])([^A-Z]))+|[^\\r\\n\\p{L}\\p{N}]?((?=[\\p{L}])([^a-z]))+((?=[\\p{L}])([^A-Z]))*|\\p{N}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n/]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",412                };413                break;414            case LLAMA_VOCAB_PRE_TYPE_CHAMELEON:415                // Note: in theory, the special token (sentinel and image token) regex_exprs below416                // are unnecessary, as they are split in `tokenizer_st_partition` anyway.417                // However, since the upstream pre-tokenizer uses them, they are also418                // included here (see https://huggingface.co/facebook/chameleon-7b).419                regex_exprs = {420                    "<sentinel:[0-9]+>",  // Sentinel tokens421                    "(IMGIMG)((A|B|C|D|E|F|G|H|I){1,4})Z",  // Image tokens422                    "([\\t\\n]|    |  )",  // directly from tokenizer.json423                    "\\p{N}", // Individual digits424                    "[\\p{P}!-/:-@\\[-`{-~]",  // Punctuation, Isolated425                    "'s|'t|'re|'ve|'m|'ll|'d| ?\\p{L}+| ?\\p{N}+| ?[^\\s\\p{L}\\p{N}]+|\\s+(?!\\S)",426                };427                break;428            case LLAMA_VOCAB_PRE_TYPE_GPT4O:429            case LLAMA_VOCAB_PRE_TYPE_MINIMAX_M2:430                regex_exprs = {431                    // original regex from tokenizer.json432                    // "[^\\r\\n\\p{L}\\p{N}]?[\\p{Lu}\\p{Lt}\\p{Lm}\\p{Lo}\\p{M}]*[\\p{Ll}\\p{Lm}\\p{Lo}\\p{M}]+(?i:'s|'t|'re|'ve|'m|'ll|'d)?|[^\\r\\n\\p{L}\\p{N}]?[\\p{Lu}\\p{Lt}\\p{Lm}\\p{Lo}\\p{M}]+[\\p{Ll}\\p{Lm}\\p{Lo}\\p{M}]*(?i:'s|'t|'re|'ve|'m|'ll|'d)?|\\p{N}{1,3}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n/]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",433                    "[^\\r\\n\\p{L}\\p{N}]?((?=[\\p{L}])([^a-z]))*((?=[\\p{L}])([^A-Z]))+(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])?|[^\\r\\n\\p{L}\\p{N}]?((?=[\\p{L}])([^a-z]))+((?=[\\p{L}])([^A-Z]))*(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])?|\\p{N}{1,3}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n/]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",434                };435                break;436            case LLAMA_VOCAB_PRE_TYPE_GRANITE_EMB_MULTI:437                // Same lookaheads as GPT4O but with \p{M} added so combining marks438                // (diacritics) attach to their base letters. Avoids excessive439                // backtracking on scripts that use them heavily (Bengali, Hindi,440                // Telugu, Thai, ...). See PR #22716 for benchmarks.441                regex_exprs = {442                    "[^\\r\\n\\p{L}\\p{N}]?((?=[\\p{L}\\p{M}])([^a-z]))*((?=[\\p{L}\\p{M}])([^A-Z]))+(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])?|[^\\r\\n\\p{L}\\p{N}]?((?=[\\p{L}\\p{M}])([^a-z]))+((?=[\\p{L}\\p{M}])([^A-Z]))*(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])?|\\p{N}{1,3}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n/]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",443                };444                break;445            case LLAMA_VOCAB_PRE_TYPE_TINY_AYA:446                regex_exprs = {447                    // original regex from tokenizer.json: "\\d{1,3}(?=(?:\\d{3})*\\b)"448                    "\\d{1,3}(?=(?:\\d{3})*\\b)",449                    // original regex from tokenizer.json: "[^\\r\\n\\p{L}\\p{N}]?[\\p{Lu}\\p{Lt}\\p{Lm}\\p{Lo}\\p{M}]*[\\p{Ll}\\p{Lm}\\p{Lo}\\p{M}]+(?i:'s|'t|'re|'ve|'m|'ll|'d)?|[^\\r\\n\\p{L}\\p{N}]?[\\p{Lu}\\p{Lt}\\p{Lm}\\p{Lo}\\p{M}]+[\\p{Ll}\\p{Lm}\\p{Lo}\\p{M}]*(?i:'s|'t|'re|'ve|'m|'ll|'d)?|\\p{N}{1,3}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n/]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+"450                    "[^\\r\\n\\p{L}\\p{N}]?[\\p{Lu}\\p{Lt}\\p{Lm}\\p{Lo}\\p{M}]*[\\p{Ll}\\p{Lm}\\p{Lo}\\p{M}]+(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])?|[^\\r\\n\\p{L}\\p{N}]?[\\p{Lu}\\p{Lt}\\p{Lm}\\p{Lo}\\p{M}]+[\\p{Ll}\\p{Lm}\\p{Lo}\\p{M}]*(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])?|\\p{N}{1,3}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n/]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",451                };452                break;453            case LLAMA_VOCAB_PRE_TYPE_KIMI_K2:454                regex_exprs = {455                    // K2 trigger pattern - this will activate the custom K2 handler in unicode.cpp456                    // The custom handler implements all K2 patterns with proper Han character exclusion457                    "\\p{Han}+",458                };459                break;460            case LLAMA_VOCAB_PRE_TYPE_SUPERBPE:461                regex_exprs = {462                    "\\p{N}+",463                    "(?=(\\d{3})+(?!\\d))",464                };465                break;466            case LLAMA_VOCAB_PRE_TYPE_BAILINGMOE:467                regex_exprs = {468                    // original regex from tokenizer.json469                    // "'(?i:[sdmt]|ll|ve|re)|[^\\r\\n\\p{L}\\p{N}]?+\\p{L}+|\\p{N}| ?[^\\s\\p{L}\\p{N}]++[\\r\\n]*|\\s*[\\r\\n]|\\s+(?!\\S)|\\s+"470                    // FIXME? Changed possessive quantifiers (?+ and ++) to greedy to avoid errors and imatrix hanging (tried atomic grouping but it's not supported?)471                    "'(?:[sSdDmMtT]|[lL][lL]|[vV][eE]|[rR][eE])|[^\\r\\n\\p{L}\\p{N}]?\\p{L}+|\\p{N}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]|\\s+(?!\\S)|\\s+",472                };473                break;474            case LLAMA_VOCAB_PRE_TYPE_SEED_CODER:475                regex_exprs = {476                    // original regex from tokenizer.json477                    // "(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\r\n\\p{L}\\p{N}]?\\p{L}+|\\p{N}{1}| ?[^\\s\\p{L}\\p{N}\r\n]+|\\s*[\r\n]+|\\s+(?!\\S)|\\s+"478                    "(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])|[^\\r\\n\\p{L}\\p{N}]?\\p{L}+|\\p{N}{1}| ?[^\\s\\p{L}\\p{N}\\r\\n]+|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",479                };480                break;481            case LLAMA_VOCAB_PRE_TYPE_GROK_2:482                regex_exprs = {483                    // original regex from tokenizer.json484                    // "(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\\r\\n\\p{L}\\p{N}]?\\p{L}+|\\p{N}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+"485                    "(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])|[^\\r\\n\\p{L}\\p{N}]?\\p{L}+|\\p{N}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",486                };487                break;488            case LLAMA_VOCAB_PRE_TYPE_AFMOE:489                regex_exprs = {490                    // Digit handling - uses custom implementation in unicode.cpp491                    // Groups digits with leading 1-2 based on total length modulo 3492                    "\\p{AFMoE_digits}",493                    // CJK and Asian scripts (using direct Unicode literals)494                    "[一-鿿㐀-䶿豈-﫿぀-ゟ゠-ヿ・-゚⼀-⿟เ-๿຀-໿ក-៿က-႟ꩠ-ꩿꧠ-꧿가-힯ᄀ-ᇿ]+",495                    // Main BPE pattern496                    "[!\"#$%&'()*+,\\-./:;<=>?@\\[\\\\\\]^_`{|}~][A-Za-z]+|[^\\r\\n\\p{L}\\p{P}\\p{S}]?[\\p{L}\\p{M}]+| ?[\\p{P}\\p{S}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",497                };498                break;499            case LLAMA_VOCAB_PRE_TYPE_LAGUNA:500                regex_exprs = {501                    "[^\\n]+|[\\n]+",502                    "(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])|[^\\r\\n\\p{L}\\p{N}]?\\p{L}+|\\p{N}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",503                };504                break;505            case LLAMA_VOCAB_PRE_TYPE_EXAONE_MOE:506                regex_exprs = {507                    // original regex from tokenizer.json508                    // "(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\\r\\n\\p{L}\\p{N}]?(?:\\p{L}\\p{M}*(?: \\p{L}\\p{M}*)*)+|\\p{N}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n/]?|\\s*[\\r\\n]|\\s+(?!\\S)|\\s+"509                    "(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])|[^\\r\\n\\p{L}\\p{N}]?(?:\\p{L}\\p{M}*(?: \\p{L}\\p{M}*)*)+|\\p{N}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n/]?|\\s*[\\r\\n]|\\s+(?!\\S)|\\s+",510                };511                break;512            case LLAMA_VOCAB_PRE_TYPE_GEMMA4:513                // Gemma4 uses SPM-style BPE: spaces are replaced with ▁ by the514                // normalizer, then BPE merges run on the whole text without515                // word-level pre-splitting. We only need to split on newlines516                // since BPE merge lookup asserts no newlines in tokens.517                regex_exprs = {518                    "[^\\n]+|[\\n]+",519                };520                byte_encode = false; // uses raw UTF-8, not GPT-2 byte encoding521                break;522            case LLAMA_VOCAB_PRE_TYPE_SARVAM_MOE:523                // Sarvam uses SPM-style BPE (same shape as Gemma4): spaces replaced with U+2581524                // by the normalizer, BPE merges over the whole text on raw UTF-8.525                regex_exprs = {526                    "[^\\n]+|[\\n]+",527                };528                byte_encode = false;529                break;530            case LLAMA_VOCAB_PRE_TYPE_MINICPM5:531                regex_exprs = {532                    // original regex from tokenizer.json (openbmb/MiniCPM5-1B)533                    "\\p{N}{1,3}",534                    // "(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\\r\\n\\p{L}\\p{N}]?\\p{L}+|\\p{N}+| ?[^\\s\\p{L}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+"535                    "(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])|[^\\r\\n\\p{L}\\p{N}]?\\p{L}+|\\p{N}+| ?[^\\s\\p{L}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",536                };537                break;538            case LLAMA_VOCAB_PRE_TYPE_WHITESPACE:539                // whitespace pre-tokenizer (jinaai/jina-embeddings-v2-base-zh)540                regex_exprs = {541                    "\\S+",542                };543                byte_encode = false;544                break;545            default:546                // default regex for BPE tokenization pre-processing547                regex_exprs = {548                    "[\\p{P}\\$\\+<=>\\^~\\|]+",549                    "'s|'t|'re|'ve|'m|'ll|'d| ?\\p{L}+| ?\\p{N}+| ?[^\\s\\p{L}\\p{N}]+|\\s+(?!\\S)",550                    "\\p{N}+",551                    "[0-9][0-9][0-9]",552                };553                break;554        }555    }556 557    std::vector<std::string> regex_exprs;558    bool byte_encode = true; // GPT-2 byte encoding; false for SPM-style BPE (raw UTF-8)559};560 561struct llm_tokenizer_bpe_session {562    llm_tokenizer_bpe_session(const llama_vocab & vocab, const llm_tokenizer_bpe & tokenizer) : vocab(vocab), tokenizer(tokenizer) {}563 564    virtual ~llm_tokenizer_bpe_session() = default;565 566    static void append(const llama_token token_id, std::vector<llama_token> & output)  {567        output.push_back(token_id);568    }569 570    bool append_bos(std::vector<llama_token> & output) const {571        if (vocab.get_add_bos()) {572            GGML_ASSERT(vocab.token_bos() != LLAMA_TOKEN_NULL);573            output.push_back(vocab.token_bos());574            return true;575        }576        return false;577    }578 579    bool append_eos(std::vector<llama_token> & output) const {580        if (vocab.get_add_eos()) {581            GGML_ASSERT(vocab.token_eos() != LLAMA_TOKEN_NULL);582            output.push_back(vocab.token_eos());583            return true;584        }585        return false;586    }587 588    void check_double_bos_eos(const std::vector<llama_token> & output) const {589        if (vocab.get_add_bos() && output.size() >= 2 && output[1] == vocab.token_bos()) {590            LLAMA_LOG_WARN(591                "%s: Added a BOS token to the prompt as specified by the model but the prompt "592                "also starts with a BOS token. So now the final prompt starts with 2 BOS tokens. "593                "Are you sure this is what you want?\n", __FUNCTION__);594        }595        if (vocab.get_add_eos() && output.size() >= 2 && *(output.end()-2) == vocab.token_eos()) {596            LLAMA_LOG_WARN(597                "%s: Added a EOS token to the prompt as specified by the model but the prompt "598                "also ends with a EOS token. So now the final prompt ends with 2 EOS tokens. "599                "Are you sure this is what you want?\n", __FUNCTION__);600        }601    }602 603    virtual void tokenize(const std::string & text, std::vector<llama_token> & output) {604        int final_prev_index = -1;605        const auto word_collection = unicode_regex_split(text, tokenizer.regex_exprs, tokenizer.byte_encode);606 607        symbols_final.clear();608        auto tok_pre = vocab.get_pre_type();609 610        for (const auto & word : word_collection) {611            work_queue = llm_bigram_bpe::queue();612            symbols.clear();613 614            int index = 0;615            size_t offset = 0;616 617            //if (vocab.tokenizer_ignore_merges && vocab.token_to_id.find(word) != vocab.token_to_id.end()) {618            if (vocab.get_ignore_merges() && vocab.text_to_token(word) != LLAMA_TOKEN_NULL) {619                symbols.emplace_back(llm_symbol{-1, -1, word.c_str(), word.size()});620                offset = word.size();621            } else if (tok_pre == LLAMA_VOCAB_PRE_TYPE_GEMMA4 && word.find_first_not_of('\n') == std::string::npos) {622                // fix for gemma 4, ref: https://github.com/ggml-org/llama.cpp/pull/21343623                auto tok = vocab.text_to_token(word);624                if (tok != LLAMA_TOKEN_NULL) {625                    symbols.emplace_back(llm_symbol{-1, -1, word.c_str(), word.size()});626                    offset = word.size();627                }628            }629 630            while (offset < word.size()) {631                llm_symbol sym;632                size_t char_len = std::min(word.size() - offset, (size_t) unicode_len_utf8(word[offset]));633                sym.text = word.c_str() + offset;634                sym.n = char_len;635                offset += sym.n;636                sym.prev = index - 1;637                sym.next = offset == word.size() ? -1 : index + 1;638                index++;639                symbols.emplace_back(sym);640            }641            for (int i = 1; i < (int) symbols.size(); ++i) {642                add_new_bigram(i - 1, i);643            }644 645            // build token(s)646            while (!work_queue.empty()) {647                auto bigram = work_queue.pop_move();648 649                auto & left_symbol = symbols[bigram.left];650                auto & right_symbol = symbols[bigram.right];651 652                if (left_symbol.n == 0 || right_symbol.n == 0) {653                    continue;654                }655                std::string left_token = std::string(left_symbol.text, left_symbol.n);656                std::string right_token = std::string(right_symbol.text, right_symbol.n);657                if (left_token + right_token != bigram.text) {658                    continue;  // Skip this bigram if it's outdated659                }660 661                // merge the right sym into the left one662                left_symbol.n += right_symbol.n;663                right_symbol.n = 0;664 665                // remove the right sym from the chain666                left_symbol.next = right_symbol.next;667                if (right_symbol.next >= 0) {668                    symbols[right_symbol.next].prev = bigram.left;669                }670 671                add_new_bigram(left_symbol.prev, bigram.left);  // left side of current symbol672                add_new_bigram(bigram.left, left_symbol.next);  // right side of current symbol673            }674 675            // add the finished tokens to the final list keeping correct order for next and prev676            for (auto & sym : symbols) {677                if (sym.n > 0) {678                    sym.prev = final_prev_index;679                    sym.next = -1;680                    if (final_prev_index != -1) {681                        symbols_final[final_prev_index].next = symbols_final.size();682                    }683                    symbols_final.emplace_back(sym);684                    final_prev_index = symbols_final.size() - 1;685                }686            }687        }688 689        symbols = symbols_final;690 691        if (!symbols.empty()) {692            for (int i = 0; i != -1; i = symbols[i].next) {693                auto & symbol = symbols[i];694                if (symbol.n == 0) {695                    continue;696                }697 698                const std::string str = std::string(symbol.text, symbol.n);699                const auto token = vocab.text_to_token(str);700 701                if (token == LLAMA_TOKEN_NULL) {702                    for (auto j = str.begin(); j != str.end(); ++j) {703                        llama_token token_multibyte = LLAMA_TOKEN_NULL;704                        if (tokenizer.byte_encode) {705                            std::string byte_str(1, *j);706                            token_multibyte = vocab.text_to_token(byte_str);707                        } else {708                            // For non-byte-encoded BPE (e.g. gemma-4), byte tokens use <0xXX> format709                            static const char * hex = "0123456789ABCDEF";710                            const uint8_t ch = (uint8_t)*j;711                            const char buf[7] = { '<', '0', 'x', hex[ch >> 4], hex[ch & 15], '>', 0 };712                            token_multibyte = vocab.text_to_token(buf);713                        }714                        if (token_multibyte != LLAMA_TOKEN_NULL) {715                            output.push_back(token_multibyte);716                        }717                    }718                } else {719                    output.push_back(token);720                }721            }722        }723    }724 725private:726    void add_new_bigram(int left, int right) {727        if (left == -1 || right == -1) {728            return;729        }730        std::string left_token  = std::string(symbols[left].text,  symbols[left].n);731        std::string right_token = std::string(symbols[right].text, symbols[right].n);732 733        int rank_found = -1;734 735        rank_found = vocab.find_bpe_rank(left_token, right_token);736 737        if (rank_found < 0) {738            return;739        }740 741        llm_bigram_bpe bigram;742 743        bigram.left  = left;744        bigram.right = right;745        bigram.text  = left_token + right_token;746        bigram.size  = left_token.size() + right_token.size();747        bigram.rank  = rank_found;748 749        work_queue.push(bigram);750    }751 752    const llama_vocab & vocab;753    const llm_tokenizer_bpe & tokenizer;754 755    std::vector<llm_symbol> symbols;756    std::vector<llm_symbol> symbols_final;757    llm_bigram_bpe::queue work_queue;758};759 760//761// WPM tokenizer762//763 764struct llm_tokenizer_wpm : llm_tokenizer {765    llm_tokenizer_wpm(const llama_vocab & /*vocab*/) {}766};767 768struct llm_tokenizer_wpm_session {769    llm_tokenizer_wpm_session(const llama_vocab & vocab) : vocab(vocab) {}770 771    void tokenize(const std::string & text, std::vector<llama_token> & output) {772        // normalize and split by whitespace773        std::vector<std::string> words = preprocess(text, vocab.get_normalizer_opts());774        // bos token prepended already775 776        // find the longest tokens that form the words777        for (const std::string & word : words) {778            // skip empty words779            if (word.size() == 0) {780                continue;781            }782 783            // prepend phantom space784            const std::string word1 = "\xe2\x96\x81" + word;785            const int n = word1.size();786 787            const size_t current_tokens = output.size();788 789            // we're at the start of a new word790            // move through character position in word791            for (int i = 0; i < n; ++i) {792                // loop through possible match length793                bool match = false;794                for (int j = std::min(n, i + vocab.max_token_len() + 1); j > i; j--) {795                    auto id = vocab.text_to_token(word1.substr(i, j - i));796                    if (id != LLAMA_TOKEN_NULL) {797                        output.push_back(id);798                        match = true;799                        i = j - 1;800                        break;801                    }802                }803 804                if (!match) { // discard all805                    output.resize(current_tokens);806                    break;  // and discard next tokens807                }808            }809 810            // we didn't find any matches for this word811            if (current_tokens == output.size()) {812                output.push_back(vocab.token_unk());813            }814        }815    }816 817    // TODO: reduce string copies by using cpts_offs array818    static std::vector<std::string> preprocess(const std::string & text, const llama_vocab::normalizer_options & normalizer_opts)  {819        std::vector<uint32_t> cpts = unicode_cpts_from_utf8(text);820        if (normalizer_opts.strip_accents) {821            cpts = unicode_cpts_normalize_nfd(cpts);822        }823        std::vector<std::string> words(1, "");824 825        for (const uint32_t cpt : cpts) {826            const auto flags = unicode_cpt_flags_from_cpt(cpt);827 828            if (flags.is_whitespace) {829                if (words.back().size()) {  // finish previous word if any830                    words.emplace_back();831                }832                continue;833            }834 835            assert (!flags.is_separator);836            if (cpt == 0 || cpt == 0xFFFD || flags.is_control) {837                continue;838            }839 840            if (normalizer_opts.strip_accents && flags.is_accent_mark) {841                continue;842            }843 844            const std::string s = unicode_cpt_to_utf8(normalizer_opts.lowercase ? unicode_tolower(cpt) : cpt);845            if (flags.is_punctuation || ( cpt < 0x7F && flags.is_symbol ) || is_chinese_char(cpt)) {846                if (words.back().size()) {  // finish previous word if any847                    words.emplace_back();848                }849                words.back() = s;       // single char word850                words.emplace_back();   // start a new word851            } else {852                words.back() += s;  // append char to word853            }854        }855 856        if (!words.back().size()) {857            words.pop_back();858        }859 860        return words;861    }862 863    static bool is_chinese_char(uint32_t cpt) {864        return865            (cpt >= 0x04E00 && cpt <= 0x09FFF) ||866            (cpt >= 0x03400 && cpt <= 0x04DBF) ||867            (cpt >= 0x20000 && cpt <= 0x2A6DF) ||868            (cpt >= 0x2A700 && cpt <= 0x2B73F) ||869            (cpt >= 0x2B740 && cpt <= 0x2B81F) ||870            (cpt >= 0x2B920 && cpt <= 0x2CEAF) || // this should be 0x2B820 but in hf rust code it is 0x2B920871            (cpt >= 0x0F900 && cpt <= 0x0FAFF) ||872            (cpt >= 0x2F800 && cpt <= 0x2FA1F);873            //(cpt >= 0x3000  && cpt <= 0x303F)  ||874            //(cpt >= 0xFF00  && cpt <= 0xFFEF);875    }876 877private:878    const llama_vocab & vocab;879    // currently unused880    // const llm_tokenizer_wpm * wpm_tokenizer;881};882 883//884// UGM tokenizer885//886 887struct llm_tokenizer_ugm : llm_tokenizer {888    llm_tokenizer_ugm(const llama_vocab & vocab, const std::vector<char> & precompiled_charsmap) {889        if (precompiled_charsmap.size() > 0) {890            size_t charsmap_offset = 0;891 892            // First four bytes of precompiled_charsmap contains length of binary893            // blob containing XOR-compressed compact double array (XCDA) entries894            uint32_t xcda_blob_size = *(const uint32_t *) &precompiled_charsmap[0];895            charsmap_offset += sizeof(xcda_blob_size);896 897            // Next xcda_blob_size bytes contain entries of XOR-compressed compact898            // double array (XCDA). Each entry is bit-packed into a 32-bit integer.899            xcda_array = (const uint32_t *) &precompiled_charsmap[charsmap_offset];900            xcda_array_size = xcda_blob_size / sizeof(uint32_t);901            charsmap_offset += xcda_blob_size;902 903            // Remaining bytes of precompiled charsmap contain null-terminated904            // replacement strings for prefixes matched by the XCDA.905            prefix_replacements = &precompiled_charsmap[charsmap_offset];906            prefix_replacements_size = precompiled_charsmap.size() - charsmap_offset;907        }908 909        for (uint32_t id = 0; id < vocab.n_tokens(); ++id) {910            const auto & token_data = vocab.get_token_data(id);911 912            if (vocab.is_normal(id)) {913                min_score = std::min<float>(min_score, token_data.score);914                max_score = std::max<float>(max_score, token_data.score);915            }916 917            if (vocab.is_normal(id) ||918                vocab.is_user_defined(id) ||919                vocab.is_unused(id)) {920                token_matcher.insert(token_data.text.data(), token_data.text.size(), id);921            }922 923            if (vocab.is_user_defined(id)) {924                user_defined_token_matcher.insert(token_data.text.data(), token_data.text.size());925            }926        }927 928        unknown_token_score = min_score - unknown_token_score_penalty;929    }930 931    // escaped space symbol - U+2581 (Lower One Eighth Block)932    const std::string escaped_space = "\xE2\x96\x81";933 934    const char * prefix_replacements = NULL;935    size_t prefix_replacements_size = 0;936 937    const uint32_t * xcda_array = NULL;938    size_t xcda_array_size = 0;939 940    struct naive_trie user_defined_token_matcher;941 942    float min_score = FLT_MAX;943    float max_score = -FLT_MAX;944 945    float unknown_token_score_penalty = 10.0;946    float unknown_token_score;947 948    struct naive_trie token_matcher;949};950 951struct llm_tokenizer_ugm_session {952    llm_tokenizer_ugm_session(const llama_vocab & vocab, const llm_tokenizer_ugm & tokenizer) : vocab(vocab), tokenizer(tokenizer) {}953 954    /* This implementation is based on SentencePiece optimized Viterbi algorithm for955     * unigram language models. The general idea is to:956     * - move along the input sequence in steps of one UTF code point,957     * - at each step find all possible tokenizations of the prefix by958     *   traversing the tokens trie,959     * - for each tokenization store the best one so far (by higher score)960     * - use the position in sequence after given token as an index to store961     *   results962     * - if there was no valid tokenization of the current UTF code point963     *   then use unknown token with additional score penalty964     * After processing the whole sequence we backtrack from the end to get965     * the best tokenization.966    */967    void tokenize(const std::string & text, std::vector<llama_token> & output) {968        // get current size of output (for reversal later)969        size_t output_size = output.size();970 971        // normalize the input first972        std::string normalized;973        normalize(text, &normalized);974        size_t input_len = normalized.size();975        if (input_len == 0) {976            return;977        }978 979        // initialize score_sum to -FLT_MAX so it will be always lower than sums of token scores980        std::vector<struct best_tokenization> tokenization_results(input_len + 1, {vocab.token_unk(), 0, -DBL_MAX});981        // at the beginning tokenization score is zero982        tokenization_results[0] = { vocab.token_unk(), 0, 0 };983 984        for (size_t input_offset = 0; input_offset < input_len;) {985            size_t prefix_offset = input_offset;986            // calculate how many code units are in the currently processed UTF code point987            size_t n_utf8_code_units = std::min<size_t>(unicode_len_utf8(normalized[input_offset]), input_len - input_offset);988 989            // traverse the token matcher trie to find a matching token990            bool single_codepoint_token_found = false;991            const struct best_tokenization & current_best = tokenization_results[input_offset];992            const struct naive_trie * node = tokenizer.token_matcher.traverse(normalized[prefix_offset++]);993 994            while (prefix_offset <= input_len && node != NULL) {995                // check if we found valid token in prefix996                if (node->has_value) {997                    // check if it corresponds to the whole UTF code point998                    if (prefix_offset - input_offset == n_utf8_code_units) {999                        single_codepoint_token_found = true;1000                    }1001                    llama_token token_id = node->value;1002                    const auto & token_data = vocab.get_token_data(token_id);1003 1004                    // we set the user-defined token scores to 0 to make them more likely to be selected1005                    // (normal token scores are log probabilities, so they are negative)1006                    // score type is double here to make tokenization results exactly1007                    // the same as in the HF tokenizer using SentencePiece1008                    const double token_score = vocab.is_user_defined(token_id) ? 0.0 : token_data.score;1009                    const double challenger_score = current_best.score_sum + token_score;1010                    struct best_tokenization & current_champ = tokenization_results[prefix_offset];1011                    if (challenger_score > current_champ.score_sum) {1012                        struct best_tokenization challenger = { token_id, input_offset, challenger_score };1013                        current_champ = challenger;1014                    }1015                }1016                node = node->traverse(normalized[prefix_offset++]);1017            }1018 1019            // if we didn't find a valid token corresponding to the whole UTF code point1020            // then use unknown token as the tokenization of this UTF code point1021            if (!single_codepoint_token_found) {1022                const double challenger_score = current_best.score_sum + tokenizer.unknown_token_score;1023                prefix_offset = input_offset + n_utf8_code_units;1024                struct best_tokenization & current_champ = tokenization_results[prefix_offset];1025                if (challenger_score > current_champ.score_sum) {1026                    struct best_tokenization challenger = { vocab.token_unk(), input_offset, challenger_score };1027                    current_champ = challenger;1028                }1029            }1030 1031            // move to the next UTF code point1032            input_offset += n_utf8_code_units;1033        }1034 1035        // now backtrack from the end to gather token ids of the best tokenization1036        // merge sequences of consecutive unknown tokens into single unknown tokens1037        bool is_prev_unknown = false;1038        for (struct best_tokenization & tokenization = tokenization_results[input_len]; ; tokenization = tokenization_results[tokenization.input_offset]) {1039            bool is_unknown = tokenization.token_id == vocab.token_unk();1040            if (!(is_prev_unknown && is_unknown)) {1041                output.push_back(tokenization.token_id);1042            }1043            if (tokenization.input_offset == 0) {1044                break;1045            }1046            is_prev_unknown = is_unknown;1047        }1048 1049        // reverse the output since we added tokens starting from the end of the input1050        std::reverse(output.begin() + output_size, output.end());1051    }1052 1053private:1054 1055    // helper structure for returning normalization results1056    struct normalization_result {1057        const char * normalized;1058        size_t normalized_len;1059        size_t consumed_input;1060    };1061 1062    void normalize(const std::string& input, std::string * normalized) {1063        normalized->clear();1064        normalized->reserve(input.size() * 3);1065 1066        const std::string space = vocab.get_escape_whitespaces() ? tokenizer.escaped_space : " ";1067 1068        const bool shall_prepend_space = !vocab.get_treat_whitespace_as_suffix() && vocab.get_add_space_prefix();1069        const bool shall_append_space  =  vocab.get_treat_whitespace_as_suffix() && vocab.get_add_space_prefix();1070        const bool shall_merge_spaces  =  vocab.get_remove_extra_whitespaces();1071 1072        bool is_space_prepended = false;1073        bool processing_non_ws = false;1074 1075        size_t input_len = input.size();1076 1077        for (size_t input_offset = 0; input_offset < input_len; ) {1078            auto norm_res = normalize_prefix(input, input_offset);1079            for (size_t i = 0; i < norm_res.normalized_len; i++) {1080                char c = norm_res.normalized[i];1081                if (c != ' ') {1082                    if (!processing_non_ws) {1083                        processing_non_ws = true;1084                        if ((shall_prepend_space && !is_space_prepended) || shall_merge_spaces) {1085                            normalized->append(space);1086                            is_space_prepended = true;1087                        }1088                    }1089                    normalized->push_back(c);1090                } else {1091                    if (processing_non_ws) {1092                        processing_non_ws = false;1093                    }1094                    if (!shall_merge_spaces) {1095                        normalized->append(space);1096                    }1097                }1098            }1099 1100            input_offset += norm_res.consumed_input;1101        }1102 1103        if (shall_append_space) {1104            normalized->append(space);1105        }1106    }1107 1108    /*1109     * This structure is a view wrapper for XOR-compressed double array (XCDA)1110     * See Shunsuke Kanda (2018). Space- and Time-Efficient String Dictionaries.1111     * Each bit-packed entry contains:1112     * - BASE array value in bits 10-301113     * - LCHECK array value in bits 0-71114     * - LEAF array value in bit 91115     * Entries containing indexes of replacement sequences have set bit 311116     */1117    struct xcda_array_view {1118    public:1119        xcda_array_view(const uint32_t * xcda_array, size_t xcda_array_size) : xcda_array(xcda_array), xcda_array_size(xcda_array_size) {1120        }1121        uint32_t get_base(size_t index) {1122            uint32_t packed_node = get_node(index);1123            return (packed_node >> 10) << ((packed_node & (1U << 9)) >> 6);1124        }1125        uint32_t get_lcheck(size_t index) {1126            uint32_t packed_node = get_node(index);1127            return packed_node & ((1U << 31) | 0xff);1128        }1129        bool get_leaf(size_t index) {1130            uint32_t packed_node = get_node(index);1131            return (packed_node >> 8) & 1;1132        }1133        uint32_t get_value(size_t index) {1134            uint32_t packed_node = get_node(index);1135            return packed_node & ((1U << 31) - 1);1136        }1137    private:1138        uint32_t get_node(size_t index) {1139            if (index >= xcda_array_size) {1140                throw std::runtime_error("Index out of array bounds in XCDA array!");1141            }1142            return xcda_array[index];1143        }1144        const uint32_t * xcda_array;1145        size_t xcda_array_size;1146    };1147 1148    // this structure stores the best tokenization so far at input_offset1149    struct best_tokenization {1150        llama_token token_id;1151        size_t input_offset;1152        double score_sum;1153    };1154 1155    struct normalization_result normalize_prefix(const std::string & input, size_t input_offset) {1156        if (input_offset == input.size()) {1157            return { &input[input_offset], 0, 0 };1158        }1159 1160        // if input prefix matches some user-defined token return this token as normalization result1161        auto user_defined_token_match =1162           tokenizer.user_defined_token_matcher.get_longest_prefix(&input[input_offset], input.size() - input_offset);1163        if (user_defined_token_match.second > 0) {1164            return { &input[input_offset], user_defined_token_match.second, user_defined_token_match.second };1165        }1166 1167        size_t longest_prefix_length = 0;1168        size_t longest_prefix_offset = 0;1169 1170        if (tokenizer.xcda_array_size > 0) {1171            struct xcda_array_view xcda_view(tokenizer.xcda_array, tokenizer.xcda_array_size);1172 1173            // Find the longest normalized sequence matching the input prefix by walking1174            // the XOR-compressed compact double array (XCDA) starting from the root node1175            // We find the index of the next node by calculating BASE[s] ^ c where s is1176            // the index of the previous node and c is a numerical character value1177            uint32_t node_index = 0;1178            // get BASE of the root node1179            node_index = xcda_view.get_base(node_index);1180            for (size_t prefix_offset = input_offset; prefix_offset < input.size(); prefix_offset++) {1181                unsigned char c = input[prefix_offset];1182                if (c == 0) {1183                    break;1184                }1185                node_index ^= c;1186                // if value of LCHECK is not c it means that this is not a child of1187                // the previous node, so we stop matching1188                if (xcda_view.get_lcheck(node_index) != c) {1189                    break;1190                }1191                bool is_leaf = xcda_view.get_leaf(node_index);1192                // get BASE of the current node1193                node_index ^= xcda_view.get_base(node_index);1194                // if LEAF of the current node is true, it means that its BASE points to the node1195                // containing index of replacement sequence for currently matched input prefix1196                if (is_leaf)1197                {1198                    longest_prefix_length = prefix_offset - input_offset + 1;1199                    // get index of replacement sequence for currently matched input prefix1200                    longest_prefix_offset = xcda_view.get_value(node_index);

Showing the first 1,200 of 4398 lines. Download the file for the rest.

Brunobkr/llama.cpp_AlgMor24_github · Team Ai