char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
4801e3c56
char/aliceai-t5-moe
1#include "ggml.h" 2#include "llama.h" 3#include "llama-cpp.h" 4#include "common.h" 5 6#ifdef NDEBUG 7#undef NDEBUG 8#endif 9 10#include <algorithm> 11#include <cmath> 12#include <cstdlib> 13#include <cstring> 14#include <fstream> 15#include <functional> 16#include <map> 17#include <random> 18#include <string> 19#include <unordered_map> 20#include <unordered_set> 21#include <vector> 22 23struct test_args { 24 std::string model ; 25 std::string test ; 26 std::string device = "auto" ; 27}; 28 29struct test_params { 30llama_model_ptr model ; 31}; 32 33static llama_model_ptr load_model (const test_args & args ) { 34auto mparams = llama_model_default_params (); 35 36ggml_backend_dev_t devs [2 ]= {nullptr ,nullptr }; 37 38if (args .device != "auto" ) { 39if (args .device == "gpu" ) { 40devs [0 ]= ggml_backend_dev_by_type (GGML_BACKEND_DEVICE_TYPE_GPU ); 41 42if (devs [0 ]== nullptr ) { 43fprintf (stderr ,"Error: GPU requested but not available\n" ); 44return nullptr ; 45 } 46 47mparams .n_gpu_layers = 999 ; 48 }else if (args .device == "cpu" ) { 49devs [0 ]= ggml_backend_dev_by_type (GGML_BACKEND_DEVICE_TYPE_CPU ); 50 51mparams .n_gpu_layers = 0 ; 52 }else { 53fprintf (stderr ,"Error: invalid device '%s'\n" ,args .device .c_str ()); 54return nullptr ; 55 } 56 57mparams .devices = devs ; 58 59fprintf (stderr ,"Using device: %s\n" ,ggml_backend_dev_name (devs [0 ])); 60 } 61 62llama_model_ptr res ; 63 64res .reset (llama_model_load_from_file (args .model .c_str (),mparams )); 65 66if (!res ) { 67fprintf (stderr ,"Warning: failed to load model '%s', skipping test\n" ,args .model .c_str ()); 68return nullptr ; 69 } 70 71return res ; 72} 73 74struct test_context { 75llama_context_ptr ctx ; 76 77int n_vocab = 0 ; 78 79const llama_vocab * vocab = nullptr ; 80 81 std::unordered_map < llama_seq_id ,int32_t > seq_positions ; 82 std::unordered_map < llama_seq_id ,int32_t > last_batch_info ; 83 84test_context ( 85const test_params & params , 86 std::vector < llama_sampler_seq_config > & configs , 87int32_t n_seq_max = -1 , 88uint32_t n_outputs_max = 0 , 89uint32_t n_ubatch = 0 , 90uint32_t n_outputs_max_per_seq = 1 ) { 91auto * model = params .model .get (); 92 93GGML_ASSERT (model ); 94GGML_ASSERT (!ctx ); 95 96llama_context_params cparams = llama_context_default_params (); 97cparams .n_ctx = 512 ; 98cparams .n_batch = 512 ; 99if (n_ubatch > 0 ) { 100cparams .n_ubatch = n_ubatch ; 101 } 102cparams .n_outputs_max = n_outputs_max ; 103cparams .n_outputs_max_per_seq = n_outputs_max_per_seq ; 104cparams .samplers = configs .data (); 105cparams .n_samplers = configs .size (); 106cparams .kv_unified = true; 107 108// If n_seq_max is not specified, calculate it from configs 109if (n_seq_max < 0 ) { 110int32_t max_seq_id = 0 ; 111for (const auto & config :configs ) { 112max_seq_id = std::max (config .seq_id ,max_seq_id ); 113 } 114cparams .n_seq_max = max_seq_id + 1 ; 115 }else { 116cparams .n_seq_max = n_seq_max ; 117 } 118 119ctx .reset (llama_init_from_model (model ,cparams )); 120if (!ctx ) { 121throw std::runtime_error ("failed to create context" ); 122 } 123 124vocab = llama_model_get_vocab (model ); 125n_vocab = llama_vocab_n_tokens (vocab ); 126 } 127 128bool decode (const std::map < llama_seq_id , std::string > & prompts ) { 129GGML_ASSERT (ctx ); 130 131last_batch_info .clear (); 132llama_batch batch = llama_batch_init (512 ,0 ,prompts .size ()); 133 134for (const auto & [seq_id ,prompt ] :prompts ) { 135 std::vector < llama_token > tokens ; 136tokens .push_back (llama_vocab_bos (vocab )); 137 138 std::vector < llama_token > prompt_tokens (32 ); 139int n_tokens = llama_tokenize (vocab ,prompt .c_str (),prompt .length (), 140prompt_tokens .data (),prompt_tokens .size (), 141 false, false); 142if (n_tokens < 0 ) { 143fprintf (stderr ,"Warning: tokenization failed for seq_id %d\n" ,seq_id ); 144llama_batch_free (batch ); 145return false; 146 } 147 148for (int i = 0 ;i < n_tokens ;i ++ ) { 149tokens .push_back (prompt_tokens [i ]); 150 } 151 152if (seq_positions .find (seq_id )== seq_positions .end ()) { 153seq_positions [seq_id ]= 0 ; 154 } 155 156int32_t start_pos = seq_positions [seq_id ]; 157for (size_t i = 0 ;i < tokens .size ();i ++ ) { 158common_batch_add (batch ,tokens [i ],start_pos + i , {seq_id },i == tokens .size ()- 1 ); 159 } 160 161seq_positions [seq_id ]= start_pos + tokens .size (); 162 } 163 164 165printf ("Batch contents:\n" ); 166printf ("n_tokens: %d\n" ,batch .n_tokens ); 167for (int i = 0 ;i < batch .n_tokens ;i ++ ) { 168printf ("token[%d]: tok=%-5d, pos=%d, n_seq_id=%d, seq_ids=[" ,i ,batch .token [i ],batch .pos [i ],batch .n_seq_id [i ]); 169 170for (int j = 0 ;j < batch .n_seq_id [i ];j ++ ) { 171printf ("%d%s" ,batch .seq_id [i ][j ],j < batch .n_seq_id [i ]- 1 ?", " :"" ); 172 } 173printf ("], logits=%d\n" ,batch .logits [i ]); 174 } 175 176if (llama_decode (ctx .get (),batch )!= 0 ) { 177fprintf (stderr ,"Warning: llama_decode failed\n" ); 178llama_batch_free (batch ); 179return false; 180 } 181 182// Build mapping from seq id to batch token idx 183for (int i = 0 ;i < batch .n_tokens ;i ++ ) { 184if (batch .logits [i ]) { 185llama_seq_id seq_id = batch .seq_id [i ][0 ]; 186last_batch_info [seq_id ]= i ; 187 } 188 } 189 190llama_batch_free (batch ); 191return true; 192 } 193 194int32_t idx_for_seq (llama_seq_id seq_id ) { 195auto it = last_batch_info .find (seq_id ); 196if (it == last_batch_info .end ()) { 197fprintf (stderr ,"Error: no batch index found for seq_id %d\n" ,seq_id ); 198return -1 ; 199 } 200return it -> second ; 201 } 202 203void update_batch_info (const llama_batch & batch ) { 204last_batch_info .clear (); 205for (int i = 0 ;i < batch .n_tokens ;i ++ ) { 206if (batch .logits [i ]) { 207llama_seq_id cur_seq = batch .seq_id [i ][0 ]; 208last_batch_info [cur_seq ]= i ; 209 } 210 } 211 } 212 213bool decode_token (llama_token token ,llama_seq_id seq_id = 0 ) { 214GGML_ASSERT (ctx ); 215 216llama_batch batch = llama_batch_init (1 ,0 ,1 ); 217int32_t pos = seq_positions [seq_id ]; 218common_batch_add (batch ,token ,pos , {seq_id }, true); 219 220if (llama_decode (ctx .get (),batch )!= 0 ) { 221fprintf (stderr ,"Warning: llama_decode failed for token %d in seq %d\n" ,token ,seq_id ); 222llama_batch_free (batch ); 223return false; 224 } 225 226update_batch_info (batch ); 227 228seq_positions [seq_id ]++ ; 229llama_batch_free (batch ); 230 231return true; 232 } 233 234bool decode_tokens (const std::map < llama_seq_id ,llama_token > & seq_tokens ) { 235GGML_ASSERT (ctx ); 236 237llama_batch batch = llama_batch_init (seq_tokens .size (),0 ,seq_tokens .size ()); 238 239for (const auto & [seq_id ,token ] :seq_tokens ) { 240int32_t pos = seq_positions [seq_id ]; 241common_batch_add (batch ,token ,pos , {seq_id }, true); 242 } 243 244if (llama_decode (ctx .get (),batch )!= 0 ) { 245fprintf (stderr ,"Warning: llama_decode failed for batch tokens\n" ); 246llama_batch_free (batch ); 247return false; 248 } 249 250for (const auto & [seq_id ,_ ] :seq_tokens ) { 251seq_positions [seq_id ]++ ; 252 } 253 254update_batch_info (batch ); 255 256llama_batch_free (batch ); 257 258return true; 259 } 260 261 std::string token_to_piece (llama_token token ,bool special )const { 262 std::string piece ; 263piece .resize (piece .capacity ());// using string internal cache, 15 bytes + '\n' 264const int n_chars = llama_token_to_piece (vocab ,token ,& piece [0 ],piece .size (),0 ,special ); 265if (n_chars < 0 ) { 266piece .resize (- n_chars ); 267int check = llama_token_to_piece (vocab ,token ,& piece [0 ],piece .size (),0 ,special ); 268GGML_ASSERT (check == - n_chars ); 269 }else { 270piece .resize (n_chars ); 271 } 272 273return piece ; 274 } 275}; 276 277struct test_single_output_backend_sampler { 278bool backend_initialized = false; 279uint32_t backend_outputs_max_per_seq = 0 ; 280int backend_apply_count = 0 ; 281int apply_count = 0 ; 282}; 283 284static const char * test_single_output_backend_sampler_name (const llama_sampler * /*smpl*/ ) { 285return "single-output-backend" ; 286} 287 288static void test_single_output_backend_sampler_apply ( 289llama_sampler * smpl ,llama_token_data_array * /*cur_p*/ ) { 290auto * ctx = (test_single_output_backend_sampler * )smpl -> ctx ; 291ctx -> apply_count ++ ; 292} 293 294static void test_single_output_backend_sampler_free (llama_sampler * smpl ) { 295delete (test_single_output_backend_sampler * )smpl -> ctx ; 296} 297 298static bool test_single_output_backend_sampler_backend_init ( 299llama_sampler * smpl ,ggml_backend_buffer_type_t /*buft*/ ,uint32_t n_outputs_max_per_seq ) { 300auto * ctx = (test_single_output_backend_sampler * )smpl -> ctx ; 301ctx -> backend_outputs_max_per_seq = n_outputs_max_per_seq ; 302if (n_outputs_max_per_seq > 1 ) { 303return false; 304 } 305ctx -> backend_initialized = true; 306return true; 307} 308 309static void test_single_output_backend_sampler_backend_apply ( 310llama_sampler * smpl ,ggml_context * /*ctx*/ ,ggml_cgraph * /*gf*/ ,llama_sampler_data * /*data*/ ) { 311auto * ctx = (test_single_output_backend_sampler * )smpl -> ctx ; 312ctx -> backend_apply_count ++ ; 313} 314 315static llama_sampler_i test_single_output_backend_sampler_i = { 316/* .name = */ test_single_output_backend_sampler_name , 317/* .accept = */ nullptr , 318/* .apply = */ test_single_output_backend_sampler_apply , 319/* .reset = */ nullptr , 320/* .clone = */ nullptr , 321/* .free = */ test_single_output_backend_sampler_free , 322/* .backend_init = */ test_single_output_backend_sampler_backend_init , 323/* .backend_accept = */ nullptr , 324/* .backend_apply = */ test_single_output_backend_sampler_backend_apply , 325/* .backend_set_input = */ nullptr , 326/* .backend_reset = */ nullptr , 327/* .copy_state = */ nullptr , 328}; 329 330static llama_sampler * test_single_output_backend_sampler_init ( 331test_single_output_backend_sampler ** sampler_ctx ) { 332auto * ctx = new test_single_output_backend_sampler ; 333* sampler_ctx = ctx ; 334return llama_sampler_init (& test_single_output_backend_sampler_i ,ctx ); 335} 336 337static void test_backend_greedy_sampling (const test_params & params ) { 338const int seq_id = 0 ; 339 340struct llama_sampler_chain_params backend_sampler_params = llama_sampler_chain_default_params (); 341llama_sampler_ptr backend_sampler_chain (llama_sampler_chain_init (backend_sampler_params )); 342 343llama_sampler_chain_add (backend_sampler_chain .get (),llama_sampler_init_greedy ()); 344 std::vector < llama_sampler_seq_config > backend_sampler_configs = {{seq_id ,backend_sampler_chain .get () }}; 345 346test_context test_ctx (params ,backend_sampler_configs ); 347 348if (!test_ctx .decode ({{seq_id ,"Some" }})) { 349GGML_ASSERT (false&& "Failed to decode token" ); 350 } 351 352int32_t batch_idx = test_ctx .idx_for_seq (seq_id ); 353 354llama_token token = llama_get_sampled_token_ith (test_ctx .ctx .get (),batch_idx ); 355printf ("greedy sampled id:%d, string:'%s'\n" ,token ,test_ctx .token_to_piece (token , false).c_str ()); 356GGML_ASSERT (token >=0 && token < test_ctx .n_vocab ); 357 358token = llama_get_sampled_token_ith (test_ctx .ctx .get (),-1 ); 359printf ("greedy sampled id:%d, string:'%s'\n" ,token ,test_ctx .token_to_piece (token , false).c_str ()); 360GGML_ASSERT (token >=0 && token < test_ctx .n_vocab ); 361 362for (int i = 0 ;i < 10 ;i ++ ) { 363int32_t loop_idx = test_ctx .idx_for_seq (seq_id ); 364llama_token token = llama_get_sampled_token_ith (test_ctx .ctx .get (),loop_idx ); 365printf ("Generation step %d: token id:%d, string: %s\n" ,i ,token ,test_ctx .token_to_piece (token , false).c_str ()); 366if (!test_ctx .decode_token (token ,0 )) { 367GGML_ASSERT (false&& "Failed to decode token" ); 368 } 369 } 370} 371 372static void test_backend_top_k_sampling (const test_params & params ) { 373const int seq_id = 0 ; 374const int32_t k = 8 ; 375struct llama_sampler_chain_params backend_chain_params = llama_sampler_chain_default_params (); 376llama_sampler_ptr backend_sampler_chain (llama_sampler_chain_init (backend_chain_params )); 377llama_sampler_chain_add (backend_sampler_chain .get (),llama_sampler_init_top_k (k )); 378 std::vector < llama_sampler_seq_config > backend_sampler_configs = {{seq_id ,backend_sampler_chain .get () }}; 379 380test_context test_ctx (params ,backend_sampler_configs ); 381 382if (!test_ctx .decode ({{seq_id ,"Hello" }})) { 383GGML_ASSERT (false&& "Failed to decode token" ); 384 } 385 386int32_t batch_idx = test_ctx .idx_for_seq (seq_id ); 387 388float * logits = llama_get_sampled_logits_ith (test_ctx .ctx .get (),batch_idx ); 389uint32_t n_logits = llama_get_sampled_logits_count_ith (test_ctx .ctx .get (),batch_idx ); 390for (size_t i = 0 ;i < n_logits ;++ i ) { 391printf ("top_k logit[%zu] = %.6f\n" ,i ,logits [i ]); 392 } 393 394llama_token * candidates = llama_get_sampled_candidates_ith (test_ctx .ctx .get (),batch_idx ); 395uint32_t n_candidates = llama_get_sampled_candidates_count_ith (test_ctx .ctx .get (),batch_idx ); 396for (size_t i = 0 ;i < n_candidates ;++ i ) { 397printf ("top_k candidate[%zu] = %d : %s\n" ,i ,candidates [i ], 398test_ctx .token_to_piece (candidates [i ], false).c_str ()); 399 } 400 401// Sample using CPU sampler for verification that it is possible to do hybrid 402// sampling, first top_k on the backend and then dist on the CPU. 403struct llama_sampler_chain_params chain_params = llama_sampler_chain_default_params (); 404llama_sampler_ptr chain (llama_sampler_chain_init (chain_params )); 405GGML_ASSERT (chain -> iface -> backend_apply != nullptr ); 406 407llama_sampler_chain_add (chain .get (),llama_sampler_init_dist (18 )); 408llama_token token = llama_sampler_sample (chain .get (),test_ctx .ctx .get (),batch_idx ); 409GGML_ASSERT (token >=0 && token < test_ctx .n_vocab ); 410 411printf ("backend top-k hybrid sampling test PASSED\n" ); 412} 413 414static void test_backend_temp_sampling (const test_params & params ) { 415 { 416const float temp_0 = 0.8f ; 417struct llama_sampler_chain_params backend_chain_params_0 = llama_sampler_chain_default_params (); 418llama_sampler_ptr backend_sampler_chain_0 (llama_sampler_chain_init (backend_chain_params_0 )); 419llama_sampler_chain_add (backend_sampler_chain_0 .get (),llama_sampler_init_temp (temp_0 )); 420 421const float temp_1 = 0.1f ; 422struct llama_sampler_chain_params backend_chain_params_1 = llama_sampler_chain_default_params (); 423llama_sampler_ptr backend_sampler_chain_1 (llama_sampler_chain_init (backend_chain_params_1 )); 424llama_sampler_chain_add (backend_sampler_chain_1 .get (),llama_sampler_init_temp (temp_1 )); 425 426 std::vector < llama_sampler_seq_config > backend_sampler_configs = { 427 {0 ,backend_sampler_chain_0 .get () }, 428 {1 ,backend_sampler_chain_1 .get () } 429 }; 430 431test_context test_ctx (params ,backend_sampler_configs ); 432 433if (!test_ctx .decode ({{0 ,"Some where over the" }, {1 ,"Once upon a" }})) { 434GGML_ASSERT (false&& "Failed to decode token" ); 435 } 436 437// Verify sequence 0 438 { 439int32_t batch_idx = test_ctx .idx_for_seq (0 ); 440int n_logits = llama_get_sampled_logits_count_ith (test_ctx .ctx .get (),batch_idx ); 441GGML_ASSERT (n_logits == test_ctx .n_vocab ); 442 443// Sample from sequence 0 using CPU sampler 444struct llama_sampler_chain_params chain_params = llama_sampler_chain_default_params (); 445llama_sampler_ptr chain (llama_sampler_chain_init (chain_params )); 446llama_sampler_chain_add (chain .get (),llama_sampler_init_dist (18 )); 447 448llama_token token = llama_sampler_sample (chain .get (),test_ctx .ctx .get (),batch_idx ); 449const std::string token_str = test_ctx .token_to_piece (token , false); 450printf ("Sequence 0 sampled token id:%d, string: '%s'\n" ,token ,token_str .c_str ()); 451GGML_ASSERT (token >=0 && token < test_ctx .n_vocab ); 452 } 453 454 455// Verify sequence 1 456 { 457int32_t batch_idx = test_ctx .idx_for_seq (1 ); 458 459// Sample from sequence 1 using CPU sampler 460struct llama_sampler_chain_params chain_params = llama_sampler_chain_default_params (); 461llama_sampler_ptr chain (llama_sampler_chain_init (chain_params )); 462llama_sampler_chain_add (chain .get (),llama_sampler_init_dist (18 )); 463 464llama_token token = llama_sampler_sample (chain .get (),test_ctx .ctx .get (),batch_idx ); 465const std::string token_str = test_ctx .token_to_piece (token , false); 466printf ("Sequence 1 sampled token id:%d, string: '%s'\n" ,token ,token_str .c_str ()); 467GGML_ASSERT (token >=0 && token < test_ctx .n_vocab ); 468 } 469 } 470 471// lambda for testing non-positive temperature values. 472auto test_argmax_temp = [& ](float temp ) { 473printf ("\nTesting temperature = %.1f\n" ,temp ); 474 475int seq_id = 0 ; 476struct llama_sampler_chain_params backend_chain_params = llama_sampler_chain_default_params (); 477llama_sampler_ptr backend_sampler_chain (llama_sampler_chain_init (backend_chain_params )); 478llama_sampler_chain_add (backend_sampler_chain .get (),llama_sampler_init_temp (temp )); 479 480 std::vector < llama_sampler_seq_config > backend_sampler_configs = { 481 {seq_id ,backend_sampler_chain .get () }, 482 }; 483 484test_context test_ctx (params ,backend_sampler_configs ); 485 486if (!test_ctx .decode ({{seq_id ,"Once" }})) { 487GGML_ASSERT (false&& "Failed to decode token" ); 488 } 489 490int32_t batch_idx = test_ctx .idx_for_seq (seq_id ); 491 492uint32_t n_logits = llama_get_sampled_logits_count_ith (test_ctx .ctx .get (),batch_idx ); 493GGML_ASSERT (n_logits == 1 ); 494 }; 495 496test_argmax_temp (0.0f ); 497test_argmax_temp (-1.0f ); 498 499printf ("backend temp sampling test PASSED\n" ); 500} 501 502static void test_backend_temp_ext_sampling (const test_params & params ) { 503 { 504int seq_id = 0 ; 505const float temp = 0.8f ; 506const float delta = 0.5f ; 507const float exponent = 1.5f ; 508struct llama_sampler_chain_params backend_chain_params = llama_sampler_chain_default_params (); 509llama_sampler_ptr backend_sampler_chain (llama_sampler_chain_init (backend_chain_params )); 510llama_sampler_chain_add (backend_sampler_chain .get (),llama_sampler_init_temp_ext (temp ,delta ,exponent )); 511 512 std::vector < llama_sampler_seq_config > backend_sampler_configs = { 513 {seq_id ,backend_sampler_chain .get () }, 514 }; 515 516test_context test_ctx (params ,backend_sampler_configs ); 517 518if (!test_ctx .decode ({{seq_id ,"Once upon a" }})) { 519GGML_ASSERT (false&& "Failed to decode token" ); 520 } 521 522// Verify sequence 0 523 { 524int32_t batch_idx = test_ctx .idx_for_seq (seq_id ); 525int n_logits = llama_get_sampled_logits_count_ith (test_ctx .ctx .get (),batch_idx ); 526GGML_ASSERT (n_logits == test_ctx .n_vocab ); 527 } 528 } 529 530// lambda for testing non-positive temp/delta/exponent values. 531auto test_argmax_temp = [& ](float temp ,float delta ,float exponent ) { 532printf ("\nTesting temperature = %.1f, delta = %1.f, exponent = %1.f\n" ,temp ,delta ,exponent ); 533 534int seq_id = 0 ; 535struct llama_sampler_chain_params backend_chain_params = llama_sampler_chain_default_params (); 536llama_sampler_ptr backend_sampler_chain (llama_sampler_chain_init (backend_chain_params )); 537llama_sampler_chain_add (backend_sampler_chain .get (),llama_sampler_init_temp_ext (temp ,delta ,exponent )); 538 539 std::vector < llama_sampler_seq_config > backend_sampler_configs = { 540 {seq_id ,backend_sampler_chain .get () }, 541 }; 542 543test_context test_ctx (params ,backend_sampler_configs ); 544 545if (!test_ctx .decode ({{seq_id ,"Once" }})) { 546GGML_ASSERT (false&& "Failed to decode token" ); 547 } 548 549int32_t batch_idx = test_ctx .idx_for_seq (seq_id ); 550 551uint32_t n_logits = llama_get_sampled_logits_count_ith (test_ctx .ctx .get (),batch_idx ); 552 553if (temp <=0.0f && delta >=0.0f ) { 554GGML_ASSERT (n_logits == 1 ); 555 }else { 556GGML_ASSERT (n_logits == (uint32_t )test_ctx .n_vocab ); 557 } 558 }; 559 560test_argmax_temp (0.0f ,0.3f ,1.0f );// Greedy (temp=0) 561test_argmax_temp (-1.0f ,0.3f ,2.0f );// Greedy (temp<0) 562test_argmax_temp (0.8f ,0.0f ,2.0f );// Temperature scaling 563 564printf ("backend temp_ext sampling test PASSED\n" ); 565} 566 567static void test_backend_min_p_sampling (const test_params & params ) { 568const int seq_id = 0 ; 569const float p = 0.1 ; 570struct llama_sampler_chain_params backend_chain_params = llama_sampler_chain_default_params (); 571llama_sampler_ptr backend_sampler_chain (llama_sampler_chain_init (backend_chain_params )); 572llama_sampler_chain_add (backend_sampler_chain .get (),llama_sampler_init_min_p (p ,0 )); 573 std::vector < llama_sampler_seq_config > backend_sampler_configs = {{seq_id ,backend_sampler_chain .get () }}; 574 575test_context test_ctx (params ,backend_sampler_configs ); 576 577if (!test_ctx .decode ({{seq_id ,"Hello" }})) { 578GGML_ASSERT (false&& "Failed to decode token" ); 579 } 580 581int32_t batch_idx = test_ctx .idx_for_seq (seq_id ); 582 583float * logits = llama_get_sampled_logits_ith (test_ctx .ctx .get (),batch_idx ); 584uint32_t n_logits = llama_get_sampled_logits_count_ith (test_ctx .ctx .get (),batch_idx ); 585 586// Print the logits that are above the min-p threshold 587 std::vector < float > filtered_logits ; 588for (size_t i = 0 ;i < n_logits ;++ i ) { 589if (logits [i ]> -1e9f ) { 590filtered_logits .push_back (logits [i ]); 591//printf("min_p logit[%zu] = %.6f\n", i, logits[i]); 592 } 593 } 594GGML_ASSERT (filtered_logits .size ()< (size_t )test_ctx .n_vocab ); 595 596// Sample using CPU sampler for verification to inspect they are reasonable 597struct llama_sampler_chain_params chain_params = llama_sampler_chain_default_params (); 598llama_sampler_ptr chain (llama_sampler_chain_init (chain_params )); 599llama_sampler_chain_add (chain .get (),llama_sampler_init_dist (88 )); 600 601llama_token token = llama_sampler_sample (chain .get (),test_ctx .ctx .get (),batch_idx ); 602const std::string token_str = test_ctx .token_to_piece (token , false); 603printf ("min-p cpu sampled token id:%d, string: '%s'\n" ,token ,token_str .c_str ()); 604GGML_ASSERT (token >=0 && token < test_ctx .n_vocab ); 605 606// Decode and sample 10 more tokens 607for (int i = 0 ;i < 10 ;i ++ ) { 608int32_t loop_idx = test_ctx .idx_for_seq (seq_id ); 609llama_token token = llama_sampler_sample (chain .get (),test_ctx .ctx .get (),loop_idx ); 610printf ("min-p gen step %d: token id :%5.d, string: %s\n" ,i ,token ,test_ctx .token_to_piece (token , false).c_str ()); 611if (!test_ctx .decode_token (token ,0 )) { 612GGML_ASSERT (false&& "Failed to decode token" ); 613 } 614 } 615 616printf ("min-p sampling test PASSED\n" ); 617} 618 619static void test_backend_top_p_sampling (const test_params & params ) { 620const int seq_id = 0 ; 621const float p = 0.9 ; 622struct llama_sampler_chain_params backend_chain_params = llama_sampler_chain_default_params (); 623llama_sampler_ptr backend_sampler_chain (llama_sampler_chain_init (backend_chain_params )); 624llama_sampler_chain_add (backend_sampler_chain .get (),llama_sampler_init_top_p (p ,0 )); 625 std::vector < llama_sampler_seq_config > backend_sampler_configs = {{seq_id ,backend_sampler_chain .get () }}; 626 627test_context test_ctx (params ,backend_sampler_configs ); 628 629if (!test_ctx .decode ({{seq_id ,"Hello" }})) { 630return ; 631 } 632 633int32_t batch_idx = test_ctx .idx_for_seq (seq_id ); 634 635float * logits = llama_get_sampled_logits_ith (test_ctx .ctx .get (),batch_idx ); 636uint32_t n_logits = llama_get_sampled_logits_count_ith (test_ctx .ctx .get (),batch_idx ); 637 638// Print the logits that are above the min-p threshold 639 std::vector < float > filtered_logits ; 640for (size_t i = 0 ;i < n_logits ;++ i ) { 641if (logits [i ]> -1e9f ) { 642filtered_logits .push_back (logits [i ]); 643 } 644 } 645GGML_ASSERT (filtered_logits .size ()< (size_t )test_ctx .n_vocab ); 646GGML_ASSERT (filtered_logits .size ()> 0 ); 647 648// Sample using CPU sampler for verification to inspect they are reasonable 649struct llama_sampler_chain_params chain_params = llama_sampler_chain_default_params (); 650llama_sampler_ptr chain (llama_sampler_chain_init (chain_params )); 651llama_sampler_chain_add (chain .get (),llama_sampler_init_dist (88 )); 652 653llama_token token = llama_sampler_sample (chain .get (),test_ctx .ctx .get (),batch_idx ); 654const std::string token_str = test_ctx .token_to_piece (token , false); 655printf ("top-p cpu sampled token id:%d, string: '%s'\n" ,token ,token_str .c_str ()); 656GGML_ASSERT (token >=0 && token < test_ctx .n_vocab ); 657 658// Decode and sample 10 more tokens 659for (int i = 0 ;i < 10 ;i ++ ) { 660int32_t loop_idx = test_ctx .idx_for_seq (seq_id ); 661llama_token token = llama_sampler_sample (chain .get (),test_ctx .ctx .get (),loop_idx ); 662printf ("top-p gen step %d: token id :%5.d, string: %s\n" ,i ,token ,test_ctx .token_to_piece (token , false).c_str ()); 663test_ctx .decode_token (token ,0 ); 664 } 665 666printf ("top-p sampling test PASSED\n" ); 667} 668 669static void test_backend_multi_sequence_sampling (const test_params & params ) { 670struct llama_sampler_chain_params chain_params_0 = llama_sampler_chain_default_params (); 671llama_sampler_ptr sampler_chain_0 (llama_sampler_chain_init (chain_params_0 )); 672llama_sampler_chain_add (sampler_chain_0 .get (),llama_sampler_init_greedy ()); 673 674struct llama_sampler_chain_params chain_params_1 = llama_sampler_chain_default_params (); 675llama_sampler_ptr sampler_chain_1 (llama_sampler_chain_init (chain_params_1 )); 676llama_sampler_chain_add (sampler_chain_1 .get (),llama_sampler_init_temp (0.8f )); 677llama_sampler_chain_add (sampler_chain_1 .get (),llama_sampler_init_greedy ()); 678 679 std::vector < llama_sampler_seq_config > backend_sampler_configs = { 680 {0 ,sampler_chain_0 .get () }, 681 {1 ,sampler_chain_1 .get () } 682 }; 683 684test_context test_ctx (params ,backend_sampler_configs ); 685 686 std::map < llama_seq_id , std::string > prompts = { 687 {0 ,"Hello" }, 688 {1 ,"Some" } 689 }; 690 691if (!test_ctx .decode (prompts )) { 692GGML_ASSERT (false&& "Failed to decode token" ); 693 } 694 695// Verify sequence 0 696 { 697int32_t batch_idx = test_ctx .idx_for_seq (0 ); 698llama_token token = llama_get_sampled_token_ith (test_ctx .ctx .get (),batch_idx ); 699const std::string token_str = test_ctx .token_to_piece (token , false); 700printf ("Seq 0 sampled token id=%d, string='%s'\n" ,token ,token_str .c_str ()); 701GGML_ASSERT (token >=0 && token < test_ctx .n_vocab ); 702 } 703 704// Verify sequence 1 705 { 706int32_t batch_idx = test_ctx .idx_for_seq (1 ); 707llama_token token = llama_get_sampled_token_ith (test_ctx .ctx .get (),batch_idx ); 708const std::string token_str = test_ctx .token_to_piece (token , false); 709printf ("Seq 1 sampled token id=%d, string='%s'\n" ,token ,token_str .c_str ()); 710GGML_ASSERT (token >=0 && token < test_ctx .n_vocab ); 711 } 712 713// Generate tokens for each sequence 714printf ("\nMulti-sequence generation:\n" ); 715for (int step = 0 ;step < 4 ;step ++ ) { 716 std::map < llama_seq_id ,llama_token > tokens ; 717 718for (llama_seq_id seq_id : {0 ,1 }) { 719int32_t idx = test_ctx .idx_for_seq (seq_id ); 720llama_token token = llama_get_sampled_token_ith (test_ctx .ctx .get (),idx ); 721const std::string token_str = test_ctx .token_to_piece (token , false); 722printf (" Seq %d, step %d: token id=%d, string='%s'\n" ,seq_id ,step ,token ,token_str .c_str ()); 723tokens [seq_id ]= token ; 724 } 725 726// Decode all tokens in a single batch 727if (!test_ctx .decode_tokens (tokens )) { 728GGML_ASSERT (false&& "Failed to decode token" ); 729 } 730 } 731 732printf ("backend multi-sequence sampling test PASSED\n" ); 733} 734 735static void test_backend_dist_sampling (const test_params & params ) { 736const int seq_id = 0 ; 737const int32_t seed = 88 ; 738 739struct llama_sampler_chain_params backend_chain_params = llama_sampler_chain_default_params (); 740llama_sampler_ptr backend_sampler_chain (llama_sampler_chain_init (backend_chain_params )); 741llama_sampler_chain_add (backend_sampler_chain .get (),llama_sampler_init_dist (seed )); 742 std::vector < llama_sampler_seq_config > backend_sampler_configs = {{seq_id ,backend_sampler_chain .get () }}; 743 744test_context test_ctx (params ,backend_sampler_configs ); 745 746if (!test_ctx .decode ({{seq_id ,"Some" }})) { 747GGML_ASSERT (false&& "Failed to decode token" ); 748 } 749 750int32_t batch_idx = test_ctx .idx_for_seq (seq_id ); 751llama_token token = llama_get_sampled_token_ith (test_ctx .ctx .get (),batch_idx ); 752printf ("dist sampled id:%d, string:'%s'\n" ,token ,test_ctx .token_to_piece (token , false).c_str ()); 753GGML_ASSERT (token >=0 && token < test_ctx .n_vocab ); 754//GGML_ASSERT(llama_get_sampled_logits_ith(test_ctx.ctx.get(), batch_idx) == nullptr); 755 756token = llama_get_sampled_token_ith (test_ctx .ctx .get (),-1 ); 757printf ("dist sampled id:%d, string:'%s'\n" ,token ,test_ctx .token_to_piece (token , false).c_str ()); 758GGML_ASSERT (token >=0 && token < test_ctx .n_vocab ); 759 760printf ("backend dist sampling test PASSED\n" ); 761} 762 763static void test_backend_dist_sampling_and_cpu (const test_params & params ) { 764const int seq_id = 0 ; 765const int32_t seed = 88 ; 766 767struct llama_sampler_chain_params backend_chain_params = llama_sampler_chain_default_params (); 768llama_sampler_ptr backend_sampler_chain (llama_sampler_chain_init (backend_chain_params )); 769llama_sampler_chain_add (backend_sampler_chain .get (),llama_sampler_init_dist (seed )); 770 std::vector < llama_sampler_seq_config > backend_sampler_configs = {{seq_id ,backend_sampler_chain .get () }}; 771 772test_context test_ctx (params ,backend_sampler_configs ); 773 774if (!test_ctx .decode ({{seq_id ,"Some" }})) { 775GGML_ASSERT (false&& "Failed to decode token" ); 776 } 777 778int32_t batch_idx = test_ctx .idx_for_seq (seq_id ); 779 780// Sample using CPU sampler 781struct llama_sampler_chain_params chain_params = llama_sampler_chain_default_params (); 782llama_sampler_ptr chain (llama_sampler_chain_init (chain_params )); 783llama_sampler_chain_add (chain .get (),llama_sampler_init_dist (18 )); 784 785llama_token backend_token = llama_get_sampled_token_ith (test_ctx .ctx .get (),batch_idx ); 786llama_token cpu_token = llama_sampler_sample (chain .get (),test_ctx .ctx .get (),batch_idx ); 787printf ("dist & cpu sampled id:%d, string:'%s'\n" ,cpu_token ,test_ctx .token_to_piece (cpu_token , false).c_str ()); 788GGML_ASSERT (backend_token == cpu_token ); 789 790printf ("backend dist & cpu sampling test PASSED\n" ); 791} 792 793static void test_backend_logit_bias_sampling (const test_params & params ) { 794const auto * model = params .model .get (); 795const auto * vocab = llama_model_get_vocab (model ); 796 797const int seq_id = 0 ; 798 799 std::vector < llama_logit_bias > logit_bias ; 800 801// Get the token for the piece "World". 802const std::string piece = "World" ; 803 std::vector < llama_token > tokens (16 ); 804llama_tokenize (vocab ,piece .c_str (),piece .size (),tokens .data (),tokens .size (), false, false); 805 806llama_token bias_token = tokens [0 ]; 807// TODO: biasing too much here makes the Vulkan sampling fail - should be investigated further 808// https://github.com/ggml-org/llama.cpp/actions/runs/20894267644/job/60030252675?pr=18753#step:3:23350 809//logit_bias.push_back({ bias_token, +100.0f }); 810logit_bias .push_back ({bias_token ,+10.0f }); 811 812printf ("biasing token piece '%s' -> token id %d\n" ,piece .c_str (),bias_token ); 813 814struct llama_sampler_chain_params backend_chain_params = llama_sampler_chain_default_params (); 815llama_sampler_ptr backend_sampler_chain (llama_sampler_chain_init (backend_chain_params )); 816llama_sampler_chain_add (backend_sampler_chain .get (),llama_sampler_init_logit_bias ( 817llama_vocab_n_tokens (vocab ), 818logit_bias .size (), 819logit_bias .data ())); 820llama_sampler_chain_add (backend_sampler_chain .get (),llama_sampler_init_dist (88 )); 821 822 std::vector < llama_sampler_seq_config > backend_sampler_configs = { 823 {seq_id ,backend_sampler_chain .get () }, 824 }; 825 826test_context test_ctx (params ,backend_sampler_configs ); 827 828if (!test_ctx .decode ({{seq_id ,"Hello" }})) { 829GGML_ASSERT (false&& "Failed to decode token" ); 830 } 831 832llama_token backend_token = llama_get_sampled_token_ith (test_ctx .ctx .get (),test_ctx .idx_for_seq (seq_id )); 833printf ("sampled token = %d, expected = %d\n" ,backend_token ,bias_token ); 834GGML_ASSERT (backend_token == bias_token ); 835 836printf ("backend logit bias sampling test PASSED\n" ); 837} 838 839static void accept_prompt (llama_sampler * smpl ,const llama_vocab * vocab ,const std::string & prompt ) { 840const llama_token bos = llama_vocab_bos (vocab ); 841if (bos != LLAMA_TOKEN_NULL ) { 842llama_sampler_accept (smpl ,bos ); 843 } 844 845 std::vector < llama_token > tokens (64 ); 846int32_t n_tokens = llama_tokenize (vocab ,prompt .c_str (), (int32_t )prompt .size (), 847tokens .data (), (int32_t )tokens .size (), false, false); 848if (n_tokens < 0 ) { 849tokens .resize (- n_tokens ); 850n_tokens = llama_tokenize (vocab ,prompt .c_str (), (int32_t )prompt .size (), 851tokens .data (), (int32_t )tokens .size (), false, false); 852 } 853 854for (int32_t i = 0 ;i < n_tokens ;++ i ) { 855llama_sampler_accept (smpl ,tokens [i ]); 856 } 857} 858 859static std::vector < float > decode_raw_logits (const test_params & params ,const std::string & prompt ) { 860const int seq_id = 0 ; 861const int n_vocab = llama_vocab_n_tokens (llama_model_get_vocab (params .model .get ())); 862 std::vector < llama_sampler_seq_config > empty_configs ; 863test_context ctx (params ,empty_configs ); 864 865GGML_ASSERT (ctx .decode ({{seq_id ,prompt }})); 866 867float * logits = llama_get_logits_ith (ctx .ctx .get (),ctx .idx_for_seq (seq_id )); 868GGML_ASSERT (logits != nullptr ); 869return std::vector < float > (logits ,logits + n_vocab ); 870} 871 872static std::vector < llama_token_data > apply_cpu_sampler ( 873const std::vector < float > & raw_logits , 874llama_sampler * sampler ) { 875 std::vector < llama_token_data > data ; 876data .reserve (raw_logits .size ()); 877for (llama_token token = 0 ;token < (llama_token )raw_logits .size ();++ token ) { 878data .push_back ({token ,raw_logits [token ],0.0f }); 879 } 880 881llama_token_data_array cur_p = {data .data (),data .size (),-1 , false }; 882llama_sampler_apply (sampler ,& cur_p ); 883data .resize (cur_p .size ); 884return data ; 885} 886 887using sampler_setup_fn = std::function < void (llama_sampler * )> ; 888using sampler_init_fn = std::function < llama_sampler * ()> ; 889 890enum class penalties_position { 891before_filter , 892after_filter , 893}; 894 895static void add_filter_and_penalties ( 896llama_sampler * chain , 897const sampler_init_fn & init_filter , 898int32_t n_vocab , 899int32_t penalty_last_n , 900float penalty_repeat , 901float penalty_freq , 902float penalty_present , 903penalties_position position ) { 904const auto add_penalties = [& ]() { 905llama_sampler_chain_add (chain ,llama_sampler_init_penalties ( 906n_vocab ,penalty_last_n ,penalty_repeat ,penalty_freq ,penalty_present )); 907 }; 908 909if (position == penalties_position::before_filter ) { 910add_penalties (); 911llama_sampler_chain_add (chain ,init_filter ()); 912 }else { 913llama_sampler_chain_add (chain ,init_filter ()); 914add_penalties (); 915 } 916} 917 918static llama_sampler_ptr make_sampler_chain ( 919const sampler_setup_fn & add_samplers , 920const sampler_setup_fn & accept_history ) { 921llama_sampler_ptr chain (llama_sampler_chain_init (llama_sampler_chain_default_params ())); 922add_samplers (chain .get ()); 923accept_history (chain .get ()); 924return chain ; 925} 926 927struct backend_sampler_output { 928 std::vector < float > logits ; 929 std::vector < llama_token > candidates ; 930}; 931 932static backend_sampler_output run_backend_sampler ( 933const test_params & params , 934const std::string & prompt , 935llama_sampler * sampler ) { 936const int seq_id = 0 ; 937 std::vector < llama_sampler_seq_config > configs = {{seq_id ,sampler }}; 938test_context ctx (params ,configs ); 939 940GGML_ASSERT (ctx .decode ({{seq_id ,prompt }})); 941llama_synchronize (ctx .ctx .get ()); 942 943const int32_t idx = ctx .idx_for_seq (seq_id ); 944const uint32_t n_logits = llama_get_sampled_logits_count_ith (ctx .ctx .get (),idx ); 945const uint32_t n_candidates = llama_get_sampled_candidates_count_ith (ctx .ctx .get (),idx ); 946float * logits = llama_get_sampled_logits_ith (ctx .ctx .get (),idx ); 947llama_token * candidates = llama_get_sampled_candidates_ith (ctx .ctx .get (),idx ); 948GGML_ASSERT (logits != nullptr ); 949 950backend_sampler_output result ; 951result .logits .assign (logits ,logits + n_logits ); 952result .candidates .resize (n_logits ); 953 954if (n_candidates == 0 ) { 955for (uint32_t i = 0 ;i < n_logits ;++ i ) { 956result .candidates [i ]= (llama_token )i ; 957 } 958 }else { 959GGML_ASSERT (candidates != nullptr ); 960GGML_ASSERT (n_candidates == n_logits ); 961 std::memcpy (result .candidates .data (),candidates ,n_candidates * sizeof (llama_token )); 962 } 963 964return result ; 965} 966 967struct sampler_comparison_output { 968 std::vector < llama_token_data > expected ; 969backend_sampler_output actual ; 970}; 971 972static sampler_comparison_output run_sampler_comparison ( 973const test_params & params , 974const std::string & prompt , 975const std::vector < float > & raw_logits , 976const sampler_setup_fn & add_samplers , 977const sampler_setup_fn & accept_history ) { 978llama_sampler_ptr cpu_chain = make_sampler_chain (add_samplers ,accept_history ); 979llama_sampler_ptr backend_chain = make_sampler_chain (add_samplers ,accept_history ); 980return { 981apply_cpu_sampler (raw_logits ,cpu_chain .get ()), 982run_backend_sampler (params ,prompt ,backend_chain .get ()), 983 }; 984} 985 986static std::unordered_map < llama_token ,float > map_logits (const std::vector < llama_token_data > & data ) { 987 std::unordered_map < llama_token ,float > result ; 988result .reserve (data .size ()); 989for (const auto & item :data ) { 990result [item .id ]= item .logit ; 991 } 992return result ; 993} 994 995struct sampler_comparison_stats { 996int n_mismatch = 0 ; 997int n_masked = 0 ; 998float max_diff = 0.0f ; 999}; 1000 1001static sampler_comparison_stats compare_sampler_outputs ( 1002const char * name , 1003const std::unordered_map < llama_token ,float > & expected , 1004const backend_sampler_output & actual , 1005bool allow_extra_candidates = false) { 1006GGML_ASSERT (actual .logits .size ()== actual .candidates .size ()); 1007 1008sampler_comparison_stats result ; 1009 std::unordered_set < llama_token > seen ; 1010seen .reserve (actual .candidates .size ()); 1011 1012for (size_t i = 0 ;i < actual .logits .size ();++ i ) { 1013const llama_token token = actual .candidates [i ]; 1014const float logit = actual .logits [i ]; 1015if (!seen .insert (token ).second || std::isnan (logit )) { 1016if (result .n_mismatch < 5 ) { 1017printf ("%s token %d has invalid backend output\n" ,name ,token ); 1018 } 1019++ result .n_mismatch ; 1020continue ; 1021 } 1022 1023const auto it = expected .find (token ); 1024if (it == expected .end ()) { 1025if (std::isinf (logit )&& logit < 0.0f ) { 1026++ result .n_masked ; 1027 }else if (!allow_extra_candidates ) { 1028if (result .n_mismatch < 5 ) { 1029printf ("%s token %d was not masked\n" ,name ,token ); 1030 } 1031++ result .n_mismatch ; 1032 } 1033continue ; 1034 } 1035 1036const float diff = fabsf (it -> second - logit ); 1037result .max_diff = std::max (result .max_diff ,diff ); 1038if (!std::isfinite (logit )|| diff > 1e-3f ) { 1039if (result .n_mismatch < 5 ) { 1040printf ("%s mismatch token %d: cpu=%.6f backend=%.6f diff=%.6f\n" , 1041name ,token ,it -> second ,logit ,diff ); 1042 } 1043++ result .n_mismatch ; 1044 } 1045 } 1046 1047for (const auto & item :expected ) { 1048if (seen .find (item .first )== seen .end ()) { 1049if (result .n_mismatch < 5 ) { 1050printf ("%s missing backend token %d\n" ,name ,item .first ); 1051 } 1052++ result .n_mismatch ; 1053 } 1054 } 1055 1056printf ("%s logits: max_diff=%.6f n_masked=%d n_mismatch=%d\n" , 1057name ,result .max_diff ,result .n_masked ,result .n_mismatch ); 1058return result ; 1059} 1060 1061static float find_backend_logit (const backend_sampler_output & output ,llama_token token ) { 1062for (size_t i = 0 ;i < output .candidates .size ();++ i ) { 1063if (output .candidates [i ]== token ) { 1064return output .logits [i ]; 1065 } 1066 } 1067GGML_ABORT ("backend token not found" ); 1068} 1069 1070static sampler_comparison_output run_penalties_comparison ( 1071const test_params & params , 1072int32_t penalty_last_n , 1073float penalty_repeat , 1074float penalty_freq , 1075float penalty_present , 1076const std::string & prompt , 1077const std::function < void (llama_sampler * )> & extra_accept = {}) { 1078const auto * vocab = llama_model_get_vocab ( params . model . get ()); 1079const std::vector < float > raw_logits = decode_raw_logits ( params , prompt ); 1080const auto add_samplers = [ & ](llama_sampler * chain ) { 1081llama_sampler_chain_add ( chain , llama_sampler_init_penalties ( 1082llama_vocab_n_tokens ( vocab ), penalty_last_n , penalty_repeat , penalty_freq , penalty_present )); 1083}; 1084const auto accept_history = [ & ](llama_sampler * chain ) { 1085accept_prompt ( chain , vocab , prompt ); 1086if ( extra_accept ) { 1087extra_accept ( chain ); 1088} 1089}; 1090 1091return run_sampler_comparison ( 1092params , prompt , raw_logits , add_samplers , accept_history ); 1093} 1094 1095static void compare_penalties_logits ( 1096const test_params & params , 1097int32_t penalty_last_n , 1098float penalty_repeat , 1099float penalty_freq , 1100float penalty_present , 1101const std::string & prompt , 1102const std::function < void(llama_sampler * ) > & extra_accept = {}) { 1103const sampler_comparison_output output = run_penalties_comparison ( 1104params , penalty_last_n , penalty_repeat , penalty_freq , penalty_present , prompt , extra_accept ); 1105 1106GGML_ASSERT ( output . expected . size () == output . actual . logits . size ()); 1107 1108const sampler_comparison_stats stats = compare_sampler_outputs ( 1109"penalties" , map_logits ( output . expected ), output . actual ); 1110GGML_ASSERT ( stats . n_masked == 0 ); 1111GGML_ASSERT ( stats . n_mismatch == 0 ); 1112} 1113 1114static void test_penalty_parameter_values ( const test_params & params ) { 1115struct penalty_test_case { 1116const char * name ; 1117float repeat ; 1118float frequency ; 1119float presence ; 1120}; 1121 1122const penalty_test_case cases [] = { 1123{ "frequency -1" , 1.0f , -1.0f , 0.0f }, 1124{ "frequency 0" , 1.0f , 0.0f , 0.0f }, 1125{ "frequency 1" , 1.0f , 1.0f , 0.0f }, 1126{ "presence -1" , 1.0f , 0.0f , -1.0f }, 1127{ "presence 0" , 1.0f , 0.0f , 0.0f }, 1128{ "presence 1" , 1.0f , 0.0f , 1.0f }, 1129{ "repeat 1" , 1.0f , 0.0f , 0.0f }, 1130}; 1131 1132int n_failed = 0 ; 1133for ( const auto & test : cases ) { 1134const sampler_comparison_output output = run_penalties_comparison ( 1135params , 64 , test . repeat , test . frequency , test . presence , "Hello Hello world" ); 1136GGML_ASSERT ( output . expected . size () == output . actual . logits . size ()); 1137const sampler_comparison_stats stats = compare_sampler_outputs ( 1138test . name , map_logits ( output . expected ), output . actual ); 1139n_failed += stats . n_mismatch != 0 ; 1140} 1141 1142GGML_ASSERT ( n_failed == 0 ); 1143} 1144 1145static void compare_top_k_penalties_logits ( 1146const test_params & params , 1147int32_t k , 1148int32_t penalty_last_n , 1149float penalty_repeat , 1150float penalty_freq , 1151float penalty_present , 1152const std::string & prompt , 1153penalties_position position ) { 1154const auto * vocab = llama_model_get_vocab ( params . model . get ()); 1155const std::vector < float > raw_logits = decode_raw_logits ( params , prompt ); 1156const int n_vocab = (int) raw_logits . size (); 1157 1158GGML_ASSERT ( n_vocab > k ); 1159 1160const sampler_init_fn init_top_k = [ k ]() { 1161return llama_sampler_init_top_k ( k ); 1162}; 1163llama_sampler_ptr top_k ( init_top_k ()); 1164const std::vector < llama_token_data > top_k_data = apply_cpu_sampler ( raw_logits , top_k . get ()); 1165GGML_ASSERT ( top_k_data . size () == (size_t) k ); 1166const llama_token retained_history_token = top_k_data [ 0 ]. id ; 1167 1168llama_token excluded_history_token = LLAMA_TOKEN_NULL ; 1169for (llama_token token = 0 ; token < n_vocab ; ++ token ) { 1170const auto it = std:: find_if ( top_k_data . begin (), top_k_data . end (), [ token ]( const llama_token_data & data ) { 1171return data . id == token ; 1172}); 1173if ( it == top_k_data . end ()) { 1174excluded_history_token = token ; 1175break ; 1176} 1177} 1178GGML_ASSERT ( excluded_history_token != LLAMA_TOKEN_NULL ); 1179 1180const auto add_samplers = [ & ](llama_sampler * chain ) { 1181add_filter_and_penalties ( chain , init_top_k , n_vocab , 1182penalty_last_n , penalty_repeat , penalty_freq , penalty_present , position ); 1183}; 1184 1185auto accept_history = [ & ](llama_sampler * smpl ) { 1186accept_prompt ( smpl , vocab , prompt ); 1187llama_sampler_accept ( smpl , excluded_history_token ); 1188llama_sampler_accept ( smpl , excluded_history_token ); 1189llama_sampler_accept ( smpl , retained_history_token ); 1190llama_sampler_accept ( smpl , retained_history_token ); 1191}; 1192 1193const sampler_comparison_output output = run_sampler_comparison ( 1194params , prompt , raw_logits , add_samplers , accept_history ); 1195 1196GGML_ASSERT ( output . expected . size () == (size_t) k ); 1197GGML_ASSERT ( output . actual . logits . size () == (size_t) k ); 1198 1199const std::unordered_map < llama_token, float > expected_logits = map_logits ( output . expected ); 1200 1201if ( position == penalties_position:: after_filter ) { 1202GGML_ASSERT ( expected_logits . find ( retained_history_token ) != expected_logits . end ()); 1203GGML_ASSERT ( fabsf ( expected_logits . at ( retained_history_token ) - raw_logits [ retained_history_token ]) > 1e-6f ); 1204GGML_ASSERT ( expected_logits . find ( excluded_history_token ) == expected_logits . end ()); 1205GGML_ASSERT (std:: find ( output . actual . candidates . begin (), output . actual . candidates . end (), 1206excluded_history_token ) == output . actual . candidates . end ()); 1207} else { 1208const std::unordered_map < llama_token, float > unpenalized_logits = map_logits ( top_k_data ); 1209bool changed = false; 1210for ( const auto & item : expected_logits ) { 1211const auto it = unpenalized_logits . find ( item . first ); 1212if ( it == unpenalized_logits . end () || fabsf ( it -> second - item . second ) > 1e-6f ) { 1213changed = true; 1214break ; 1215} 1216} 1217GGML_ASSERT ( changed ); 1218} 1219 1220const char * name = position == penalties_position:: before_filter 1221? "penalties top-k" 1222: "top-k penalties" ; 1223const sampler_comparison_stats stats = compare_sampler_outputs ( 1224name , expected_logits , output . actual ); 1225GGML_ASSERT ( stats . n_masked == 0 ); 1226GGML_ASSERT ( stats . n_mismatch == 0 ); 1227} 1228 1229static void compare_masking_penalties_logits ( 1230const test_params & params , 1231const char * filter_name , 1232const sampler_init_fn & init_filter , 1233int32_t penalty_last_n , 1234float penalty_repeat , 1235float penalty_freq , 1236float penalty_present , 1237const std::string & prompt , 1238penalties_position position , 1239bool allow_extra_candidates , 1240bool add_history = true) { 1241const auto * vocab = llama_model_get_vocab ( params . model . get ()); 1242const std::vector < float > raw_logits = decode_raw_logits ( params , prompt ); 1243const int n_vocab = (int) raw_logits . size (); 1244llama_sampler_ptr filter ( init_filter ()); 1245const std::vector < llama_token_data > filtered_data = apply_cpu_sampler ( raw_logits , filter . get ()); 1246GGML_ASSERT (! filtered_data . empty ()); 1247GGML_ASSERT ( filtered_data . size () < (size_t) n_vocab ); 1248 1249const llama_token penalized_token = filtered_data [ 0 ]. id ; 1250std::unordered_set < llama_token > retained_tokens ; 1251retained_tokens . reserve ( filtered_data . size ()); 1252for ( const auto & data : filtered_data ) { 1253retained_tokens . insert ( data . id ); 1254} 1255 1256llama_token masked_token = LLAMA_TOKEN_NULL ; 1257for (llama_token token = 0 ; token < n_vocab ; ++ token ) { 1258if ( retained_tokens . find ( token ) == retained_tokens . end ()) { 1259masked_token = token ; 1260break ; 1261} 1262} 1263GGML_ASSERT ( masked_token != LLAMA_TOKEN_NULL ); 1264 1265const auto add_samplers = [ & ](llama_sampler * chain ) { 1266add_filter_and_penalties ( chain , init_filter , n_vocab , 1267penalty_last_n , penalty_repeat , penalty_freq , penalty_present , position ); 1268}; 1269auto accept_history = [ & ](llama_sampler * smpl ) { 1270if (! add_history ) { 1271return ; 1272} 1273accept_prompt ( smpl , vocab , prompt ); 1274llama_sampler_accept ( smpl , penalized_token ); 1275llama_sampler_accept ( smpl , penalized_token ); 1276llama_sampler_accept ( smpl , masked_token ); 1277llama_sampler_accept ( smpl , masked_token ); 1278}; 1279 1280const sampler_comparison_output output = run_sampler_comparison ( 1281params , prompt , raw_logits , add_samplers , accept_history ); 1282 1283GGML_ASSERT ( output . actual . logits . size () == (size_t) n_vocab ); 1284 1285const std::unordered_map < llama_token, float > expected_logits = map_logits ( output . expected ); 1286 1287GGML_ASSERT ( expected_logits . find ( masked_token ) == expected_logits . end ()); 1288if ( add_history ) { 1289if ( position == penalties_position:: after_filter ) { 1290GGML_ASSERT ( expected_logits . find ( penalized_token ) != expected_logits . end ()); 1291GGML_ASSERT ( fabsf ( expected_logits . at ( penalized_token ) - raw_logits [ penalized_token ]) > 1e-6f ); 1292} else { 1293llama_sampler_ptr penalties ( llama_sampler_init_penalties ( 1294n_vocab , penalty_last_n , penalty_repeat , penalty_freq , penalty_present )); 1295accept_history ( penalties . get ()); 1296const std::unordered_map < llama_token, float > penalized_logits = 1297map_logits ( apply_cpu_sampler ( raw_logits , penalties . get ())); 1298GGML_ASSERT ( fabsf ( penalized_logits . at ( penalized_token ) - raw_logits [ penalized_token ]) > 1e-6f ); 1299} 1300} 1301 1302const std::string name = position == penalties_position:: before_filter 1303? "penalties " + std:: string ( filter_name ) 1304: std:: string ( filter_name ) + " penalties" ; 1305const sampler_comparison_stats stats = compare_sampler_outputs ( 1306name . c_str (), expected_logits , output . actual , allow_extra_candidates ); 1307const float masked_logit = find_backend_logit ( output . actual , masked_token ); 1308GGML_ASSERT ( stats . n_masked > 0 ); 1309GGML_ASSERT (std:: isinf ( masked_logit ) && masked_logit < 0.0f ); 1310GGML_ASSERT ( stats . n_mismatch == 0 ); 1311} 1312 1313static void test_backend_penalties_sampling ( const test_params & params ) { 1314printf ( "Testing backend penalties (repeat + freq + presence)\n" ); 1315compare_penalties_logits ( params , 64 , 1.1f , 0.5f , 0.25f , "Hello Hello world" ); 1316 1317printf ( "Testing backend penalties with penalty_last_n > 64\n" ); 1318const auto * vocab = llama_model_get_vocab ( params . model . get ()); 1319std::vector < llama_token > tokens ( 8 ); 1320int32_t n_tok = llama_tokenize ( vocab , "a" , 1 , tokens . data (), (int32_t) tokens . size (), false, false); 1321if ( n_tok < 0 ) { 1322tokens . resize ( - n_tok ); 1323n_tok = llama_tokenize ( vocab , "a" , 1 , tokens . data (), (int32_t) tokens . size (), false, false); 1324} 1325GGML_ASSERT ( n_tok > 0 ); 1326const llama_token tok = tokens [ 0 ]; 1327 1328compare_penalties_logits ( params , 80 , 1.15f , 0.1f , 0.05f , "a" , [ tok ](llama_sampler * smpl ) { 1329// accept_prompt already accepted BOS + one 'a'; fill the ring to n=80 1330for (int i = 0 ; i < 78 ; ++ i ) { 1331llama_sampler_accept ( smpl , tok ); 1332} 1333}); 1334 1335printf ( "Testing backend penalties without filler entries\n" ); 1336compare_penalties_logits ( params , 64 , 1.1f , 0.5f , 0.25f , "Hello" , [](llama_sampler * smpl ) { 1337for (llama_token token = 0 ; token < 64 ; ++ token ) { 1338llama_sampler_accept ( smpl , token ); 1339} 1340}); 1341 1342printf ( "Testing backend top-k followed by penalties\n" ); 1343compare_top_k_penalties_logits ( params , 8 , 64 , 1.1f , 0.5f , 0.25f , "Hello" , 1344penalties_position:: after_filter ); 1345 1346printf ( "Testing backend penalties followed by top-k\n" ); 1347compare_top_k_penalties_logits ( params , 8 , 64 , 1.1f , 0.5f , 0.25f , "Hello" , 1348penalties_position:: before_filter ); 1349 1350printf ( "Testing backend top-p followed by penalties\n" ); 1351compare_masking_penalties_logits ( params , "top-p" , []() { 1352return llama_sampler_init_top_p ( 0.9f , 0 ); 1353}, 64 , 1.1f , 0.5f , 0.25f , "Hello" , penalties_position:: after_filter , true); 1354 1355printf ( "Testing backend top-p followed by penalties with a large history window\n" ); 1356compare_masking_penalties_logits ( params , "top-p large-window" , []() { 1357return llama_sampler_init_top_p ( 0.9f , 0 ); 1358}, 4096 , 1.1f , 0.5f , 0.25f , "Hello" , penalties_position:: after_filter , true); 1359 1360printf ( "Testing backend penalties followed by top-p\n" ); 1361compare_masking_penalties_logits ( params , "top-p" , []() { 1362return llama_sampler_init_top_p ( 0.9f , 0 ); 1363}, 64 , 1.1f , 0.5f , 0.25f , "Hello" , penalties_position:: before_filter , true); 1364 1365printf ( "Testing backend min-p followed by penalties\n" ); 1366compare_masking_penalties_logits ( params , "min-p" , []() { 1367return llama_sampler_init_min_p ( 0.1f , 0 ); 1368}, 64 , 1.1f , 0.5f , 0.25f , "Hello" , penalties_position:: after_filter , false); 1369 1370printf ( "Testing backend penalties followed by min-p\n" ); 1371compare_masking_penalties_logits ( params , "min-p" , []() { 1372return llama_sampler_init_min_p ( 0.1f , 0 ); 1373}, 64 , 1.1f , 0.5f , 0.25f , "Hello" , penalties_position:: before_filter , false); 1374 1375printf ( "Testing backend top-p followed by penalties with empty history\n" ); 1376compare_masking_penalties_logits ( params , "top-p empty" , []() { 1377return llama_sampler_init_top_p ( 0.9f , 0 ); 1378}, 64 , 1.1f , 0.5f , 0.25f , "Hello" , penalties_position:: after_filter , true, false); 1379 1380printf ( "Testing backend top-p followed by individual penalties\n" ); 1381compare_masking_penalties_logits ( params , "top-p repeat" , []() { 1382return llama_sampler_init_top_p ( 0.9f , 0 ); 1383}, 64 , 1.1f , 0.0f , 0.0f , "Hello" , penalties_position:: after_filter , true); 1384compare_masking_penalties_logits ( params , "top-p frequency" , []() { 1385return llama_sampler_init_top_p ( 0.9f , 0 ); 1386}, 64 , 1.0f , 0.5f , 0.0f , "Hello" , penalties_position:: after_filter , true); 1387compare_masking_penalties_logits ( params , "top-p presence" , []() { 1388return llama_sampler_init_top_p ( 0.9f , 0 ); 1389}, 64 , 1.0f , 0.0f , 0.25f , "Hello" , penalties_position:: after_filter , true); 1390 1391printf ( "Testing backend penalty parameter values\n" ); 1392test_penalty_parameter_values ( params ); 1393 1394printf ( "backend penalties sampling test PASSED\n" ); 1395} 1396 1397// This test verifies that it is possible to have two different backend samplers, 1398// one that uses the backend dist sampler, and another that uses CPU dist sampler. 1399static void test_backend_mixed_sampling ( const test_params & params ) { 1400struct llama_sampler_chain_params chain_params_0 = llama_sampler_chain_default_params (); 1401llama_sampler_ptr sampler_chain_0 ( llama_sampler_chain_init ( chain_params_0 )); 1402llama_sampler_chain_add ( sampler_chain_0 . get (), llama_sampler_init_dist ( 88 )); 1403 1404int k = 40 ; 1405struct llama_sampler_chain_params chain_params_1 = llama_sampler_chain_default_params (); 1406llama_sampler_ptr sampler_chain_1 ( llama_sampler_chain_init ( chain_params_1 )); 1407llama_sampler_chain_add ( sampler_chain_1 . get (), llama_sampler_init_top_k ( k )); 1408 1409std::vector < llama_sampler_seq_config > backend_sampler_configs = { 1410{ 0 , sampler_chain_0 . get () }, 1411{ 1 , sampler_chain_1 . get () } 1412}; 1413 1414test_context test_ctx (params, backend_sampler_configs); 1415 1416std::map < llama_seq_id, std::string > prompts = { 1417{ 0 , "Hello" }, 1418{ 1 , "Some" } 1419}; 1420 1421if (! test_ctx . decode ( prompts )) { 1422GGML_ASSERT (false && "Failed to decode token" ); 1423} 1424 1425// Verify sequence 0 that used the dist backend sampler. 1426{ 1427int32_t batch_idx = test_ctx . idx_for_seq ( 0 ); 1428llama_token token = llama_get_sampled_token_ith ( test_ctx . ctx . get (), batch_idx ); 1429const std::string token_str = test_ctx . token_to_piece ( token , false); 1430printf ( "sampled token id=%d, string='%s'\n" , token , token_str . c_str ()); 1431GGML_ASSERT ( token >= 0 && token < test_ctx . n_vocab ); 1432//GGML_ASSERT(llama_get_sampled_logits_ith(test_ctx.ctx.get(), batch_idx) == nullptr); 1433//GGML_ASSERT(llama_get_sampled_logits_count_ith(test_ctx.ctx.get(), batch_idx) == 0); 1434} 1435 1436// Verify sequence 1 that used the top-k backend sampler. 1437{ 1438int32_t batch_idx = test_ctx . idx_for_seq ( 1 ); 1439float * logits = llama_get_sampled_logits_ith ( test_ctx . ctx . get (), batch_idx ); 1440GGML_ASSERT ( logits != nullptr ); 1441size_t n_logits = llama_get_sampled_logits_count_ith ( test_ctx . ctx . get (), batch_idx ); 1442GGML_ASSERT ( n_logits == (size_t) k ); 1443GGML_ASSERT ( llama_get_sampled_token_ith ( test_ctx . ctx . get (), batch_idx ) == LLAMA_TOKEN_NULL ); 1444} 1445 1446printf ( "backend mixed sampling test PASSED\n" ); 1447} 1448 1449static void test_backend_set_sampler ( const test_params & params ) { 1450const int seq_id = 0 ; 1451const int32_t seed = 88 ; 1452 1453struct llama_sampler_chain_params backend_chain_params = llama_sampler_chain_default_params (); 1454llama_sampler_ptr backend_sampler_chain ( llama_sampler_chain_init ( backend_chain_params )); 1455llama_sampler_chain_add ( backend_sampler_chain . get (), llama_sampler_init_dist ( seed )); 1456std::vector < llama_sampler_seq_config > backend_sampler_configs = {{ seq_id , backend_sampler_chain . get () }}; 1457 1458test_context test_ctx (params, backend_sampler_configs); 1459 1460if (! test_ctx . decode ({{ seq_id , "Hello" }})) { 1461GGML_ASSERT (false && "Failed to decode token" ); 1462} 1463 1464int32_t batch_idx = test_ctx . idx_for_seq ( seq_id ); 1465 1466// Sample using backend sampler configured above 1467llama_token backend_token = llama_get_sampled_token_ith ( test_ctx . ctx . get (), batch_idx ); 1468const std::string backend_token_str = test_ctx . token_to_piece ( backend_token , false); 1469printf ( "dist sampled token = %d, string='%s'\n" , backend_token , backend_token_str . c_str ()); 1470 1471// Now clear the backend sampler for this sequence. 1472llama_set_sampler ( test_ctx . ctx . get (), seq_id , nullptr ); 1473printf ( "Cleared backend sampler for seq_id %d\n" , seq_id ); 1474 1475// Sample using CPU sampler 1476struct llama_sampler_chain_params chain_params = llama_sampler_chain_default_params (); 1477llama_sampler_ptr chain ( llama_sampler_chain_init ( chain_params )); 1478llama_sampler_chain_add ( chain . get (), llama_sampler_init_dist ( 18 )); 1479 1480std::map < llama_seq_id, llama_token > tokens = { { seq_id , backend_token }, }; 1481if (! test_ctx . decode_tokens ( tokens )) { 1482GGML_ASSERT (false && "Failed to decode token" ); 1483} 1484 1485// Should not have any sampled token or probs after clearing the backend sampler. 1486const int32_t idx = test_ctx . idx_for_seq ( seq_id ); 1487GGML_ASSERT ( llama_get_sampled_token_ith ( test_ctx . ctx . get (), idx ) == LLAMA_TOKEN_NULL ); 1488GGML_ASSERT ( llama_get_sampled_probs_ith ( test_ctx . ctx . get (), idx ) == nullptr ); 1489 1490// Sample the token using the CPU sampler chain. 1491llama_token token2 = llama_sampler_sample ( chain . get (), test_ctx . ctx . get (), seq_id ); 1492const std::string token2_str = test_ctx . token_to_piece ( token2 , false); 1493printf ( "CPU sampled token after clearing backend sampler: id=%d, string='%s'\n" , token2 , token2_str . c_str ()); 1494std::map < llama_seq_id, llama_token > tokens2 = { { seq_id , token2 }, }; 1495 1496// Set a new backend sampler for the sequence. 1497struct llama_sampler_chain_params new_backend_chain_params = llama_sampler_chain_default_params (); 1498llama_sampler_ptr new_backend_sampler_chain ( llama_sampler_chain_init ( new_backend_chain_params )); 1499llama_sampler_chain_add ( new_backend_sampler_chain . get (), llama_sampler_init_top_k ( 20 )); 1500llama_sampler_chain_add ( new_backend_sampler_chain . get (), llama_sampler_init_dist ( seed )); 1501llama_set_sampler ( test_ctx . ctx . get (), seq_id , new_backend_sampler_chain . get ()); 1502 1503if (! test_ctx . decode_tokens ( tokens2 )) { 1504GGML_ASSERT (false && "Failed to decode token" ); 1505} 1506 1507llama_token new_backend_token = llama_get_sampled_token_ith ( test_ctx . ctx . get (), test_ctx . idx_for_seq ( seq_id )); 1508const std::string new_backend_token_str = test_ctx . token_to_piece ( new_backend_token , false); 1509printf ( "dist sampled token = %d, string='%s'\n" , new_backend_token , new_backend_token_str . c_str ()); 1510 1511printf ( "backend set sampler test PASSED\n" ); 1512} 1513 1514static void test_backend_cpu_mixed_batch ( const test_params & params ) { 1515// Sequence 0 uses backend sampling 1516struct llama_sampler_chain_params chain_params_0 = llama_sampler_chain_default_params (); 1517llama_sampler_ptr sampler_chain_0 ( llama_sampler_chain_init ( chain_params_0 )); 1518llama_sampler_chain_add ( sampler_chain_0 . get (), llama_sampler_init_dist ( 88 )); 1519 1520std::vector < llama_sampler_seq_config > backend_sampler_configs = { 1521{ 0 , sampler_chain_0 . get () }, 1522}; 1523 1524// We need 2 sequences: seq 0 with backend sampling, seq 1 with CPU sampling 1525test_context test_ctx ( params , backend_sampler_configs , 2 ); 1526 1527std::map < llama_seq_id, std::string > prompts = { 1528{ 0 , "Hello" }, // Will use backend sampling 1529{ 1 , "Some" } // Will use CPU sampling 1530}; 1531 1532if (! test_ctx . decode ( prompts )) { 1533GGML_ASSERT (false && "Failed to decode token" ); 1534} 1535 1536// Verify sequence 0 (backend sampled) 1537{ 1538int32_t batch_idx = test_ctx . idx_for_seq ( 0 ); 1539llama_token token = llama_get_sampled_token_ith ( test_ctx . ctx . get (), batch_idx ); 1540const std::string token_str = test_ctx . token_to_piece ( token , false); 1541printf ( "Seq 0 (backend) sampled token id=%d, string='%s'\n" , token , token_str . c_str ()); 1542GGML_ASSERT ( token >= 0 && token < test_ctx . n_vocab ); 1543} 1544 1545// Verify sequence 1 (CPU sampled) 1546{ 1547int32_t batch_idx = test_ctx . idx_for_seq ( 1 ); 1548 1549llama_token backend_token = llama_get_sampled_token_ith ( test_ctx . ctx . get (), batch_idx ); 1550GGML_ASSERT ( backend_token == LLAMA_TOKEN_NULL ); 1551 1552struct llama_sampler_chain_params chain_params = llama_sampler_chain_default_params (); 1553llama_sampler_ptr chain ( llama_sampler_chain_init ( chain_params )); 1554llama_sampler_chain_add ( chain . get (), llama_sampler_init_greedy ()); 1555 1556llama_token token = llama_sampler_sample ( chain . get (), test_ctx . ctx . get (), batch_idx ); 1557const std::string token_str = test_ctx . token_to_piece ( token , false); 1558printf ( "Seq 1 (CPU) sampled token id=%d, string='%s'\n" , token , token_str . c_str ()); 1559GGML_ASSERT ( token >= 0 && token < test_ctx . n_vocab ); 1560} 1561 1562// Clear/remove the backend sampler, and sample again 1563{ 1564// clear the backend sampler for seq 0 so that there are no backend 1565// samplers. 1566llama_set_sampler ( test_ctx . ctx . get (), 0 , nullptr ); 1567 1568// Create a CPU sampler and verify we can sample from it. 1569struct llama_sampler_chain_params chain_params = llama_sampler_chain_default_params (); 1570llama_sampler_ptr chain ( llama_sampler_chain_init ( chain_params )); 1571llama_sampler_chain_add ( chain . get (), llama_sampler_init_greedy ()); 1572 1573int32_t batch_idx = test_ctx . idx_for_seq ( 1 ); 1574llama_token token = llama_sampler_sample ( chain . get (), test_ctx . ctx . get (), batch_idx ); 1575if (! test_ctx . decode_token ( token , 1 )) { 1576GGML_ASSERT (false && "Failed to decode token" ); 1577} 1578} 1579 1580// Set a backend sampler so that we can verify that it can be reset 1581{ 1582struct llama_sampler_chain_params chain_params = llama_sampler_chain_default_params (); 1583llama_sampler_ptr sampler_chain ( llama_sampler_chain_init ( chain_params )); 1584llama_sampler_chain_add ( sampler_chain . get (), llama_sampler_init_dist ( 88 )); 1585 1586llama_set_sampler ( test_ctx . ctx . get (), 0 , sampler_chain . get ()); 1587 1588if (! test_ctx . decode_token ( 3834 , 0 )) { 1589GGML_ASSERT (false && "Failed to decode token" ); 1590} 1591 1592int32_t batch_idx = test_ctx . idx_for_seq ( 0 ); 1593llama_token token = llama_get_sampled_token_ith ( test_ctx . ctx . get (), batch_idx ); 1594const std::string token_str = test_ctx . token_to_piece ( token , false); 1595printf ( "re-added backend sampled token id=%d, string='%s'\n" , token , token_str . c_str ()); 1596GGML_ASSERT ( token >= 0 && token < test_ctx . n_vocab ); 1597} 1598 1599printf ( "backend-cpu mixed batch test PASSED\n" ); 1600} 1601 1602static void test_backend_multi_output_limit ( const test_params & params ) { 1603const llama_seq_id seq_id = 0 ; 1604 1605llama_sampler_ptr chain ( llama_sampler_chain_init ( llama_sampler_chain_default_params ())); 1606llama_sampler_chain_add ( chain . get (), llama_sampler_init_dist ( 88 )); 1607std::vector < llama_sampler_seq_config > configs = {{ seq_id , chain . get () }}; 1608test_context test_ctx ( params , configs , 1 , 3 , 0 , 2 ); 1609 1610llama_batch batch = llama_batch_init ( 3 , 0 , 1 ); 1611for (int i = 0 ; i < 3 ; ++ i ) { 1612common_batch_add ( batch , llama_vocab_bos ( test_ctx . vocab ), i , { seq_id }, true); 1613} 1614 1615printf ( ">>> test_backend_multi_output_limit expected error start:\n" ); 1616const int ret = llama_decode ( test_ctx . ctx . get (), batch ); 1617GGML_ASSERT ( ret != 0 && "llama_decode should reject outputs above the per-sequence limit" ); 1618printf ( "<<< test_backend_multi_output_limit expected error end.\n" ); 1619 1620llama_batch_free ( batch ); 1621 1622printf ( "backend multi-output limit test PASSED\n" ); 1623} 1624 1625static void test_backend_multi_sequence_multi_output_dist ( const test_params & params ) { 1626const llama_vocab * vocab = llama_model_get_vocab ( params . model . get ()); 1627const int32_t n_vocab = llama_vocab_n_tokens ( vocab ); 1628const uint32_t seeds [] = { 88 , 1337 }; 1629// reduce the chance that swapped random inputs select the same token 1630const float temp = 10.0f ; 1631 1632llama_sampler_ptr chain_0 ( llama_sampler_chain_init ( llama_sampler_chain_default_params ())); 1633llama_sampler_ptr chain_1 ( llama_sampler_chain_init ( llama_sampler_chain_default_params ())); 1634llama_sampler_chain_add ( chain_0 . get (), llama_sampler_init_temp ( temp )); 1635llama_sampler_chain_add ( chain_0 . get (), llama_sampler_init_dist ( seeds [ 0 ])); 1636llama_sampler_chain_add ( chain_1 . get (), llama_sampler_init_temp ( temp )); 1637llama_sampler_chain_add ( chain_1 . get (), llama_sampler_init_dist ( seeds [ 1 ])); 1638std::vector < llama_sampler_seq_config > configs = { 1639{ 0 , chain_0 . get () }, 1640{ 1 , chain_1 . get () }, 1641}; 1642test_context test_ctx ( params , configs , 2 , 4 , 0 , 2 ); 1643 1644std::vector < llama_sampler_seq_config > reference_configs ; 1645test_context reference_ctx ( params , reference_configs , 2 , 4 ); 1646 1647const llama_token seq_tokens [ 2 ][ 2 ] = { 1648{ llama_vocab_bos ( vocab ), llama_vocab_eos ( vocab ) }, 1649{ llama_vocab_eos ( vocab ), llama_vocab_bos ( vocab ) }, 1650}; 1651 1652llama_batch batch = llama_batch_init ( 4 , 0 , 1 ); 1653for (int pos = 0 ; pos < 2 ; ++ pos ) { 1654common_batch_add ( batch , seq_tokens [ 0 ][ pos ], pos , { 0 }, true); 1655common_batch_add ( batch , seq_tokens [ 1 ][ pos ], pos , { 1 }, true); 1656} 1657 1658GGML_ASSERT ( llama_decode ( test_ctx . ctx . get (), batch ) == 0 ); 1659GGML_ASSERT ( llama_decode ( reference_ctx . ctx . get (), batch ) == 0 ); 1660 1661std::mt19937 reference_rngs [] = { 1662std:: mt19937 ( seeds [ 0 ]), 1663std:: mt19937 ( seeds [ 1 ]), 1664}; 1665std::uniform_real_distribution < double > reference_dist ( 0.0 , 1.0 ); 1666 1667for (int i = 0 ; i < batch . n_tokens ; ++ i ) { 1668const llama_seq_id seq_id = batch . seq_id [ i ][ 0 ]; 1669GGML_ASSERT ( seq_id == 0 || seq_id == 1 ); 1670 1671llama_sampler * chain = seq_id == 0 ? chain_0 . get () : chain_1 . get (); 1672const llama_token backend_token = llama_sampler_sample ( chain , test_ctx . ctx . get (), i ); 1673const float * sampled_logits = llama_get_sampled_logits_ith ( test_ctx . ctx . get (), i ); 1674const float * sampled_probs = llama_get_sampled_probs_ith ( test_ctx . ctx . get (), i ); 1675const uint32_t n_logits = llama_get_sampled_logits_count_ith ( test_ctx . ctx . get (), i ); 1676const uint32_t n_probs = llama_get_sampled_probs_count_ith ( test_ctx . ctx . get (), i ); 1677const float * reference_logits = llama_get_logits_ith ( reference_ctx . ctx . get (), i ); 1678 1679GGML_ASSERT ( backend_token >= 0 && backend_token < n_vocab ); 1680GGML_ASSERT ( sampled_logits != nullptr ); 1681GGML_ASSERT ( sampled_probs != nullptr ); 1682GGML_ASSERT ( reference_logits != nullptr ); 1683GGML_ASSERT ( n_logits == (uint32_t) n_vocab ); 1684GGML_ASSERT ( n_probs == (uint32_t) n_vocab ); 1685 1686float prob_sum = 0.0f ; 1687float cumsum_before = 0.0f ; 1688for (llama_token token = 0 ; token < n_vocab ; ++ token ) { 1689const float expected_logit = reference_logits [ token ] / temp ; 1690const float tolerance = 1e-4f * std:: max ( 1.0f , std:: fabs ( expected_logit )); 1691GGML_ASSERT (std:: fabs ( sampled_logits [ token ] - expected_logit ) <= tolerance ); 1692GGML_ASSERT (std:: isfinite ( sampled_probs [ token ])); 1693GGML_ASSERT ( sampled_probs [ token ] >= 0.0f ); 1694 1695prob_sum += sampled_probs [ token ]; 1696if ( token < backend_token ) { 1697cumsum_before += sampled_probs [ token ]; 1698} 1699} 1700 1701GGML_ASSERT (std:: fabs ( prob_sum - 1.0f ) <= 1e-3f ); 1702 1703const float rnd = reference_dist ( reference_rngs [ seq_id ]); 1704const float cumsum_sampled = cumsum_before + sampled_probs [ backend_token ]; 1705GGML_ASSERT ( rnd >= cumsum_before - 1e-4f ); 1706GGML_ASSERT ( rnd <= cumsum_sampled + 1e-4f ); 1707} 1708 1709llama_batch_free ( batch ); 1710 1711printf ( "backend multi-sequence multi-output dist test PASSED\n" ); 1712} 1713 1714static void test_backend_multi_output_dist_transaction ( const test_params & params ) { 1715const llama_seq_id seq_id = 0 ; 1716const uint32_t seed = 95 ; 1717const llama_vocab * vocab = llama_model_get_vocab ( params . model . get ()); 1718 1719llama_sampler_ptr chain ( llama_sampler_chain_init ( llama_sampler_chain_default_params ())); 1720llama_sampler_chain_add ( chain . get (), llama_sampler_init_temp ( 10.0f )); 1721llama_sampler_chain_add ( chain . get (), llama_sampler_init_dist ( seed )); 1722std::vector < llama_sampler_seq_config > configs = {{ seq_id , chain . get () }}; 1723test_context test_ctx ( params , configs , 1 , 3 , 2 , 3 ); 1724 1725auto verify_random = [ & ](int32_t row , float rnd , bool accept = true) { 1726const llama_token token = accept ? 1727llama_sampler_sample ( chain . get (), test_ctx . ctx . get (), row ) : 1728llama_get_sampled_token_ith ( test_ctx . ctx . get (), row ); 1729const float * probs = llama_get_sampled_probs_ith ( test_ctx . ctx . get (), row ); 1730 1731GGML_ASSERT ( token >= 0 && token < llama_vocab_n_tokens ( vocab )); 1732GGML_ASSERT ( probs != nullptr ); 1733 1734float cumsum_before = 0.0f ; 1735for (llama_token i = 0 ; i < token ; ++ i ) { 1736cumsum_before += probs [ i ]; 1737} 1738 1739const float cumsum_sampled = cumsum_before + probs [ token ]; 1740GGML_ASSERT ( rnd >= cumsum_before - 1e-4f ); 1741GGML_ASSERT ( rnd <= cumsum_sampled + 1e-4f ); 1742}; 1743 1744std::mt19937 rng (seed); 1745std::uniform_real_distribution < double > dist ( 0.0 , 1.0 ); 1746float randoms [ 3 ]; 1747for (float & rnd : randoms ) { 1748rnd = dist ( rng ); 1749} 1750 1751int32_t pos = 0 ; 1752auto decode = [ & ]() { 1753llama_batch batch = llama_batch_init ( 3 , 0 , 1 ); 1754for (int32_t i = 0 ; i < 3 ; ++ i ) { 1755common_batch_add ( batch , llama_vocab_bos ( vocab ), pos ++ , { seq_id }, true); 1756} 1757GGML_ASSERT ( llama_decode ( test_ctx . ctx . get (), batch ) == 0 ); 1758return batch ; 1759}; 1760 1761llama_batch batch = decode (); 1762verify_random ( 0 , randoms [ 0 ], false); 1763llama_batch_free ( batch ); 1764 1765batch = decode (); 1766verify_random ( 0 , randoms [ 0 ]); 1767verify_random ( 1 , randoms [ 1 ]); 1768llama_batch_free ( batch ); 1769 1770batch = decode (); 1771llama_sampler_ptr saved ( llama_sampler_clone ( chain . get ())); 1772verify_random ( 0 , randoms [ 2 ]); 1773llama_batch_free ( batch ); 1774 1775llama_sampler_copy ( saved . get (), chain . get ()); 1776 1777batch = decode (); 1778verify_random ( 0 , randoms [ 2 ]); 1779llama_batch_free ( batch ); 1780 1781printf ( "backend multi-output dist transaction test PASSED\n" ); 1782} 1783 1784static void test_backend_multi_output_sampling_chain ( const test_params & params ) { 1785const llama_seq_id seq_id = 0 ; 1786const uint32_t seed = 88 ; 1787const float p = 0.9f ; 1788const float temp = 0.8f ; 1789const float cdf_epsilon = 1e-4f ; 1790const llama_vocab * vocab = llama_model_get_vocab ( params . model . get ()); 1791const int32_t n_vocab = llama_vocab_n_tokens ( vocab ); 1792const uint32_t k = std:: min < uint32_t > ( 512 , n_vocab ); 1793const llama_logit_bias bias = { llama_vocab_bos ( vocab ), -0.1f }; 1794 1795auto make_filter_chain = [ & ]() { 1796llama_sampler_ptr result ( llama_sampler_chain_init ( llama_sampler_chain_default_params ())); 1797llama_sampler_chain_add ( result . get (), llama_sampler_init_logit_bias ( n_vocab , 1 , & bias )); 1798llama_sampler_chain_add ( result . get (), llama_sampler_init_top_k ( k )); 1799llama_sampler_chain_add ( result . get (), llama_sampler_init_top_p ( p , 1 )); 1800llama_sampler_chain_add ( result . get (), llama_sampler_init_min_p ( 0.01f , 1 )); 1801llama_sampler_chain_add ( result . get (), llama_sampler_init_temp ( temp )); 1802return result ; 1803}; 1804 1805llama_sampler_ptr chain = make_filter_chain (); 1806llama_sampler_chain_add ( chain . get (), llama_sampler_init_dist ( seed )); 1807std::vector < llama_sampler_seq_config > configs = {{ seq_id , chain . get () }}; 1808test_context test_ctx ( params , configs , 1 , 2 , 2 , 2 ); 1809 1810std::vector < llama_sampler_seq_config > reference_configs ; 1811test_context reference_ctx ( params , reference_configs , 1 , 2 , 2 ); 1812 1813llama_sampler_ptr reference_bias ( llama_sampler_init_logit_bias ( n_vocab , 1 , & bias )); 1814llama_sampler_ptr reference_top_k ( llama_sampler_init_top_k ( k )); 1815llama_sampler_ptr reference_top_p ( llama_sampler_init_top_p ( p , 1 )); 1816llama_sampler_ptr reference_min_p ( llama_sampler_init_min_p ( 0.01f , 1 )); 1817llama_sampler_ptr reference_temp ( llama_sampler_init_temp ( temp )); 1818std::vector < llama_token_data > reference_data (n_vocab); 1819 1820auto make_batch = [ & ](int32_t pos ) { 1821llama_batch batch = llama_batch_init ( 2 , 0 , 1 ); 1822for (int i = 0 ; i < 2 ; ++ i ) { 1823common_batch_add ( batch , llama_vocab_bos ( vocab ), pos + i , { seq_id }, true); 1824} 1825return batch ; 1826}; 1827 1828llama_batch batch = make_batch ( 0 ); 1829GGML_ASSERT ( llama_decode ( test_ctx . ctx . get (), batch ) == 0 ); 1830GGML_ASSERT ( llama_decode ( reference_ctx . ctx . get (), batch ) == 0 ); 1831 1832for (int i = 0 ; i < batch . n_tokens ; ++ i ) { 1833const llama_token backend_token = llama_sampler_sample ( chain . get (), test_ctx . ctx . get (), i ); 1834const float * sampled_logits = llama_get_sampled_logits_ith ( test_ctx . ctx . get (), i ); 1835const float * sampled_probs = llama_get_sampled_probs_ith ( test_ctx . ctx . get (), i ); 1836const llama_token * sampled_candidates = llama_get_sampled_candidates_ith ( test_ctx . ctx . get (), i ); 1837const uint32_t n_logits = llama_get_sampled_logits_count_ith ( test_ctx . ctx . get (), i ); 1838const uint32_t n_probs = llama_get_sampled_probs_count_ith ( test_ctx . ctx . get (), i ); 1839const uint32_t n_candidates = llama_get_sampled_candidates_count_ith ( test_ctx . ctx . get (), i ); 1840const float * reference_logits = llama_get_logits_ith ( reference_ctx . ctx . get (), i ); 1841 1842GGML_ASSERT ( backend_token >= 0 && backend_token < n_vocab ); 1843GGML_ASSERT ( sampled_logits != nullptr ); 1844GGML_ASSERT ( sampled_probs != nullptr ); 1845GGML_ASSERT ( sampled_candidates != nullptr ); 1846GGML_ASSERT ( reference_logits != nullptr ); 1847GGML_ASSERT ( n_logits == k ); 1848GGML_ASSERT ( n_probs == n_logits ); 1849GGML_ASSERT ( n_candidates == n_logits ); 1850 1851for (llama_token token = 0 ; token < n_vocab ; ++ token ) { 1852reference_data [ token ] = { token , reference_logits [ token ], 0.0f }; 1853} 1854 1855llama_token_data_array reference = { 1856/* .data = */ reference_data . data (), 1857/* .size = */ reference_data . size (), 1858/* .selected = */ LLAMA_TOKEN_NULL , 1859/* .sorted = */ false, 1860}; 1861 1862llama_sampler_apply ( reference_bias . get (), & reference ); 1863llama_sampler_apply ( reference_top_k . get (), & reference ); 1864llama_sampler_apply ( reference_top_p . get (), & reference ); 1865GGML_ASSERT ( reference . size > 0 ); 1866 1867float cdf = 0.0f ; 1868for (size_t j = 0 ; j < reference . size ; ++ j ) { 1869cdf += reference . data [ j ]. p ; 1870} 1871const float cdf_before = cdf - reference . data [ reference . size - 1 ]. p ; 1872const float boundary_distance = std:: min (std:: fabs ( cdf_before - p ), std:: fabs ( cdf - p )); 1873 1874llama_sampler_apply ( reference_min_p . get (), & reference ); 1875llama_sampler_apply ( reference_temp . get (), & reference ); 1876 1877std::unordered_map < llama_token, float > reference_by_id ; 1878for (size_t j = 0 ; j < reference . size ; ++ j ) { 1879reference_by_id . emplace ( reference . data [ j ]. id , reference . data [ j ]. logit ); 1880} 1881size_t n_backend_only = 0 ; 1882int32_t sampled_index = -1 ; 1883float prob_sum = 0.0f ; 1884 1885for (uint32_t j = 0 ; j < n_logits ; ++ j ) { 1886GGML_ASSERT ( sampled_candidates [ j ] >= 0 && sampled_candidates [ j ] < n_vocab ); 1887GGML_ASSERT (std:: isfinite ( sampled_probs [ j ])); 1888GGML_ASSERT ( sampled_probs [ j ] >= 0.0f ); 1889prob_sum += sampled_probs [ j ]; 1890 1891if ( sampled_candidates [ j ] == backend_token ) { 1892sampled_index = j ; 1893} 1894if (!std:: isfinite ( sampled_logits [ j ])) { 1895GGML_ASSERT (std:: isinf ( sampled_logits [ j ]) && sampled_logits [ j ] < 0.0f ); 1896GGML_ASSERT ( sampled_probs [ j ] == 0.0f ); 1897continue ; 1898} 1899 1900const auto match = reference_by_id . find ( sampled_candidates [ j ]); 1901if ( match == reference_by_id . end ()) { 1902++ n_backend_only ; 1903continue ; 1904} 1905 1906const float tolerance = 1e-4f * std:: max ( 1.0f , std:: fabs ( match -> second )); 1907GGML_ASSERT (std:: fabs ( sampled_logits [ j ] - match -> second ) <= tolerance ); 1908reference_by_id . erase ( match ); 1909} 1910 1911const size_t n_reference_only = reference_by_id . size (); 1912 1913if ( n_backend_only != 0 || n_reference_only != 0 ) { 1914GGML_ASSERT ( n_backend_only <= 1 ); 1915GGML_ASSERT ( n_reference_only <= 1 ); 1916GGML_ASSERT ( boundary_distance <= cdf_epsilon ); 1917} 1918 1919GGML_ASSERT ( sampled_index >= 0 ); 1920GGML_ASSERT (std:: isfinite ( sampled_logits [ sampled_index ])); 1921GGML_ASSERT ( sampled_probs [ sampled_index ] > 0.0f ); 1922GGML_ASSERT (std:: fabs ( prob_sum - 1.0f ) <= 1e-3f ); 1923} 1924 1925llama_batch_free ( batch ); 1926 1927batch = make_batch ( 2 ); 1928GGML_ASSERT ( llama_decode ( test_ctx . ctx . get (), batch ) == 0 ); 1929llama_batch_free ( batch ); 1930 1931printf ( "backend multi-output sampling chain test PASSED\n" ); 1932} 1933 1934static void test_backend_multi_output_cpu_suffix ( const test_params & params ) { 1935const llama_seq_id seq_id = 0 ; 1936const int32_t k = 8 ; 1937const llama_vocab * vocab = llama_model_get_vocab ( params . model . get ()); 1938 1939auto make_chain = [ & ](test_single_output_backend_sampler ** sampler_ctx ) { 1940llama_sampler_ptr result ( llama_sampler_chain_init ( llama_sampler_chain_default_params ())); 1941llama_sampler_chain_add ( result . get (), llama_sampler_init_top_k ( k )); 1942llama_sampler_chain_add ( result . get (), test_single_output_backend_sampler_init ( sampler_ctx )); 1943llama_sampler_chain_add ( result . get (), llama_sampler_init_dist ( 88 )); 1944return result ; 1945}; 1946 1947{ 1948test_single_output_backend_sampler * sampler_ctx = nullptr ; 1949llama_sampler_ptr chain = make_chain ( & sampler_ctx ); 1950std::vector < llama_sampler_seq_config > configs = {{ seq_id , chain . get () }}; 1951test_context test_ctx ( params , configs , 1 , 1 , 0 , 4 ); 1952 1953llama_batch batch = llama_batch_init ( 1 , 0 , 1 ); 1954common_batch_add ( batch , llama_vocab_bos ( vocab ), 0 , { seq_id }, true); 1955GGML_ASSERT ( llama_decode ( test_ctx . ctx . get (), batch ) == 0 ); 1956 1957GGML_ASSERT ( sampler_ctx -> backend_initialized ); 1958GGML_ASSERT ( sampler_ctx -> backend_outputs_max_per_seq == 1 ); 1959GGML_ASSERT ( sampler_ctx -> backend_apply_count > 0 ); 1960GGML_ASSERT ( sampler_ctx -> apply_count == 0 ); 1961GGML_ASSERT ( llama_get_sampled_token_ith ( test_ctx . ctx . get (), 0 ) != LLAMA_TOKEN_NULL ); 1962 1963llama_batch_free ( batch ); 1964} 1965 1966{ 1967test_single_output_backend_sampler * sampler_ctx = nullptr ; 1968llama_sampler_ptr chain = make_chain ( & sampler_ctx ); 1969std::vector < llama_sampler_seq_config > configs = {{ seq_id , chain . get () }}; 1970test_context test_ctx ( params , configs , 1 , 2 , 0 , 0 ); 1971 1972llama_batch batch = llama_batch_init ( 2 , 0 , 1 ); 1973for (int i = 0 ; i < 2 ; ++ i ) { 1974common_batch_add ( batch , llama_vocab_bos ( vocab ), i , { seq_id }, true); 1975} 1976GGML_ASSERT ( llama_decode ( test_ctx . ctx . get (), batch ) == 0 ); 1977 1978GGML_ASSERT (! sampler_ctx -> backend_initialized ); 1979GGML_ASSERT ( sampler_ctx -> backend_outputs_max_per_seq == 2 ); 1980GGML_ASSERT ( sampler_ctx -> backend_apply_count == 0 ); 1981for (int i = 0 ; i < batch . n_tokens ; ++ i ) { 1982GGML_ASSERT ( llama_get_sampled_token_ith ( test_ctx . ctx . get (), i ) == LLAMA_TOKEN_NULL ); 1983GGML_ASSERT ( llama_get_sampled_logits_count_ith ( test_ctx . ctx . get (), i ) == (uint32_t) k ); 1984GGML_ASSERT ( llama_get_sampled_candidates_count_ith ( test_ctx . ctx . get (), i ) == (uint32_t) k ); 1985const llama_token token = llama_sampler_sample ( chain . get (), test_ctx . ctx . get (), i ); 1986GGML_ASSERT ( token >= 0 && token < llama_vocab_n_tokens ( vocab )); 1987} 1988GGML_ASSERT ( sampler_ctx -> apply_count == batch . n_tokens ); 1989 1990llama_batch_free ( batch ); 1991} 1992 1993printf ( "backend multi-output CPU suffix test PASSED\n" ); 1994} 1995 1996struct backend_test_case { 1997std::string name ; 1998void ( * fn )( const test_params & ); 1999bool enabled_by_default ; 2000}; 2001 2002static const backend_test_case BACKEND_TESTS [] = { 2003{ "greedy" , test_backend_greedy_sampling , true }, 2004{ "logit_bias" , test_backend_logit_bias_sampling , true }, 2005{ "penalties" , test_backend_penalties_sampling , true }, 2006{ "temp" , test_backend_temp_sampling , true }, 2007{ "temp_ext" , test_backend_temp_ext_sampling , true }, 2008{ "top_k" , test_backend_top_k_sampling , true }, 2009{ "multi_sequence" , test_backend_multi_sequence_sampling , true }, 2010{ "dist" , test_backend_dist_sampling , true }, 2011{ "dist_and_cpu" , test_backend_dist_sampling_and_cpu , true }, 2012{ "set_sampler" , test_backend_set_sampler , true }, 2013{ "multi_output_limit" , test_backend_multi_output_limit , true }, 2014{ "multi_sequence_multi_output_dist" , test_backend_multi_sequence_multi_output_dist , true }, 2015{ "multi_output_dist_transaction" , test_backend_multi_output_dist_transaction , true }, 2016{ "multi_output_sampling_chain" , test_backend_multi_output_sampling_chain , true }, 2017{ "multi_output_cpu" , test_backend_multi_output_cpu_suffix , true }, 2018{ "mixed" , test_backend_mixed_sampling , true }, 2019{ "min_p" , test_backend_min_p_sampling , true }, 2020{ "cpu_mixed" , test_backend_cpu_mixed_batch , true }, 2021{ "top_p" , test_backend_top_p_sampling , true }, 2022}; 2023 2024static test_args parse_cli (int argc , char ** argv ) { 2025test_args out ; 2026 2027for (int i = 1 ; i < argc ; ++ i ) { 2028const char * arg = argv [ i ]; 2029 2030if (std:: strcmp ( arg , "--test" ) == 0 ) { 2031if ( i + 1 >= argc ) { 2032fprintf ( stderr , "--test expects a value\n" ); 2033exit ( EXIT_FAILURE ); 2034} 2035out . test = argv [ ++ i ]; 2036continue ; 2037} 2038if (std:: strncmp ( arg , "--test=" , 7 ) == 0 ) { 2039out . test = arg + 7 ; 2040continue ; 2041} 2042if (std:: strcmp ( arg , "--model" ) == 0 ) { 2043if ( i + 1 >= argc ) { 2044fprintf ( stderr , "--model expects a value\n" ); 2045exit ( EXIT_FAILURE ); 2046} 2047out . model = argv [ ++ i ]; 2048continue ; 2049} 2050if (std:: strncmp ( arg , "--model=" , 8 ) == 0 ) { 2051out . model = arg + 8 ; 2052continue ; 2053} 2054if (std:: strcmp ( arg , "--device" ) == 0 ) { 2055if ( i + 1 >= argc ) { 2056fprintf ( stderr , "--device expects a value (cpu or gpu)\n" ); 2057exit ( EXIT_FAILURE ); 2058} 2059out . device = argv [ ++ i ]; 2060continue ; 2061} 2062if (std:: strncmp ( arg , "--device=" , 9 ) == 0 ) { 2063out . device = arg + 9 ; 2064continue ; 2065} 2066if ( out . model . empty ()) { 2067out . model = arg ; 2068continue ; 2069} 2070 2071fprintf ( stderr , "Unexpected argument: %s\n" , arg ); 2072exit ( EXIT_FAILURE ); 2073} 2074 2075if ( out . device != "cpu" && out . device != "gpu" && out . device != "auto" ) { 2076fprintf ( stderr , "Invalid device '%s'. Must be 'cpu', 'gpu' or 'auto'\n" , out . device . c_str ()); 2077exit ( EXIT_FAILURE ); 2078} 2079 2080return out ; 2081} 2082 2083static std::vector < const backend_test_case *> collect_tests_to_run ( const std::string & requested ) { 2084std::vector < const backend_test_case *> selected ; 2085 2086if (! requested . empty ()) { 2087for ( const auto & test : BACKEND_TESTS ) { 2088if ( test . name == requested ) { 2089selected . push_back ( & test ); 2090break ; 2091} 2092} 2093if ( selected . empty ()) { 2094fprintf ( stderr , "Unknown test '%s'. Available tests:\n" , requested . c_str ()); 2095for ( const auto & test : BACKEND_TESTS ) { 2096fprintf ( stderr , " %s\n" , test . name . c_str ()); 2097} 2098exit ( EXIT_FAILURE ); 2099} 2100} else { 2101for ( const auto & test : BACKEND_TESTS ) { 2102if (! test . enabled_by_default ) { 2103continue ; 2104} 2105#ifdef GGML_USE_HIP 2106// TODO: remove this when https://github.com/ggml-org/llama.cpp/pull/26592 is merged 2107if ( test . name == "penalties" || test . name == "set_sampler" || 2108test . name == "mixed" || test . name == "top_p" || 2109test . name == "multi_output_sampling_chain" || 2110test . name == "multi_output_cpu" ) { 2111fprintf ( stderr , "Skipping test '%s' on HIP backend (no backend TOP_K support)\n" , test . name . c_str ()); 2112continue ; 2113} 2114#endif // GGML_USE_HIP 2115selected . push_back ( & test ); 2116} 2117} 2118 2119if ( selected . empty ()) { 2120fprintf ( stderr , "No backend sampling tests selected. Use --test=<name> to pick one.\n" ); 2121} 2122 2123return selected ; 2124} 2125 2126static void run_tests ( const std::vector < const backend_test_case *> & tests , const test_params & args ) { 2127for ( const auto & test : tests ) { 2128fprintf ( stderr , "\n=== %s ===\n" , test -> name . c_str ()); 2129try { 2130test -> fn ( args ); 2131} catch ( const std::exception & e ) { 2132fprintf ( stderr , "Error running test '%s': %s\n" , test -> name . c_str (), e . what ()); 2133exit ( EXIT_FAILURE ); 2134} 2135} 2136} 2137 2138int main (int argc , char ** argv ) { 2139test_args args = parse_cli ( argc , argv ); 2140 2141if ( args . model . empty ()) { 2142args . model = common_get_model_or_exit ( 1 , argv ); 2143} 2144 2145{ 2146std::ifstream file ( args . model ); 2147if (! file . is_open ()) { 2148fprintf ( stderr , "no model '%s' found\n" , args . model . c_str ()); 2149return EXIT_FAILURE ; 2150} 2151} 2152 2153fprintf ( stderr , "using '%s'\n" , args . model . c_str ()); 2154 2155llama_backend_init (); 2156 2157test_params params = { 2158/*.model =*/ load_model ( args ), 2159}; 2160 2161const std::vector < const backend_test_case *> tests = collect_tests_to_run ( args . test ); 2162if (! tests . empty ()) { 2163run_tests ( tests , params ); 2164} 2165 2166return 0 ; 2167}