char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
961e4b26a
char/aliceai-t5-moe
1#include "testing.h" 2 3#include "llama.h" 4 5#include "../src/llama-batch.h" 6#include "../src/llama-memory.h" 7#include "../src/llama-vocab.h" 8 9#include <cstdlib> 10#include <initializer_list> 11#include <map> 12#include <string> 13#include <utility> 14#include <vector> 15 16// mock memory that only provides per-sequence position ranges 17struct mock_memory :public llama_memory_i { 18 std::map < llama_seq_id , std::pair < llama_pos ,llama_pos >> ranges ;// seq_id -> [pos_min, pos_max] 19 20llama_memory_context_ptr init_batch (llama_batch_allocr & ,uint32_t ,bool )override {GGML_ASSERT (false&& "not implemented" ); } 21llama_memory_context_ptr init_full ()override {GGML_ASSERT (false&& "not implemented" ); } 22llama_memory_context_ptr init_update (llama_context * ,bool )override {GGML_ASSERT (false&& "not implemented" ); } 23 24bool get_can_shift ()const override {GGML_ASSERT (false&& "not implemented" ); } 25 26void clear (bool )override {GGML_ASSERT (false&& "not implemented" ); } 27 28bool seq_rm (llama_seq_id ,llama_pos ,llama_pos )override {GGML_ASSERT (false&& "not implemented" ); } 29void seq_cp (llama_seq_id ,llama_seq_id ,llama_pos ,llama_pos )override {GGML_ASSERT (false&& "not implemented" ); } 30void seq_keep (llama_seq_id )override {GGML_ASSERT (false&& "not implemented" ); } 31void seq_add (llama_seq_id ,llama_pos ,llama_pos ,llama_pos )override {GGML_ASSERT (false&& "not implemented" ); } 32void seq_div (llama_seq_id ,llama_pos ,llama_pos ,int )override {GGML_ASSERT (false&& "not implemented" ); } 33 34llama_pos seq_pos_min (llama_seq_id seq_id )const override { 35auto it = ranges .find (seq_id ); 36return it == ranges .end () ?-1 :it -> second .first ; 37 } 38 39llama_pos seq_pos_max (llama_seq_id seq_id )const override { 40auto it = ranges .find (seq_id ); 41return it == ranges .end () ?-1 :it -> second .second ; 42 } 43 44 std::map < ggml_backend_buffer_type_t ,size_t > memory_breakdown ()const override {return {}; } 45 46void state_write (llama_io_write_i & ,llama_seq_id ,llama_state_seq_flags )const override {GGML_ASSERT (false&& "not implemented" ); } 47void state_read (llama_io_read_i & ,llama_seq_id ,llama_state_seq_flags )override {GGML_ASSERT (false&& "not implemented" ); } 48}; 49 50// builds embedding batches - an empty llama_vocab rejects all token ids, so 51// the tests use embeddings everywhere except the token validation tests 52struct batch_builder { 53uint32_t n_embd ; 54 55 std::vector < float > embd ; 56 std::vector < llama_pos > pos ; 57 std::vector < int32_t > n_seq_id ; 58 std::vector < int8_t > logits ; 59 60 std::vector < std::vector < llama_seq_id >> seq ; 61 std::vector < llama_seq_id *> seq_ptr ; 62 63batch_builder (uint32_t n_embd = 2 ) :n_embd (n_embd ) {} 64 65// embd values are 100*i + k so that ubatch contents can be traced back to batch indices 66void add (llama_pos p , std::initializer_list < llama_seq_id > seq_ids ,bool output ) { 67const int32_t i = (int32_t )seq .size (); 68for (uint32_t k = 0 ;k < n_embd ;++ k ) { 69embd .push_back (100.0f * i + k ); 70 } 71pos .push_back (p ); 72n_seq_id .push_back ((int32_t )seq_ids .size ()); 73seq .emplace_back (seq_ids ); 74logits .push_back (output ?1 :0 ); 75 } 76 77llama_batch make (bool with_pos = true,bool with_seq = true,bool with_logits = true) { 78seq_ptr .clear (); 79for (auto & s :seq ) { 80seq_ptr .push_back (s .data ()); 81 } 82seq_ptr .push_back (nullptr ); 83 84llama_batch res = {}; 85res .n_tokens = (int32_t )seq .size (); 86res .embd = embd .data (); 87res .pos = with_pos ?pos .data () :nullptr ; 88res .n_seq_id = with_seq ?n_seq_id .data () :nullptr ; 89res .seq_id = with_seq ?seq_ptr .data () :nullptr ; 90res .logits = with_logits ?logits .data () :nullptr ; 91 92return res ; 93 } 94}; 95 96static void test_init (testing & t ) { 97llama_vocab vocab ; 98 99t .test ("rejects_n_seq_max_too_large" , [& ](testing & t ) { 100batch_builder bb ; 101bb .add (0 , {0 }, true); 102 103llama_batch_allocr ba (1 ); 104t .assert_true (!ba .init (bb .make (),vocab ,nullptr ,bb .n_embd ,LLAMA_MAX_SEQ + 1 , false)); 105 }); 106 107t .test ("rejects_invalid_token" , [& ](testing & t ) { 108llama_token tok = 0 ;// empty vocab -> every token id is out of range 109llama_batch batch = llama_batch_get_one (& tok ,1 ); 110 111llama_batch_allocr ba (1 ); 112t .assert_true ("token id >= n_tokens" , !ba .init (batch ,vocab ,nullptr ,0 ,1 , false)); 113 114tok = -1 ; 115t .assert_true ("negative token id" , !ba .init (batch ,vocab ,nullptr ,0 ,1 , false)); 116 }); 117 118t .test ("rejects_invalid_seq_id" , [& ](testing & t ) { 119llama_batch_allocr ba (1 ); 120 121 { 122batch_builder bb ; 123bb .add (0 , {4 }, true); 124t .assert_true ("seq_id >= n_seq_max" , !ba .init (bb .make (),vocab ,nullptr ,bb .n_embd ,4 , false)); 125 } 126 { 127batch_builder bb ; 128bb .add (0 , {-1 }, true); 129t .assert_true ("negative seq_id" , !ba .init (bb .make (),vocab ,nullptr ,bb .n_embd ,4 , false)); 130 } 131 }); 132 133t .test ("autofill_defaults" , [& ](testing & t ) { 134batch_builder bb ; 135for (int i = 0 ;i < 4 ;++ i ) { 136bb .add (0 , {0 }, false); 137 } 138 139llama_batch_allocr ba (1 ); 140t .assert_true (ba .init (bb .make (false, false, false),vocab ,nullptr ,bb .n_embd ,4 , false)); 141 142const llama_batch & batch = ba .get_batch (); 143 144t .assert_equal (4u ,ba .get_n_tokens ()); 145 146for (int i = 0 ;i < 4 ;++ i ) { 147t .assert_equal ("pos defaults to 0..n-1" ,i ,batch .pos [i ]); 148t .assert_equal ("n_seq_id defaults to 1" ,1 ,batch .n_seq_id [i ]); 149t .assert_equal ("seq_id defaults to 0" ,0 ,batch .seq_id [i ][0 ]); 150 } 151 152t .assert_equal ("only the last token is an output" ,1u ,ba .get_n_outputs ()); 153t .assert_equal (0 , (int )batch .logits [0 ]); 154t .assert_equal (1 , (int )batch .logits [3 ]); 155 156t .assert_equal (0 ,ba .seq_pos_min (0 )); 157t .assert_equal (3 ,ba .seq_pos_max (0 )); 158t .assert_equal (-1 ,ba .seq_pos_min (1 )); 159 }); 160 161t .test ("output_all" , [& ](testing & t ) { 162batch_builder bb ; 163for (int i = 0 ;i < 4 ;++ i ) { 164bb .add (i , {0 }, false); 165 } 166 167llama_batch_allocr ba (1 ); 168t .assert_true (ba .init (bb .make (true, true, false),vocab ,nullptr ,bb .n_embd ,4 , true)); 169t .assert_equal (4u ,ba .get_n_outputs ()); 170 }); 171 172t .test ("explicit_logits" , [& ](testing & t ) { 173batch_builder bb ; 174bb .add (0 , {0 }, true); 175bb .add (1 , {0 }, false); 176bb .add (2 , {0 }, true); 177 178llama_batch_allocr ba (1 ); 179t .assert_true (ba .init (bb .make (),vocab ,nullptr ,bb .n_embd ,4 , false)); 180t .assert_equal (2u ,ba .get_n_outputs ()); 181 182llama_ubatch ub = ba .split_simple (10 ); 183t .assert_equal (3u ,ub .n_tokens ); 184t .assert_equal (1 , (int )ub .output [0 ]); 185t .assert_equal (0 , (int )ub .output [1 ]); 186t .assert_equal (1 , (int )ub .output [2 ]); 187 188const auto & out_ids = ba .get_out_ids (); 189t .assert_equal ((size_t )2 ,out_ids .size ()); 190t .assert_equal (0 ,out_ids [0 ]); 191t .assert_equal (2 ,out_ids [1 ]); 192 }); 193 194t .test ("pos_from_memory" , [& ](testing & t ) { 195mock_memory mem ; 196mem .ranges [0 ]= {0 ,9 }; 197 198batch_builder bb ; 199for (int i = 0 ;i < 3 ;++ i ) { 200bb .add (0 , {0 }, false); 201 } 202 203llama_batch_allocr ba (1 ); 204t .assert_true (ba .init (bb .make (false, true, false),vocab ,& mem ,bb .n_embd ,4 , false)); 205 206t .assert_equal ("pos continues after memory" ,10 ,ba .seq_pos_min (0 )); 207t .assert_equal (12 ,ba .seq_pos_max (0 )); 208 }); 209 210t .test ("pos_continuity_with_memory" , [& ](testing & t ) { 211mock_memory mem ; 212mem .ranges [0 ]= {0 ,9 }; 213 214llama_batch_allocr ba (1 ); 215 216 { 217batch_builder bb ; 218bb .add (10 , {0 }, false); 219bb .add (11 , {0 }, true); 220t .assert_true ("pos_max + 1 is accepted" ,ba .init (bb .make (),vocab ,& mem ,bb .n_embd ,4 , false)); 221 } 222 { 223batch_builder bb ; 224bb .add (11 , {0 }, false); 225bb .add (12 , {0 }, true); 226t .assert_true ("gap after memory is rejected" , !ba .init (bb .make (),vocab ,& mem ,bb .n_embd ,4 , false)); 227 } 228 { 229batch_builder bb ; 230bb .add (9 , {0 }, false); 231bb .add (10 , {0 }, true); 232t .assert_true ("overlap with memory is rejected" , !ba .init (bb .make (),vocab ,& mem ,bb .n_embd ,4 , false)); 233 } 234 }); 235 236t .test ("rejects_non_continuous_positions" , [& ](testing & t ) { 237batch_builder bb ; 238bb .add (0 , {0 }, false); 239bb .add (1 , {0 }, false); 240bb .add (3 , {0 }, true); 241 242llama_batch_allocr ba (1 ); 243t .assert_true (!ba .init (bb .make (),vocab ,nullptr ,bb .n_embd ,4 , false)); 244 }); 245 246t .test ("rejects_decreasing_positions" , [& ](testing & t ) { 247batch_builder bb ; 248const llama_pos pos [7 ]= {4 ,5 ,0 ,1 ,6 ,2 ,3 }; 249const llama_seq_id seq [7 ]= {0 ,0 ,1 ,1 ,0 ,1 ,0 }; 250for (int i = 0 ;i < 7 ;++ i ) { 251bb .add (pos [i ], {seq [i ]}, false); 252 } 253// seq 0 sees positions 4,5,6,3 in batch order -> the trailing 3 decreases 254 255llama_batch_allocr ba (1 ); 256t .assert_true (!ba .init (bb .make (true, true, false),vocab ,nullptr ,bb .n_embd ,4 , false)); 257 }); 258 259t .test ("allows_equal_positions_in_seq" , [& ](testing & t ) { 260batch_builder bb ; 261bb .add (0 , {0 }, false); 262bb .add (0 , {0 }, false); 263bb .add (1 , {0 }, true); 264 265llama_batch_allocr ba (1 ); 266t .assert_true (ba .init (bb .make (true, true, false),vocab ,nullptr ,bb .n_embd ,4 , false)); 267 }); 268 269 270t .test ("rejects_coupled_diverged_seqs" , [& ](testing & t ) { 271batch_builder bb ; 272bb .add (6 , {0 ,1 }, true); 273 274llama_batch_allocr ba (1 ); 275 276mock_memory mem ; 277mem .ranges [0 ]= {0 ,5 }; 278mem .ranges [1 ]= {2 ,5 };// same pos_max, different pos_min -> diverged 279t .assert_true (!ba .init (bb .make (),vocab ,& mem ,bb .n_embd ,4 , false)); 280 281mem .ranges [1 ]= {0 ,5 }; 282t .assert_true (ba .init (bb .make (),vocab ,& mem ,bb .n_embd ,4 , false)); 283 }); 284} 285 286static void test_split (testing & t ) { 287llama_vocab vocab ; 288 289t .test ("split_simple_chunks" , [& ](testing & t ) { 290batch_builder bb ; 291for (int i = 0 ;i < 5 ;++ i ) { 292bb .add (i , {0 },i == 4 ); 293 } 294 295llama_batch_allocr ba (1 ); 296t .assert_true (ba .init (bb .make (),vocab ,nullptr ,bb .n_embd ,4 , false)); 297 298llama_ubatch ub = ba .split_simple (2 ); 299t .assert_equal (2u ,ub .n_tokens ); 300t .assert_true (!ub .equal_seqs ()); 301t .assert_equal (1u ,ub .n_seqs_unq ); 302t .assert_equal (0 ,ub .seq_id_unq [0 ]); 303t .assert_equal (0 ,ub .seq_idx [0 ]); 304for (int i = 0 ;i < 2 ;++ i ) { 305t .assert_equal (i ,ub .pos [i ]); 306t .assert_equal (1 ,ub .n_seq_id [i ]); 307t .assert_equal (0 ,ub .seq_id [i ][0 ]); 308t .assert_equal (100.0f * i ,ub .embd [i * bb .n_embd ]); 309t .assert_equal (100.0f * i + 1 ,ub .embd [i * bb .n_embd + 1 ]); 310 } 311 312ub = ba .split_simple (2 ); 313t .assert_equal (2u ,ub .n_tokens ); 314t .assert_equal (2 ,ub .pos [0 ]); 315t .assert_equal (3 ,ub .pos [1 ]); 316 317ub = ba .split_simple (2 ); 318t .assert_equal (1u ,ub .n_tokens ); 319t .assert_equal (4 ,ub .pos [0 ]); 320t .assert_equal (1 , (int )ub .output [0 ]); 321 322t .assert_equal (5u ,ba .get_n_used ()); 323 324ub = ba .split_simple (2 ); 325t .assert_equal ("batch is consumed" ,0u ,ub .n_tokens ); 326 327const auto & out_ids = ba .get_out_ids (); 328t .assert_equal ((size_t )1 ,out_ids .size ()); 329t .assert_equal (4 ,out_ids [0 ]); 330 }); 331 332t .test ("split_reset_allows_resplit" , [& ](testing & t ) { 333batch_builder bb ; 334for (int i = 0 ;i < 3 ;++ i ) { 335bb .add (i , {0 },i == 2 ); 336 } 337 338llama_batch_allocr ba (1 ); 339t .assert_true (ba .init (bb .make (),vocab ,nullptr ,bb .n_embd ,4 , false)); 340 341while (ba .split_simple (1 ).n_tokens > 0 ) { 342 } 343t .assert_equal (3u ,ba .get_n_used ()); 344 345ba .split_reset (); 346t .assert_equal (0u ,ba .get_n_used ()); 347 348llama_ubatch ub = ba .split_simple (10 ); 349t .assert_equal (3u ,ub .n_tokens ); 350 }); 351 352t .test ("split_equal_unequal_lengths" , [& ](testing & t ) { 353batch_builder bb ; 354for (int i = 0 ;i < 4 ;++ i ) { 355bb .add (i , {0 },i == 3 ); 356 } 357for (int i = 0 ;i < 2 ;++ i ) { 358bb .add (i , {1 },i == 1 ); 359 } 360 361llama_batch_allocr ba (1 ); 362t .assert_true (ba .init (bb .make (),vocab ,nullptr ,bb .n_embd ,4 , false)); 363 364llama_ubatch ub = ba .split_equal (8 , false,0 ); 365t .assert_true (ub .equal_seqs ()); 366t .assert_equal ("both seqs advance by the shorter length" ,4u ,ub .n_tokens ); 367t .assert_equal (2u ,ub .n_seq_tokens ); 368t .assert_equal (2u ,ub .n_seqs ); 369t .assert_equal (2u ,ub .n_seqs_unq ); 370// tokens are grouped per sequence set: [s0 s0 s1 s1] 371t .assert_equal (0 ,ub .seq_id [0 ][0 ]); 372t .assert_equal (0 ,ub .seq_id [1 ][0 ]); 373t .assert_equal (1 ,ub .seq_id [2 ][0 ]); 374t .assert_equal (1 ,ub .seq_id [3 ][0 ]); 375t .assert_equal (0 ,ub .pos [0 ]); 376t .assert_equal (1 ,ub .pos [1 ]); 377t .assert_equal (0 ,ub .pos [2 ]); 378t .assert_equal (1 ,ub .pos [3 ]); 379 380ub = ba .split_equal (8 , false,0 ); 381t .assert_equal ("only seq 0 remains" ,2u ,ub .n_tokens ); 382t .assert_equal (1u ,ub .n_seqs ); 383t .assert_equal (2 ,ub .pos [0 ]); 384t .assert_equal (3 ,ub .pos [1 ]); 385 386ub = ba .split_equal (8 , false,0 ); 387t .assert_equal (0u ,ub .n_tokens ); 388 389t .assert_equal (6u ,ba .get_n_used ()); 390 }); 391 392t .test ("split_equal_coupled" , [& ](testing & t ) { 393batch_builder bb ; 394bb .add (0 , {0 ,1 }, false); 395bb .add (1 , {0 ,1 }, true); 396 397llama_batch_allocr ba (1 ); 398t .assert_true (ba .init (bb .make (),vocab ,nullptr ,bb .n_embd ,4 , false)); 399 400llama_ubatch ub = ba .split_equal (4 , true,0 ); 401t .assert_equal ("sequential split rejects coupled seqs" ,0u ,ub .n_tokens ); 402 403ub = ba .split_equal (4 , false,0 ); 404t .assert_equal (2u ,ub .n_tokens ); 405t .assert_equal ("one sequence set" ,1u ,ub .n_seqs ); 406t .assert_equal ("two unique seq ids" ,2u ,ub .n_seqs_unq ); 407t .assert_equal (2 ,ub .n_seq_id [0 ]); 408t .assert_equal (0 ,ub .seq_idx [0 ]); 409t .assert_equal (1 ,ub .seq_idx [1 ]); 410 }); 411 412t .test ("split_seq_per_sequence" , [& ](testing & t ) { 413batch_builder bb ; 414for (llama_seq_id s = 0 ;s < 3 ;++ s ) { 415bb .add (0 , {s }, false); 416bb .add (1 , {s }, true); 417 } 418 419llama_batch_allocr ba (1 ); 420t .assert_true (ba .init (bb .make (),vocab ,nullptr ,bb .n_embd ,4 , false)); 421 422for (llama_seq_id s = 0 ;s < 3 ;++ s ) { 423llama_ubatch ub = ba .split_seq (8 ); 424t .assert_equal (2u ,ub .n_tokens ); 425t .assert_equal (1u ,ub .n_seqs ); 426t .assert_equal (s ,ub .seq_id [0 ][0 ]); 427t .assert_equal (s ,ub .seq_id_unq [0 ]); 428 } 429 430t .assert_equal (0u ,ba .split_seq (8 ).n_tokens ); 431t .assert_equal (6u ,ba .get_n_used ()); 432 }); 433 434t .test ("ubatch_reserve" , [& ](testing & t ) { 435llama_batch_allocr ba (1 ); 436 437llama_ubatch ub = ba .ubatch_reserve (3 ,2 ); 438t .assert_equal (6u ,ub .n_tokens ); 439t .assert_equal (3u ,ub .n_seq_tokens ); 440t .assert_equal (2u ,ub .n_seqs ); 441t .assert_equal (2u ,ub .n_seqs_unq ); 442t .assert_true (ub .equal_seqs ()); 443t .assert_equal (0 ,ub .seq_id_unq [0 ]); 444t .assert_equal (1 ,ub .seq_id_unq [1 ]); 445t .assert_true (ub .token != nullptr ); 446t .assert_true (ub .embd == nullptr ); 447 }); 448} 449 450static void test_keep_tail (testing & t ) { 451llama_vocab vocab ; 452 453// batch with n_tokens[s] tokens for each seq s, output on the last token of each seq 454auto make_batch = [](batch_builder & bb , std::initializer_list < int > n_tokens ) { 455llama_seq_id s = 0 ; 456for (int n :n_tokens ) { 457for (int i = 0 ;i < n ;++ i ) { 458bb .add (i , {s },i == n - 1 ); 459 } 460++ s ; 461 } 462return bb .make (); 463 }; 464 465t .test ("noop_when_seqs_complete" , [& ](testing & t ) { 466batch_builder bb ; 467 468llama_batch_allocr ba (1 ); 469t .assert_true (ba .init (make_batch (bb , {2 ,2 }),vocab ,nullptr ,bb .n_embd ,4 , false)); 470 471llama_ubatch ub = ba .split_equal (4 , false,2 ); 472t .assert_equal ("both seqs fit whole" ,4u ,ub .n_tokens ); 473t .assert_equal (2u ,ub .n_seqs ); 474t .assert_equal (2u ,ub .n_seq_tokens ); 475 476t .assert_equal (0u ,ba .split_equal (4 , false,2 ).n_tokens ); 477 }); 478 479t .test ("defers_seq_with_short_remainder" , [& ](testing & t ) { 480batch_builder bb ; 481 482llama_batch_allocr ba (1 ); 483t .assert_true (ba .init (make_batch (bb , {2 ,3 }),vocab ,nullptr ,bb .n_embd ,4 , false)); 484 485// expansion stops at 2 tokens per seq: seq 0 completes, seq 1 would be left 486// with 1 < n_keep_tail remaining, so it is deferred entirely 487llama_ubatch ub = ba .split_equal (4 , true,2 ); 488t .assert_equal (2u ,ub .n_tokens ); 489t .assert_equal (1u ,ub .n_seqs ); 490t .assert_equal (0 ,ub .seq_id [0 ][0 ]); 491t .assert_equal (2u ,ba .get_n_used ()); 492 493ub = ba .split_equal (4 , true,2 ); 494t .assert_equal ("deferred seq comes back whole" ,3u ,ub .n_tokens ); 495t .assert_equal (1u ,ub .n_seqs ); 496t .assert_equal (1 ,ub .seq_id [0 ][0 ]); 497for (int i = 0 ;i < 3 ;++ i ) { 498t .assert_equal (i ,ub .pos [i ]); 499 } 500 501t .assert_equal (5u ,ba .get_n_used ()); 502t .assert_equal (0u ,ba .split_equal (4 , true,2 ).n_tokens ); 503 }); 504 505t .test ("completes_first_seq_when_all_violate" , [& ](testing & t ) { 506batch_builder bb ; 507 508llama_batch_allocr ba (1 ); 509t .assert_true (ba .init (make_batch (bb , {3 ,3 }),vocab ,nullptr ,bb .n_embd ,4 , false)); 510 511// expansion stops at 2 tokens per seq, leaving both with 1 < n_keep_tail remaining; 512// seq 0 still fits in n_ubatch, so it is extended to completion and emitted alone 513llama_ubatch ub = ba .split_equal (4 , false,2 ); 514t .assert_equal (3u ,ub .n_tokens ); 515t .assert_equal (1u ,ub .n_seqs ); 516t .assert_equal (3u ,ub .n_seq_tokens ); 517t .assert_equal (0 ,ub .seq_id [0 ][0 ]); 518for (int i = 0 ;i < 3 ;++ i ) { 519t .assert_equal (i ,ub .pos [i ]); 520 } 521t .assert_equal (3u ,ba .get_n_used ()); 522 523ub = ba .split_equal (4 , false,2 ); 524t .assert_equal (3u ,ub .n_tokens ); 525t .assert_equal (1 ,ub .seq_id [0 ][0 ]); 526t .assert_equal (6u ,ba .get_n_used ()); 527 }); 528 529t .test ("truncates_to_preserve_tail" , [& ](testing & t ) { 530batch_builder bb ; 531 532llama_batch_allocr ba (1 ); 533t .assert_true (ba .init (make_batch (bb , {5 }),vocab ,nullptr ,bb .n_embd ,4 , false)); 534 535// 4 tokens would leave a remainder of 1, and the seq does not fit in n_ubatch, 536// so the ubatch is truncated until n_keep_tail tokens remain 537llama_ubatch ub = ba .split_equal (4 , false,2 ); 538t .assert_equal (3u ,ub .n_tokens ); 539t .assert_equal (1u ,ub .n_seqs ); 540t .assert_equal (2 ,ub .pos [2 ]); 541t .assert_equal (3u ,ba .get_n_used ()); 542 543ub = ba .split_equal (4 , false,2 ); 544t .assert_equal ("trailing tokens stay in one ubatch" ,2u ,ub .n_tokens ); 545t .assert_equal (3 ,ub .pos [0 ]); 546t .assert_equal (4 ,ub .pos [1 ]); 547t .assert_equal (1 , (int )ub .output [1 ]); 548 549t .assert_equal (5u ,ba .get_n_used ()); 550 }); 551 552t .test ("keeps_full_ubatch_with_sufficient_remainder" , [& ](testing & t ) { 553batch_builder bb ; 554 555llama_batch_allocr ba (1 ); 556t .assert_true (ba .init (make_batch (bb , {6 }),vocab ,nullptr ,bb .n_embd ,4 , false)); 557 558llama_ubatch ub = ba .split_equal (4 , false,2 ); 559t .assert_equal ("remainder >= n_keep_tail, no truncation" ,4u ,ub .n_tokens ); 560 561ub = ba .split_equal (4 , false,2 ); 562t .assert_equal (2u ,ub .n_tokens ); 563t .assert_equal (4 ,ub .pos [0 ]); 564t .assert_equal (5 ,ub .pos [1 ]); 565 566t .assert_equal (6u ,ba .get_n_used ()); 567 }); 568 569t .test ("multi_seq_prefix_kept" , [& ](testing & t ) { 570batch_builder bb ; 571 572llama_batch_allocr ba (1 ); 573t .assert_true (ba .init (make_batch (bb , {3 ,4 }),vocab ,nullptr ,bb .n_embd ,6 , false)); 574 575// expansion stops at 3 tokens per seq: seq 0 completes, seq 1 has 1 < n_keep_tail 576// remaining and is deferred even though its tokens were already gathered 577llama_ubatch ub = ba .split_equal (6 , true,2 ); 578t .assert_equal (3u ,ub .n_tokens ); 579t .assert_equal (1u ,ub .n_seqs ); 580t .assert_equal (0 ,ub .seq_id [0 ][0 ]); 581t .assert_equal (3u ,ba .get_n_used ()); 582 583ub = ba .split_equal (6 , true,2 ); 584t .assert_equal (4u ,ub .n_tokens ); 585t .assert_equal (1 ,ub .seq_id [0 ][0 ]); 586t .assert_equal (7u ,ba .get_n_used ()); 587 }); 588} 589 590static void test_mrope (testing & t ) { 591llama_vocab vocab ; 592 593t .test ("pos_layout_and_split" , [& ](testing & t ) { 594const uint32_t n_pos = 4 ; 595const uint32_t n_embd = 2 ; 596 597batch_builder bb (n_embd ); 598bb .add (10 , {0 }, false); 599bb .add (11 , {0 }, true); 600 601// M-RoPE positions for embeddings are laid out [n_pos][n_tokens] 602 std::vector < llama_pos > pos = { 60310 ,11 ,// temporal 6045 ,6 ,// y 6057 ,8 ,// x 6060 ,0 , 607 }; 608 609llama_batch batch = bb .make (false, true, true); 610batch .pos = pos .data (); 611 612llama_batch_allocr ba (n_pos ); 613t .assert_true (ba .init (batch ,vocab ,nullptr ,n_embd ,4 , false)); 614 615llama_ubatch ub = ba .split_simple (2 ); 616t .assert_equal (2u ,ub .n_tokens ); 617t .assert_equal (n_pos ,ub .n_pos ); 618t .assert_true (ub .is_pos_2d ()); 619 620const llama_pos expected [8 ]= {10 ,11 ,5 ,6 ,7 ,8 ,0 ,0 }; 621for (int i = 0 ;i < 8 ;++ i ) { 622t .assert_equal (expected [i ],ub .pos [i ]); 623 } 624 }); 625 626t .test ("pos_jump_allowed" , [& ](testing & t ) { 627const uint32_t n_pos = 4 ; 628const uint32_t n_embd = 2 ; 629 630mock_memory mem ; 631mem .ranges [0 ]= {0 ,9 }; 632 633llama_batch_allocr ba (n_pos ); 634 635auto try_pos = [& ](llama_pos p0 ) { 636batch_builder bb (n_embd ); 637bb .add (p0 , {0 }, true); 638 639 std::vector < llama_pos > pos = {p0 ,1 ,1 ,0 }; 640 641llama_batch batch = bb .make (false, true, true); 642batch .pos = pos .data (); 643 644return ba .init (batch ,vocab ,& mem ,n_embd ,4 , false); 645 }; 646 647t .assert_true ("gap after memory is allowed" ,try_pos (15 )); 648t .assert_true ("overlap is allowed for embd" ,try_pos (9 )); 649t .assert_true ("pos behind memory is rejected" , !try_pos (8 )); 650 }); 651} 652 653int main (int argc ,char ** argv ) { 654testing t ; 655 656const char * verbose = getenv ("LLAMA_TEST_VERBOSE" ); 657if (verbose ) { 658t .verbose = std::string (verbose )== "1" ; 659 } 660if (!t .verbose ) { 661llama_log_set ([](ggml_log_level ,const char * ,void * ) {},nullptr ); 662 } 663 664if (argc > 1 ) { 665t .set_filter (argv [1 ]); 666 } 667 668t .test ("init" ,test_init ); 669t .test ("split" ,test_split ); 670t .test ("keep_tail" ,test_keep_tail ); 671t .test ("mrope" ,test_mrope ); 672 673return t .summary (); 674}