char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
41abbfd59
char/aliceai-t5-moe
1#include "models.h" 2#include "llama-impl.h" 3#include "llama-memory-hybrid-idx.h" 4#include "llama-memory-recurrent.h" 5 6#include <algorithm> 7#include <cinttypes> 8 9// bad metadata must be catchable: GGML_ASSERT aborts the whole process 10static void qwen4exp_require_nonzero (const llama_model_loader & ml ,llm_kv kid ,uint32_t value ) { 11if (value == 0 ) { 12throw std::runtime_error (format ("%s must be greater than zero, got %u" ,ml .llm_kv (kid ).c_str (),value )); 13 } 14} 15 16// get_arr() copies a short array as-is, leaving a zero tail the n-gram hash silently drops 17static void qwen4exp_require_arr_len (llama_model_loader & ml ,llm_kv kid ,uint32_t n_min ) { 18uint32_t n_arr = 0 ; 19ml .get_arr_n (kid ,n_arr , true); 20if (n_arr < n_min ) { 21throw std::runtime_error (format ("%s has %u entries, but at least %u are required" , 22ml .llm_kv (kid ).c_str (),n_arr ,n_min )); 23 } 24} 25 26void llama_model_qwen4exp::load_arch_hparams (llama_model_loader & ml ) { 27ml .get_key_or_arr (LLM_KV_EXPERT_FEED_FORWARD_LENGTH ,hparams .n_ff_exp_arr ,hparams .n_layer_all , false); 28ml .get_key (LLM_KV_EXPERT_SHARED_FEED_FORWARD_LENGTH ,hparams .n_ff_shexp , false); 29ml .get_key (LLM_KV_ATTENTION_LAYERNORM_RMS_EPS ,hparams .f_norm_rms_eps ); 30 31ml .get_key_or_arr (LLM_KV_ROPE_DIMENSION_SECTIONS ,hparams .rope_sections ,4 , true); 32 33ml .get_key (LLM_KV_SSM_CONV_KERNEL ,hparams .ssm_d_conv ); 34ml .get_key (LLM_KV_SSM_INNER_SIZE ,hparams .ssm_d_inner ); 35ml .get_key (LLM_KV_SSM_STATE_SIZE ,hparams .ssm_d_state ); 36ml .get_key (LLM_KV_SSM_TIME_STEP_RANK ,hparams .ssm_dt_rank ); 37ml .get_key (LLM_KV_SSM_GROUP_COUNT ,hparams .ssm_n_group ); 38qwen4exp_require_nonzero (ml ,LLM_KV_SSM_CONV_KERNEL ,hparams .ssm_d_conv ); 39qwen4exp_require_nonzero (ml ,LLM_KV_SSM_INNER_SIZE ,hparams .ssm_d_inner ); 40qwen4exp_require_nonzero (ml ,LLM_KV_SSM_STATE_SIZE ,hparams .ssm_d_state ); 41qwen4exp_require_nonzero (ml ,LLM_KV_SSM_TIME_STEP_RANK ,hparams .ssm_dt_rank ); 42qwen4exp_require_nonzero (ml ,LLM_KV_SSM_GROUP_COUNT ,hparams .ssm_n_group ); 43 44// HC; low_rank is qwen4exp-specific, DeepSeek-V4 leaves it absent (full rank) 45ml .get_key (LLM_KV_HYPER_CONNECTION_COUNT ,hparams .dsv4_hc_mult ); 46ml .get_key (LLM_KV_HYPER_CONNECTION_LOW_RANK ,hparams .hc_low_rank ); 47// a count of 1 has nothing to mix: transformers configuration_qwen4_exp.py:196, vLLM 48// config.py:49 and SGLang configs/qwen4_exp.py:38 all raise on hc_count <= 1 49if (hparams .dsv4_hc_mult <=1 ) { 50throw std::runtime_error (format ("%s must be greater than one, got %u" , 51ml .llm_kv (LLM_KV_HYPER_CONNECTION_COUNT ).c_str (),hparams .dsv4_hc_mult )); 52 } 53qwen4exp_require_nonzero (ml ,LLM_KV_HYPER_CONNECTION_LOW_RANK ,hparams .hc_low_rank ); 54hparams .n_embd_out_impl = hparams .dsv4_hc_mult * hparams .n_embd ; 55 56ml .get_key (LLM_KV_ATTENTION_INDEXER_HEAD_COUNT ,hparams .indexer_n_head ); 57ml .get_key (LLM_KV_ATTENTION_INDEXER_KEY_LENGTH ,hparams .indexer_head_size ); 58ml .get_key (LLM_KV_ATTENTION_INDEXER_TOP_K ,hparams .indexer_top_k ); 59qwen4exp_require_nonzero (ml ,LLM_KV_ATTENTION_INDEXER_HEAD_COUNT ,hparams .indexer_n_head ); 60qwen4exp_require_nonzero (ml ,LLM_KV_ATTENTION_INDEXER_KEY_LENGTH ,hparams .indexer_head_size ); 61qwen4exp_require_nonzero (ml ,LLM_KV_ATTENTION_INDEXER_TOP_K ,hparams .indexer_top_k ); 62ml .get_key_or_arr (LLM_KV_ATTENTION_COMPRESS_RATIOS ,hparams .dsv4_compress_ratios ,hparams .n_layer_all , false); 63 64// PLE n-gram hash embeddings; if the key group is absent every field stays zero 65hparams .is_ple_impl .reset (); 66hparams .ple_n_heads = 0 ; 67 68uint32_t n_ple = 0 ; 69ml .get_arr_n (LLM_KV_PLE_LAYERS ,n_ple , false); 70if (n_ple > 0 ) { 71 std::vector < uint32_t > ple_layers ; 72ml .get_arr (LLM_KV_PLE_LAYERS ,ple_layers ); 73if (n_ple != 1 ) { 74// hparams holds one set of hash constants, so several PLE modules cannot be represented 75throw std::runtime_error (format ("%s lists %u layers, but only one PLE layer is supported" , 76ml .llm_kv (LLM_KV_PLE_LAYERS ).c_str (),n_ple )); 77 } 78for (uint32_t il :ple_layers ) { 79if (il >=hparams .n_layer_all ) { 80throw std::runtime_error (format ("PLE layer %u is out of range" ,il )); 81 } 82hparams .is_ple_impl .set (il ); 83 } 84 85ml .get_key (LLM_KV_PLE_NGRAM_SIZE ,hparams .ple_ngram_size ); 86ml .get_key (LLM_KV_PLE_HEADS_PER_NGRAM ,hparams .ple_heads_per_ngram ); 87ml .get_key (LLM_KV_PLE_CONV_KERNEL ,hparams .ple_conv_kernel ); 88ml .get_key (LLM_KV_PLE_EOS_TOKEN_ID ,hparams .ple_eos_token_id ); 89// optional: files written before this key fall back to the EOS token 90ml .get_key (LLM_KV_PLE_IMAGE_TOKEN_ID ,hparams .ple_image_token_id , false); 91ml .get_key (LLM_KV_EMBEDDING_LENGTH_PER_LAYER ,hparams .n_embd_per_layer ); 92qwen4exp_require_nonzero (ml ,LLM_KV_PLE_CONV_KERNEL ,hparams .ple_conv_kernel ); 93qwen4exp_require_nonzero (ml ,LLM_KV_EMBEDDING_LENGTH_PER_LAYER ,hparams .n_embd_per_layer ); 94 95hparams .ple_n_heads = (hparams .ple_ngram_size - 1 )* hparams .ple_heads_per_ngram ; 96hparams .ple_head_dim = hparams .n_embd_per_layer ; 97if (hparams .ple_ngram_size < 2 || hparams .ple_ngram_size > LLAMA_MAX_PLE_NGRAM ) { 98throw std::runtime_error (format ("PLE n-gram size %u is out of range" ,hparams .ple_ngram_size )); 99 } 100if (hparams .ple_n_heads == 0 || hparams .ple_n_heads > LLAMA_MAX_PLE_HEADS ) { 101throw std::runtime_error (format ("PLE head count %u is out of range" ,hparams .ple_n_heads )); 102 } 103 104qwen4exp_require_arr_len (ml ,LLM_KV_PLE_LAYER_MULTIPLIERS ,hparams .ple_ngram_size ); 105qwen4exp_require_arr_len (ml ,LLM_KV_PLE_HEAD_OFFSETS ,hparams .ple_n_heads ); 106qwen4exp_require_arr_len (ml ,LLM_KV_PLE_HEAD_VOCAB_SIZES ,hparams .ple_n_heads ); 107 108ml .get_arr (LLM_KV_PLE_LAYER_MULTIPLIERS ,hparams .ple_layer_multipliers ); 109 110// the file stores the head ranges as uint64, so read at that width and narrow to the int32 the gather uses 111 std::array < uint64_t ,LLAMA_MAX_PLE_HEADS > head_offsets = {}; 112 std::array < uint64_t ,LLAMA_MAX_PLE_HEADS > head_vocab_sizes = {}; 113ml .get_arr (LLM_KV_PLE_HEAD_OFFSETS ,head_offsets ); 114ml .get_arr (LLM_KV_PLE_HEAD_VOCAB_SIZES ,head_vocab_sizes ); 115for (uint32_t h = 0 ;h < hparams .ple_n_heads ;++ h ) { 116if (head_vocab_sizes [h ]== 0 || 117head_offsets [h ]> INT32_MAX || 118head_vocab_sizes [h ]> INT32_MAX || 119head_offsets [h ]+ head_vocab_sizes [h ]> INT32_MAX ) { 120throw std::runtime_error (format ("PLE head %u range does not fit the int32 row index" ,h )); 121 } 122hparams .ple_head_offsets [h ]= (uint32_t )head_offsets [h ]; 123hparams .ple_head_vocab_sizes [h ]= (uint32_t )head_vocab_sizes [h ]; 124 } 125 } 126 127// linear attention everywhere except every full_attention_interval-th layer 128if (!ml .get_key_or_arr (LLM_KV_ATTENTION_RECURRENT_LAYERS ,hparams .is_recr_impl ,hparams .n_layer_all , false)) { 129uint32_t full_attn_interval = 4 ; 130ml .get_key (LLM_KV_FULL_ATTENTION_INTERVAL ,full_attn_interval , false); 131qwen4exp_require_nonzero (ml ,LLM_KV_FULL_ATTENTION_INTERVAL ,full_attn_interval ); 132for (uint32_t i = 0 ;i < hparams .n_layer_all ;++ i ) { 133hparams .is_recr_impl [i ]= (i < hparams .n_layer ())&& ((i + 1 ) %full_attn_interval != 0 ); 134 } 135 } 136 137// the PLE conv history is a row of the recurrent cache, which linear layers alone have 138for (uint32_t i = 0 ;i < hparams .n_layer_all ;++ i ) { 139if (hparams .is_ple (i )&& !hparams .is_recr (i )) { 140throw std::runtime_error (format ("PLE layer %u is not a linear attention layer" ,i )); 141 } 142 } 143 144switch (hparams .n_layer ()) { 145case 48 :type = LLM_TYPE_A3B ;break ; 146default :type = LLM_TYPE_UNKNOWN ; 147 } 148} 149 150void llama_model_qwen4exp::load_arch_tensors (llama_model_loader & ml ) { 151LLAMA_LOAD_LOCALS ; 152 153const int64_t hc = hparams .dsv4_hc_mult ; 154const int64_t hc_dim = hc * n_embd ; 155const int64_t hc_lr = hparams .hc_low_rank ; 156 157tok_embd = create_tensor (tn (LLM_TENSOR_TOKEN_EMBD ,"weight" ), {n_embd ,n_vocab },0 ); 158 159// there is no output_norm: the final hyper-connection mixer carries it 160// the gammas load as [n_embd, hc] so the grouped norm multiplies them without a graph reshape 161hc_head_norm = create_tensor (tn (LLM_TENSOR_HC_HEAD_NORM ,"weight" ), {n_embd ,hc },TENSOR_ALLOW_RESHAPE ); 162hc_head_down = create_tensor (tn (LLM_TENSOR_HC_HEAD_DOWN ,"weight" ), {hc_dim ,hc_lr },0 ); 163hc_head_up = create_tensor (tn (LLM_TENSOR_HC_HEAD_UP ,"weight" ), {hc_lr ,hc_dim },0 ); 164 165output = create_tensor (tn (LLM_TENSOR_OUTPUT ,"weight" ), {n_embd ,n_vocab },TENSOR_NOT_REQUIRED ); 166if (output == NULL ) { 167output = create_tensor (tn (LLM_TENSOR_TOKEN_EMBD ,"weight" ), {n_embd ,n_vocab },TENSOR_DUPLICATED ); 168 } 169 170// flat [ple_head_dim, n_rows] gather target 171if (hparams .ple_n_heads > 0 ) { 172// the head ranges are what the gather indexes, so they set the minimum row count 173int64_t ple_rows = 0 ; 174for (uint32_t h = 0 ;h < hparams .ple_n_heads ;++ h ) { 175ple_rows = std::max (ple_rows , (int64_t )hparams .ple_head_offsets [h ]+ hparams .ple_head_vocab_sizes [h ]); 176 } 177 178// the converter pads the table; a model synthesised from metadata has no tensor to ask 179const std::string ple_name = tn (LLM_TENSOR_PER_LAYER_TOKEN_EMBD ,"weight" ).str (); 180if (const auto * ple_w = ml .get_weight (ple_name .c_str ())) { 181if (ple_w -> tensor -> ne [1 ]< ple_rows ) { 182throw std::runtime_error (format ("%s has %" PRId64 " rows, too few for the PLE head ranges (%" PRId64 ")" , 183ple_name .c_str (),ple_w -> tensor -> ne [1 ],ple_rows )); 184 } 185ple_rows = ple_w -> tensor -> ne [1 ]; 186 } 187 188per_layer_tok_embd = create_tensor (tn (LLM_TENSOR_PER_LAYER_TOKEN_EMBD ,"weight" ), 189 {hparams .ple_head_dim ,ple_rows },TENSOR_READ_LAZY ); 190 } 191 192for (int il = 0 ;il < n_layer ;++ il ) { 193auto & layer = layers [il ]; 194 195const int64_t n_ff_exp = hparams .n_ff_exp () ?hparams .n_ff_exp () :n_ff /n_expert_used ; 196const int64_t n_ff_shexp = hparams .n_ff_shexp ?hparams .n_ff_shexp :n_ff ; 197 198const int64_t head_k_dim = hparams .ssm_d_state ; 199const int64_t head_v_dim = hparams .ssm_d_state ; 200const int64_t n_k_heads = hparams .ssm_n_group ; 201const int64_t n_v_heads = hparams .ssm_dt_rank ; 202const int64_t key_dim = head_k_dim * n_k_heads ; 203const int64_t value_dim = head_v_dim * n_v_heads ; 204const int64_t conv_dim = key_dim * 2 + value_dim ; 205 206// two HC modules per layer: before the token mixer, before the MoE 207layer .hc_attn_norm = create_tensor (tn (LLM_TENSOR_HC_ATTN_NORM ,"weight" ,il ), {n_embd ,hc },TENSOR_ALLOW_RESHAPE ); 208layer .hc_attn_down = create_tensor (tn (LLM_TENSOR_HC_ATTN_DOWN ,"weight" ,il ), {hc_dim ,hc_lr },0 ); 209layer .hc_attn_up = create_tensor (tn (LLM_TENSOR_HC_ATTN_UP ,"weight" ,il ), {hc_lr ,hc_dim },0 ); 210layer .hc_attn_inject = create_tensor (tn (LLM_TENSOR_HC_ATTN_INJECT ,"weight" ,il ), {hc_dim ,hc },0 ); 211layer .hc_ffn_norm = create_tensor (tn (LLM_TENSOR_HC_FFN_NORM ,"weight" ,il ), {n_embd ,hc },TENSOR_ALLOW_RESHAPE ); 212layer .hc_ffn_down = create_tensor (tn (LLM_TENSOR_HC_FFN_DOWN ,"weight" ,il ), {hc_dim ,hc_lr },0 ); 213layer .hc_ffn_up = create_tensor (tn (LLM_TENSOR_HC_FFN_UP ,"weight" ,il ), {hc_lr ,hc_dim },0 ); 214layer .hc_ffn_inject = create_tensor (tn (LLM_TENSOR_HC_FFN_INJECT ,"weight" ,il ), {hc_dim ,hc },0 ); 215 216if (!hparams .is_recr (il )) { 217// full attention: wq holds [q|gate] interleaved per head 218create_tensor_qkv (layer ,il ,n_embd ,n_embd_head_k * n_head * 2 ,n_embd_k_gqa ,n_embd_v_gqa ,0 ); 219layer .wo = create_tensor (tn (LLM_TENSOR_ATTN_OUT ,"weight" ,il ), {n_embd_head_k * n_head ,n_embd },0 ); 220 221layer .attn_q_norm = create_tensor (tn (LLM_TENSOR_ATTN_Q_NORM ,"weight" ,il ), {n_embd_head_k },0 ); 222layer .attn_k_norm = create_tensor (tn (LLM_TENSOR_ATTN_K_NORM ,"weight" ,il ), {n_embd_head_k },0 ); 223 224const int64_t idx_dim = hparams .indexer_head_size ; 225layer .index_q_proj = create_tensor (tn (LLM_TENSOR_INDEXER_Q_PROJ ,"weight" ,il ), {n_embd ,hparams .indexer_n_head * idx_dim },0 ); 226layer .index_k_proj = create_tensor (tn (LLM_TENSOR_INDEXER_K_PROJ ,"weight" ,il ), {n_embd ,idx_dim },0 ); 227layer .index_q_norm = create_tensor (tn (LLM_TENSOR_INDEXER_Q_NORM ,"weight" ,il ), {idx_dim },0 ); 228layer .index_k_norm = create_tensor (tn (LLM_TENSOR_INDEXER_K_NORM ,"weight" ,il ), {idx_dim },0 ); 229 }else { 230layer .wqkv = create_tensor (tn (LLM_TENSOR_ATTN_QKV ,"weight" ,il ), {n_embd ,key_dim * 2 + value_dim },0 ); 231layer .wqkv_gate = create_tensor (tn (LLM_TENSOR_ATTN_GATE ,"weight" ,il ), {n_embd ,value_dim },0 ); 232layer .ssm_conv1d = create_tensor (tn (LLM_TENSOR_SSM_CONV1D ,"weight" ,il ), {hparams .ssm_d_conv ,conv_dim },0 ); 233layer .ssm_dt = create_tensor (tn (LLM_TENSOR_SSM_DT ,"bias" ,il ), {hparams .ssm_dt_rank },0 ); 234layer .ssm_a = create_tensor (tn (LLM_TENSOR_SSM_A_NOSCAN ,il ), {hparams .ssm_dt_rank },0 ); 235layer .ssm_beta = create_tensor (tn (LLM_TENSOR_SSM_BETA ,"weight" ,il ), {n_embd ,n_v_heads },0 ); 236layer .ssm_alpha = create_tensor (tn (LLM_TENSOR_SSM_ALPHA ,"weight" ,il ), {n_embd ,n_v_heads },0 ); 237layer .ssm_norm = create_tensor (tn (LLM_TENSOR_SSM_NORM ,"weight" ,il ), {head_v_dim },0 ); 238layer .ssm_out = create_tensor (tn (LLM_TENSOR_SSM_OUT ,"weight" ,il ), {value_dim ,n_embd },0 ); 239 } 240 241if (hparams .is_ple (il )) { 242layer .ple_key = create_tensor (tn (LLM_TENSOR_PLE_KEY ,"weight" ,il ), {n_embd ,hc_dim },0 ); 243layer .ple_value = create_tensor (tn (LLM_TENSOR_PLE_VALUE ,"weight" ,il ), {n_embd ,n_embd },0 ); 244layer .ple_norm_key = create_tensor (tn (LLM_TENSOR_PLE_NORM_KEY ,"weight" ,il ), {n_embd ,hc },TENSOR_ALLOW_RESHAPE ); 245layer .ple_norm_query = create_tensor (tn (LLM_TENSOR_PLE_NORM_QUERY ,"weight" ,il ), {n_embd ,hc },TENSOR_ALLOW_RESHAPE ); 246layer .ple_norm_conv = create_tensor (tn (LLM_TENSOR_PLE_NORM_CONV ,"weight" ,il ), {n_embd ,hc },TENSOR_ALLOW_RESHAPE ); 247layer .ple_conv1d = create_tensor (tn (LLM_TENSOR_PLE_CONV1D ,"weight" ,il ), {hparams .ple_conv_kernel ,hc_dim },0 ); 248 } 249 250layer .ffn_gate_inp = create_tensor (tn (LLM_TENSOR_FFN_GATE_INP ,"weight" ,il ), {n_embd ,n_expert },0 ); 251layer .ffn_down_exps = create_tensor (tn (LLM_TENSOR_FFN_DOWN_EXPS ,"weight" ,il ), {n_ff_exp ,n_embd ,n_expert },0 ); 252create_tensor_gate_up_exps (layer ,il ,n_embd ,n_ff_exp ,n_expert ,0 ); 253 254layer .ffn_gate_inp_shexp = create_tensor (tn (LLM_TENSOR_FFN_GATE_INP_SHEXP ,"weight" ,il ), {n_embd },0 ); 255layer .ffn_gate_shexp = create_tensor (tn (LLM_TENSOR_FFN_GATE_SHEXP ,"weight" ,il ), {n_embd ,n_ff_shexp },0 ); 256layer .ffn_up_shexp = create_tensor (tn (LLM_TENSOR_FFN_UP_SHEXP ,"weight" ,il ), {n_embd ,n_ff_shexp },0 ); 257layer .ffn_down_shexp = create_tensor (tn (LLM_TENSOR_FFN_DOWN_SHEXP ,"weight" ,il ), {n_ff_shexp ,n_embd },0 ); 258 } 259} 260 261std::unique_ptr < llm_graph_context > llama_model_qwen4exp::build_arch_graph (const llm_graph_params & params )const { 262return std::make_unique < graph > (* this ,params ); 263} 264 265// Hyper-connections keep hc parallel residual streams [n_embd, hc, T] in place of layer norms. 266// Returns the mixed [n_embd, T] stream; `inject` gets the [hc, T] scatter weights. 267ggml_tensor * llama_model_qwen4exp::graph::build_hc_mix ( 268ggml_tensor * x , 269ggml_tensor * w_norm , 270ggml_tensor * w_down , 271ggml_tensor * w_up , 272ggml_tensor * w_inject , 273ggml_tensor ** inject , 274int il ) { 275const int64_t hc = hparams .dsv4_hc_mult ; 276const int64_t hc_dim = hc * n_embd ; 277const int64_t nt = x -> ne [2 ]; 278 279// grouped RMSNorm: reduce over one stream, then scale all streams with the [n_embd, hc] gamma 280// the converter folded each gamma to (1 + w) 281ggml_tensor * xn = ggml_mul (ctx0 ,ggml_rms_norm (ctx0 ,x ,hparams .f_norm_rms_eps ),w_norm ); 282xn = ggml_reshape_2d (ctx0 ,xn ,hc_dim ,nt ); 283cb (xn ,"hc_norm" ,il ); 284 285ggml_tensor * lo = build_lora_mm (w_down ,xn ); 286lo = ggml_silu (ctx0 ,ggml_scale (ctx0 ,lo ,1.0f / (float )hc )); 287ggml_tensor * gate = ggml_sigmoid (ctx0 ,build_lora_mm (w_up ,lo )); 288cb (gate ,"hc_gate" ,il ); 289 290ggml_tensor * gated = ggml_mul (ctx0 ,xn ,gate ); 291gated = ggml_reshape_3d (ctx0 ,gated ,n_embd ,hc ,nt ); 292 293// collapse the streams by their mean 294ggml_tensor * mixed = ggml_view_2d (ctx0 ,gated ,n_embd ,nt , 295ggml_row_size (gated -> type ,n_embd )* hc ,0 ); 296mixed = ggml_cont (ctx0 ,mixed ); 297for (int64_t c = 1 ;c < hc ;++ c ) { 298ggml_tensor * s = ggml_view_2d (ctx0 ,gated ,n_embd ,nt , 299ggml_row_size (gated -> type ,n_embd )* hc , 300ggml_row_size (gated -> type ,n_embd )* c ); 301mixed = ggml_add (ctx0 ,mixed ,s ); 302 } 303mixed = ggml_scale (ctx0 ,mixed ,1.0f / (float )hc ); 304cb (mixed ,"hc_mixed" ,il ); 305 306if (inject ) { 307* inject = build_lora_mm (w_inject ,xn ); 308cb (* inject ,"hc_inject" ,il ); 309 } 310 311return mixed ; 312} 313 314ggml_tensor * llama_model_qwen4exp::graph::build_hc_combine ( 315ggml_tensor * residual , 316ggml_tensor * block_out , 317ggml_tensor * inject , 318int il ) { 319const int64_t hc = hparams .dsv4_hc_mult ; 320const int64_t nt = residual -> ne [2 ]; 321 322// 2*sigmoid centres the scatter weights on 1, so a zero injection is a plain residual add 323ggml_tensor * w = ggml_sigmoid (ctx0 ,ggml_scale (ctx0 ,inject ,1.0f / (float )hc )); 324w = ggml_scale (ctx0 ,w ,2.0f ); 325w = ggml_reshape_3d (ctx0 ,w ,1 ,hc ,nt ); 326 327ggml_tensor * b = ggml_reshape_3d (ctx0 ,block_out ,n_embd ,1 ,nt ); 328b = ggml_repeat_4d (ctx0 ,b ,n_embd ,hc ,nt ,1 ); 329 330ggml_tensor * cur = ggml_add (ctx0 ,residual ,ggml_mul (ctx0 ,b ,w )); 331cb (cur ,"hc_combine" ,il ); 332 333return cur ; 334} 335 336llama_model_qwen4exp::graph::graph (const llama_model & model ,const llm_graph_params & params ) : 337llm_build_delta_net_base (params ),model (model ) { 338const int64_t hc = hparams .dsv4_hc_mult ; 339 340GGML_ASSERT (hparams .n_embd_head_v ()== hparams .n_embd_head_k ()); 341 342int sections [4 ]; 343 std::copy (std::begin (hparams .rope_sections ), std::begin (hparams .rope_sections )+ 4 ,sections ); 344 345ggml_tensor * inpL = build_inp_embd (model .tok_embd ); 346cb (inpL ,"model.input_embed" ,-1 ); 347ggml_build_forward_expand (gf ,inpL ); 348 349auto * inp = build_inp_mem_hybrid (); 350 351// qwen4exp always builds llama_memory_hybrid_idx, so this downcast is safe 352// the indexer cache inside it is absent when the GGUF has no indexer tensors 353const auto * mctx_hyb = static_cast < const llama_memory_hybrid_idx_context *> (inp -> mctx ); 354 355const llama_kv_cache_context * mctx_idx = mctx_hyb -> get_idx (); 356if (mctx_idx ) { 357GGML_ASSERT (mctx_idx -> get_n_kv ()== inp -> mctx -> get_attn ()-> get_n_kv ()&& 358"the indexer cache must track the attention cache cell for cell" ); 359 } 360 361ggml_tensor * inp_pos = build_inp_pos (); 362ggml_tensor * inp_out_ids = build_inp_out_ids (); 363 364ggml_tensor * ple_emb = nullptr ; 365if (hparams .ple_n_heads > 0 ) { 366ple_emb = build_inp_ple (mctx_hyb ); 367// make sure ple_emb and build_inp_embd are in the same graph split 368ggml_build_forward_expand (gf ,ple_emb ); 369 } 370 371// the wide residual starts as hc identical copies of the embedding 372ggml_tensor * res_hc = ggml_repeat_4d (ctx0 , 373ggml_reshape_3d (ctx0 ,inpL ,n_embd ,1 ,n_tokens ), 374n_embd ,hc ,n_tokens ,1 ); 375cb (res_hc ,"hc_init" ,-1 ); 376 377for (int il = 0 ;il < n_layer ;++ il ) { 378res -> t_layer_inp [il ]= res_hc ; 379 380if (hparams .is_ple (il )) { 381res_hc = build_ple (inp -> get_recr (),ple_emb ,res_hc ,il ); 382 } 383 384ggml_tensor * inject = nullptr ; 385ggml_tensor * cur = build_hc_mix (res_hc , 386model .layers [il ].hc_attn_norm , 387model .layers [il ].hc_attn_down , 388model .layers [il ].hc_attn_up , 389model .layers [il ].hc_attn_inject , 390& inject ,il ); 391 392ggml_build_forward_expand (gf ,cur ); 393 394if (hparams .is_recr (il )) { 395cur = build_layer_attn_linear (inp -> get_recr (),cur ,il ); 396 }else { 397cur = build_layer_attn (inp -> get_attn (),mctx_hyb ,cur ,inp_pos ,sections ,il ); 398 } 399 400if (il == n_layer - 1 && inp_out_ids ) { 401// everything below is per token, so drop the rows that produce no output 402cur = ggml_get_rows (ctx0 ,cur ,inp_out_ids ); 403inject = ggml_get_rows (ctx0 ,inject ,inp_out_ids ); 404 405res_hc = ggml_reshape_2d (ctx0 ,res_hc ,n_embd * hc ,res_hc -> ne [2 ]); 406res_hc = ggml_get_rows (ctx0 ,res_hc ,inp_out_ids ); 407res_hc = ggml_reshape_3d (ctx0 ,res_hc ,n_embd ,hc ,res_hc -> ne [1 ]); 408 } 409 410res_hc = build_hc_combine (res_hc ,cur ,inject ,il ); 411 412cur = build_hc_mix (res_hc , 413model .layers [il ].hc_ffn_norm , 414model .layers [il ].hc_ffn_down , 415model .layers [il ].hc_ffn_up , 416model .layers [il ].hc_ffn_inject , 417& inject ,il ); 418 419cur = build_layer_ffn (cur ,il ); 420cb (cur ,"ffn_out" ,il ); 421 422res_hc = build_hc_combine (res_hc ,cur ,inject ,il ); 423 424// "l_last" is the layer output name that build_cvec and imatrix look for 425cb (res_hc ,"l_last" ,il ); 426 } 427 428// the final mixer is the output norm: there is no separate one 429ggml_tensor * cur = build_hc_mix (res_hc , 430model .hc_head_norm ,model .hc_head_down ,model .hc_head_up , 431nullptr ,nullptr ,-1 ); 432 433cb (cur ,"result_norm" ,-1 ); 434res -> t_embd = cur ; 435 436cur = build_lora_mm (model .output ,cur ,model .output_s ); 437cb (cur ,"result_output" ,-1 ); 438res -> t_logits = cur ; 439 440ggml_build_forward_expand (gf ,cur ); 441} 442 443std::pair < ggml_tensor * ,ggml_tensor *> llama_model_qwen4exp::graph::build_qkvz ( 444ggml_tensor * input , 445int il ) { 446const int64_t n_seqs = ubatch .n_seqs ; 447const int64_t n_seq_tokens = ubatch .n_seq_tokens ; 448 449ggml_tensor * qkv_mixed = build_lora_mm (model .layers [il ].wqkv ,input ,model .layers [il ].wqkv_s ); 450qkv_mixed = ggml_reshape_3d (ctx0 ,qkv_mixed ,qkv_mixed -> ne [0 ],n_seq_tokens ,n_seqs ); 451cb (qkv_mixed ,"linear_attn_qkv_mixed" ,il ); 452 453ggml_tensor * z = build_lora_mm (model .layers [il ].wqkv_gate ,input ,model .layers [il ].wqkv_gate_s ); 454cb (z ,"z" ,il ); 455 456return {qkv_mixed ,z }; 457} 458 459ggml_tensor * llama_model_qwen4exp::graph::build_norm_gated ( 460ggml_tensor * input , 461ggml_tensor * weights , 462ggml_tensor * gate , 463int layer ) { 464// the one numerical difference from Qwen3.5's GDN: sigmoid output gate, not silu 465ggml_tensor * normalized = build_norm (input ,weights ,nullptr ,LLM_NORM_RMS ,layer ); 466ggml_tensor * gated = ggml_sigmoid (ctx0 ,gate ); 467 468return ggml_mul (ctx0 ,normalized ,gated ); 469} 470 471// QSA attends to a budget of whole blocks of compress_ratio tokens, plus the incomplete tail 472// one mean-pooled indexer key scores each block; set_input resolves the cache layout 473class llama_model_qwen4exp::llm_graph_input_qsa :public llm_graph_input_i { 474public : 475llm_graph_input_qsa (const llama_memory_hybrid_idx_context * mctx ,uint32_t ratio ,bool blk_bias ) : 476mctx (mctx ),ratio (ratio ),blk_bias (blk_bias ) {} 477virtual ~llm_graph_input_qsa ()= default ; 478 479void set_input (const llama_ubatch * ubatch )override { 480mctx -> get_idx ()-> set_input_k_idxs (k_idxs ,ubatch ); 481mctx -> set_input_qsa (cell_blk ,blk_cells ,blk_pos ,bias ,ubatch ,ratio ,blk_bias ); 482 } 483 484bool can_reuse (const llm_graph_params & params )override { 485mctx = static_cast < const llama_memory_hybrid_idx_context *> (params .mctx ); 486 487const auto * idx = mctx -> get_idx (); 488if (idx == nullptr ) { 489return false; 490 } 491 492const int64_t n_kv = idx -> get_n_kv (); 493const int64_t n_stream = mctx -> get_n_stream (); 494const int64_t n_blocks = (n_kv + ratio - 1 )/ratio ; 495 496bool res = true; 497 498res &=params .ubatch .n_tokens %n_stream == 0 ; 499 500res &=k_idxs -> ne [0 ]== params .ubatch .n_tokens ; 501res &=cell_blk -> ne [0 ]== n_kv ; 502res &=cell_blk -> ne [1 ]== n_stream ; 503res &=blk_cells -> ne [0 ]== (int64_t )ratio * n_blocks ; 504res &=blk_pos -> ne [0 ]== 4 * n_blocks * n_stream ; 505res &=bias -> ne [0 ]== (blk_bias ?n_blocks :n_kv ); 506res &=bias -> ne [1 ]== params .ubatch .n_tokens /n_stream ; 507 508return res ; 509 } 510 511// per stream: a cell index names a different token in each stream 512ggml_tensor * k_idxs = nullptr ;// I32 [n_tokens] 513ggml_tensor * cell_blk = nullptr ;// I32 [n_kv, n_stream] 514ggml_tensor * blk_cells = nullptr ;// I32 [ratio*n_blocks, n_stream] 515ggml_tensor * blk_pos = nullptr ;// I32 [4*n_blocks*n_stream] 516ggml_tensor * bias = nullptr ;// F32 [n_blocks or n_kv, n_tokens/n_stream, n_stream] 517 518const llama_memory_hybrid_idx_context * mctx ; 519const uint32_t ratio ; 520 521// the per-cell half of the bias is the attention mask, so only the per-block half is uploaded 522const bool blk_bias ; 523}; 524 525ggml_tensor * llama_model_qwen4exp::graph::build_qsa_top_k ( 526const llama_memory_hybrid_idx_context * mctx_hyb , 527ggml_tensor * cur , 528ggml_tensor * inp_pos , 529ggml_tensor * kq_mask , 530int * sections , 531int il ) { 532const llama_kv_cache_context * mctx_idx = mctx_hyb -> get_idx (); 533 534const int64_t idx_dim = hparams .indexer_head_size ; 535const int64_t n_idx_h = hparams .indexer_n_head ; 536const int64_t r = hparams .dsv4_compress_ratios [il ]; 537const int64_t n_kv = mctx_idx -> get_n_kv (); 538 539GGML_ASSERT (r > 0 ); 540 541const int64_t n_blocks = (n_kv + r - 1 )/r ; 542 543// build_attn_qsa and the KQ mask need the tokens to divide evenly across the streams 544const int64_t n_stream = mctx_hyb -> get_n_stream (); 545GGML_ASSERT (n_tokens %n_stream == 0 ); 546const int64_t n_tps = n_tokens /n_stream ; 547 548// only the "which block is visible" half of the bias varies per block 549// the rest is the visible/not test the attention mask already carries, so upload the per-block half only: 1/ratio of the cells 550// alibi writes distances instead of a mask and non-causal keeps future cells, so both opt out 551// the mask also holds an mrope rule for the query's own position, but only 2d image positions can differ there 552const bool blk_bias = kq_mask != nullptr && 553kq_mask -> ne [0 ]== n_kv && kq_mask -> ne [1 ]== n_tps && kq_mask -> ne [3 ]== n_stream && 554cparams .causal_attn && !hparams .use_alibi ; 555 556// nothing above depends on the layer, so the layers sharing a ratio share one input set 557llm_graph_input_qsa * inp = nullptr ; 558 559const auto it = qsa_inps .find ((uint32_t )r ); 560if (it != qsa_inps .end ()) { 561inp = it -> second ; 562 }else { 563auto qsa = std::make_unique < llm_graph_input_qsa > (mctx_hyb , (uint32_t )r ,blk_bias ); 564 565qsa -> k_idxs = mctx_idx -> build_input_k_idxs (ctx0 ,ubatch ); 566qsa -> cell_blk = ggml_new_tensor_2d (ctx0 ,GGML_TYPE_I32 ,n_kv ,n_stream ); 567qsa -> blk_cells = ggml_new_tensor_2d (ctx0 ,GGML_TYPE_I32 ,r * n_blocks ,n_stream ); 568qsa -> blk_pos = ggml_new_tensor_1d (ctx0 ,GGML_TYPE_I32 ,4 * n_blocks * n_stream ); 569qsa -> bias = ggml_new_tensor_3d (ctx0 ,GGML_TYPE_F32 ,blk_bias ?n_blocks :n_kv ,n_tps ,n_stream ); 570 571ggml_set_input (qsa -> cell_blk ); 572ggml_set_input (qsa -> blk_cells ); 573ggml_set_input (qsa -> blk_pos ); 574ggml_set_input (qsa -> bias ); 575 576inp = qsa .get (); 577res -> add_input (std::move (qsa )); 578qsa_inps .emplace ((uint32_t )r ,inp ); 579 } 580 581// cached indexer keys are raw: pooling precedes norm and rotation, so apply neither 582ggml_tensor * k_raw = build_lora_mm (model .layers [il ].index_k_proj ,cur ); 583k_raw = ggml_reshape_3d (ctx0 ,k_raw ,idx_dim ,1 ,n_tokens ); 584cb (k_raw ,"indexer_k_raw" ,il ); 585 586ggml_build_forward_expand (gf ,mctx_idx -> cpy_k (ctx0 ,k_raw ,inp -> k_idxs ,il )); 587 588// one key head, so rows are contiguous. get_k gives [idx_dim, n_head_kv, n_kv, n_stream]. 589ggml_tensor * k_all = mctx_idx -> get_k (ctx0 ,il ); 590k_all = ggml_view_3d (ctx0 ,k_all ,idx_dim ,n_kv ,n_stream ,k_all -> nb [2 ],k_all -> nb [3 ],0 ); 591 592// gathers per stream: blk_cells row s indexes stream s's own cells 593ggml_tensor * members = ggml_get_rows (ctx0 ,k_all ,inp -> blk_cells ); 594members = ggml_reshape_4d (ctx0 ,members ,idx_dim ,r ,n_blocks ,n_stream ); 595 596// mean over the block members; r is small, so summing slices beats a transpose plus sum_rows 597ggml_tensor * pooled = nullptr ; 598for (int64_t i = 0 ;i < r ;++ i ) { 599ggml_tensor * slice = ggml_cont (ctx0 , 600ggml_view_3d (ctx0 ,members ,idx_dim ,n_blocks ,n_stream , 601members -> nb [2 ],members -> nb [3 ],i * members -> nb [1 ])); 602pooled = pooled ?ggml_add (ctx0 ,pooled ,slice ) :slice ; 603 } 604pooled = ggml_scale (ctx0 ,pooled ,1.0f /(float )r ); 605cb (pooled ,"indexer_k_pooled" ,il ); 606 607// count blocks along ne1: rms_norm launches gridDim.y = ne2, capped at 65535, and 262144/4 = 65536 608pooled = ggml_reshape_3d (ctx0 ,pooled ,idx_dim ,n_blocks * n_stream ,1 ); 609pooled = build_norm (pooled ,model .layers [il ].index_k_norm ,nullptr ,LLM_NORM_RMS ,il ); 610 611// rope wants [n_dims, n_head, n_tokens]: lay every stream's blocks flat, split after. 612pooled = ggml_reshape_3d (ctx0 ,pooled ,idx_dim ,1 ,n_blocks * n_stream ); 613pooled = ggml_rope_multi (ctx0 ,pooled ,inp -> blk_pos ,nullptr , 614n_rot ,sections ,rope_type ,n_ctx_orig ,freq_base ,freq_scale , 615ext_factor ,attn_factor ,beta_fast ,beta_slow ); 616pooled = ggml_reshape_3d (ctx0 ,pooled ,idx_dim ,n_blocks ,n_stream ); 617cb (pooled ,"indexer_k" ,il ); 618 619ggml_tensor * q = build_lora_mm (model .layers [il ].index_q_proj ,cur ); 620q = ggml_reshape_3d (ctx0 ,q ,idx_dim ,n_idx_h ,n_tokens ); 621q = build_norm (q ,model .layers [il ].index_q_norm ,nullptr ,LLM_NORM_RMS ,il ); 622q = ggml_rope_multi (ctx0 ,q ,inp_pos ,nullptr , 623n_rot ,sections ,rope_type ,n_ctx_orig ,freq_base ,freq_scale , 624ext_factor ,attn_factor ,beta_fast ,beta_slow ); 625cb (q ,"indexer_q" ,il ); 626 627// rectify each head dot product before the sum, as in the DeepSeek lightning indexer 628// mul_mat matches ne[2], so the queries of stream s only meet the blocks of stream s 629ggml_tensor * score = ggml_mul_mat (ctx0 ,pooled , 630ggml_reshape_3d (ctx0 ,q ,idx_dim ,n_idx_h * n_tps ,n_stream )); 631score = ggml_reshape_4d (ctx0 ,score ,n_blocks ,n_idx_h ,n_tps ,n_stream ); 632score = ggml_relu (ctx0 ,score ); 633 634// the heads sit side by side on ne[1] and there are only a few of them 635ggml_tensor * summed = nullptr ; 636for (int64_t h = 0 ;h < n_idx_h ;++ h ) { 637ggml_tensor * slice = ggml_view_3d (ctx0 ,score ,n_blocks ,n_tps ,n_stream , 638score -> nb [2 ],score -> nb [3 ],h * score -> nb [1 ]); 639summed = summed ?ggml_add (ctx0 ,summed ,slice ) :ggml_cont (ctx0 ,slice ); 640 } 641 642score = summed ; 643cb (score ,"indexer_score" ,il ); 644 645// one value per block, so it is cheaper to bias here than after the cells are expanded 646if (blk_bias ) { 647score = ggml_add (ctx0 ,score ,inp -> bias ); 648 } 649 650// every token of a block gets the block score; the budget is whole blocks, so top-k cuts on a block boundary 651ggml_tensor * expanded = ggml_get_rows (ctx0 , 652ggml_cont (ctx0 ,ggml_permute (ctx0 ,score ,1 ,0 ,2 ,3 )),inp -> cell_blk ); 653expanded = ggml_cont (ctx0 ,ggml_permute (ctx0 ,expanded ,1 ,0 ,2 ,3 )); 654 655if (blk_bias ) { 656// flash attention keeps the mask in f16; the scores are f32 657ggml_tensor * mask = kq_mask -> type == GGML_TYPE_F32 ?kq_mask :ggml_cast (ctx0 ,kq_mask ,GGML_TYPE_F32 ); 658expanded = ggml_add (ctx0 ,expanded ,ggml_reshape_3d (ctx0 ,mask ,n_kv ,n_tps ,n_stream )); 659 }else { 660expanded = ggml_add (ctx0 ,expanded ,inp -> bias ); 661 } 662cb (expanded ,"indexer_score_tokens" ,il ); 663 664// the reference returns indexer_top_k + compress_ratio - 1: whole blocks plus the tail 665const int64_t width = std::min < int64_t > (n_kv , (int64_t )hparams .indexer_top_k + r - 1 ); 666 667ggml_tensor * top_k = ggml_cont (ctx0 ,ggml_top_k (ctx0 ,expanded ,width )); 668 669// build_attn_qsa reads [n_top_k, n_batch, 1, n_stream], matching the KQ mask. 670top_k = ggml_reshape_4d (ctx0 ,top_k ,width ,n_tps ,1 ,n_stream ); 671cb (top_k ,"indexer_top_k" ,il ); 672 673return top_k ; 674} 675 676// Dense GQA self-attention restricted to the cells that top_k names. 677// The mask build below copies the MLA sparse path in llm_graph_context::build_attn. 678ggml_tensor * llama_model_qwen4exp::graph::build_attn_qsa ( 679llm_graph_input_attn_kv * inp , 680ggml_tensor * q_cur , 681ggml_tensor * k_cur , 682ggml_tensor * v_cur , 683ggml_tensor * top_k , 684float kq_scale , 685int il ) { 686// rotate q/k/v before they reach a quantized cache, as the dense path does. the indexer 687// has already scored with its own query in build_qsa_top_k, so top_k is unaffected. 688if (inp -> self_k_rot ) { 689q_cur = llama_mul_mat_hadamard (ctx0 ,q_cur ,inp -> self_k_rot ); 690k_cur = llama_mul_mat_hadamard (ctx0 ,k_cur ,inp -> self_k_rot ); 691 } 692 693if (inp -> self_v_rot ) { 694v_cur = llama_mul_mat_hadamard (ctx0 ,v_cur ,inp -> self_v_rot ); 695 } 696 697// these nodes are added to the graph together so that they are not reordered 698// by doing so, the number of splits in the graph is reduced 699// expand k later to enable rope fusion which directly writes into k-v cache 700ggml_build_forward_expand (gf ,q_cur ); 701ggml_build_forward_expand (gf ,v_cur ); 702ggml_build_forward_expand (gf ,k_cur ); 703 704const auto * mctx_cur = inp -> mctx ; 705 706// store to KV cache 707 { 708const auto & k_idxs = inp -> get_k_idxs (); 709const auto & v_idxs = inp -> get_v_idxs (); 710 711ggml_build_forward_expand (gf ,mctx_cur -> cpy_k (ctx0 ,k_cur ,k_idxs ,il )); 712ggml_build_forward_expand (gf ,mctx_cur -> cpy_v (ctx0 ,v_cur ,v_idxs ,il )); 713 } 714 715ggml_tensor * kq_mask = inp -> get_kq_mask (); 716 717// prepare new kq mask - starts filled with -INFINITY 718ggml_tensor * kq_mask_all = ggml_fill (ctx0 ,kq_mask ,- INFINITY ); 719 720// reshape KQ mask into tensor with rows of size 1: 721// [n_kv, n_batch, 1, n_stream] -> [1, n_kv, n_batch, n_stream] 722kq_mask_all = ggml_view_4d (ctx0 ,kq_mask_all ,1 ,kq_mask_all -> ne [0 ],kq_mask_all -> ne [1 ],kq_mask_all -> ne [3 ],kq_mask_all -> nb [0 ],kq_mask_all -> nb [1 ],kq_mask_all -> nb [2 ],0 ); 723 724// reshape top_k indices: [n_top_k, n_batch, 1, n_stream] -> [n_top_k, n_batch, n_stream, 1] 725ggml_tensor * top_k_3d = ggml_view_4d (ctx0 ,top_k ,top_k -> ne [0 ],top_k -> ne [1 ],top_k -> ne [3 ],1 ,top_k -> nb [1 ],top_k -> nb [2 ],top_k -> ne [3 ]* top_k -> nb [3 ],0 ); 726 727// prepare zero-filled tensor with rows of size 1: [1, n_top_k, n_batch, n_stream] 728// this will be our source of zero values for unmasking top k mask elements 729ggml_tensor * zeros = ggml_new_tensor_4d (ctx0 ,GGML_TYPE_F32 ,1 ,top_k_3d -> ne [0 ],top_k_3d -> ne [1 ],top_k_3d -> ne [2 ]); 730zeros = ggml_fill (ctx0 ,zeros ,0.0f ); 731 732// modify KQ mask by unmasking elements that are in top_k indices 733// ggml_set_rows([1, n_kv, n_batch, n_stream], [1, n_top_k, n_batch, n_stream], [n_top_k, n_batch, n_stream, 1]) 734ggml_tensor * kq_mask_top_k = ggml_set_rows (ctx0 ,kq_mask_all ,zeros ,top_k_3d ); 735 736// reshape to restore the original shape of KQ mask: 737// [1, n_kv, n_batch, n_stream] -> [n_kv, n_batch, 1, n_stream] 738kq_mask_top_k = ggml_view_4d (ctx0 ,kq_mask_top_k ,kq_mask_top_k -> ne [1 ],kq_mask_top_k -> ne [2 ],1 ,kq_mask_top_k -> ne [3 ],kq_mask_top_k -> nb [2 ],kq_mask_top_k -> nb [3 ],kq_mask_top_k -> nb [3 ],0 ); 739 740// combine with the original kq mask 741kq_mask_top_k = ggml_add (ctx0 ,kq_mask_top_k ,kq_mask ); 742 743ggml_tensor * q = q_cur ; 744ggml_tensor * k = mctx_cur -> get_k (ctx0 ,il ); 745ggml_tensor * v = mctx_cur -> get_v (ctx0 ,il ); 746 747// TODO: enable sparse attention when we are ready 748// ref: https://github.com/ggml-org/llama.cpp/pull/27970 749//ggml_tensor * cur = build_attn_mha(q, k, v, nullptr, kq_mask_top_k, nullptr, nullptr, top_k->ne[0], kq_scale, il); 750ggml_tensor * cur = build_attn_mha (q ,k ,v ,nullptr ,kq_mask_top_k ,nullptr ,nullptr ,0 ,kq_scale ,il ); 751cb (cur ,"kqv_out" ,il ); 752 753// the rotation is its own inverse, so undo it on the value side of the output 754if (inp -> self_v_rot ) { 755cur = llama_mul_mat_hadamard (ctx0 ,cur ,inp -> self_v_rot ); 756 } 757 758return cur ; 759} 760 761ggml_tensor * llama_model_qwen4exp::graph::build_layer_attn ( 762llm_graph_input_attn_kv * inp , 763const llama_memory_hybrid_idx_context * mctx_hyb , 764ggml_tensor * cur , 765ggml_tensor * inp_pos , 766int * sections , 767int il ) { 768const int64_t n_embd_head = hparams .n_embd_head_v (); 769GGML_ASSERT (n_embd_head == hparams .n_embd_head_k ()); 770 771// indexer reads the same block input as q/k/v; no cache or no ratio means dense 772const bool qsa = mctx_hyb -> get_idx ()!= nullptr && hparams .dsv4_compress_ratios [il ]> 0 ; 773 774ggml_tensor * top_k = qsa ?build_qsa_top_k (mctx_hyb ,cur ,inp_pos ,inp -> get_kq_mask (),sections ,il ) :nullptr ; 775 776// Qwen3Next uses a single Q projection that outputs query + gate 777ggml_tensor * Qcur_full = build_lora_mm (model .layers [il ].wq ,cur ,model .layers [il ].wq_s );// [ (n_embd_head * 2) * n_head, n_tokens ] 778cb (Qcur_full ,"Qcur_full" ,il ); 779 780ggml_tensor * Qcur = ggml_view_3d (ctx0 ,Qcur_full ,n_embd_head ,n_head ,n_tokens , 781ggml_element_size (Qcur_full )* n_embd_head * 2 , 782ggml_element_size (Qcur_full )* n_embd_head * 2 * n_head ,0 ); 783cb (Qcur ,"Qcur_reshaped" ,il ); 784 785Qcur = build_norm (Qcur ,model .layers [il ].attn_q_norm ,nullptr ,LLM_NORM_RMS ,il ); 786cb (Qcur ,"Qcur_normed" ,il ); 787 788ggml_tensor * Kcur = build_lora_mm (model .layers [il ].wk ,cur ,model .layers [il ].wk_s ); 789cb (Kcur ,"Kcur" ,il ); 790 791ggml_tensor * Vcur = build_lora_mm (model .layers [il ].wv ,cur ,model .layers [il ].wv_s ); 792cb (Vcur ,"Vcur" ,il ); 793 794Kcur = ggml_reshape_3d (ctx0 ,Kcur ,n_embd_head ,n_head_kv ,n_tokens ); 795Kcur = build_norm (Kcur ,model .layers [il ].attn_k_norm ,nullptr ,LLM_NORM_RMS ,il ); 796cb (Kcur ,"Kcur_normed" ,il ); 797 798ggml_tensor * gate = ggml_view_3d (ctx0 ,Qcur_full ,n_embd_head ,n_head ,n_tokens , 799ggml_element_size (Qcur_full )* n_embd_head * 2 , 800ggml_element_size (Qcur_full )* n_embd_head * 2 * n_head , 801ggml_element_size (Qcur_full )* n_embd_head ); 802gate = ggml_cont_2d (ctx0 ,gate ,n_embd_head * n_head ,n_tokens ); 803cb (gate ,"gate_reshaped" ,il ); 804 805Vcur = ggml_reshape_3d (ctx0 ,Vcur ,n_embd_head ,n_head_kv ,n_tokens ); 806 807// Apply IMRoPE 808Qcur = ggml_rope_multi ( 809ctx0 ,Qcur ,inp_pos ,nullptr , 810n_rot ,sections ,rope_type ,n_ctx_orig ,freq_base ,freq_scale , 811ext_factor ,attn_factor ,beta_fast ,beta_slow 812 ); 813 814Kcur = ggml_rope_multi ( 815ctx0 ,Kcur ,inp_pos ,nullptr , 816n_rot ,sections ,rope_type ,n_ctx_orig ,freq_base ,freq_scale , 817ext_factor ,attn_factor ,beta_fast ,beta_slow 818 ); 819 820cb (Qcur ,"Qcur" ,il ); 821cb (Kcur ,"Kcur" ,il ); 822cb (Vcur ,"Vcur" ,il ); 823 824const float kq_scale = hparams .f_attention_scale == 0.0f ?1.0f /sqrtf (float (n_embd_head )) :hparams .f_attention_scale ; 825 826if (top_k ) { 827cur = build_attn_qsa (inp ,Qcur ,Kcur ,Vcur ,top_k ,kq_scale ,il ); 828 }else { 829cur = build_attn (inp , 830nullptr ,nullptr ,nullptr , 831Qcur ,Kcur ,Vcur ,nullptr ,nullptr ,nullptr ,kq_scale ,il ); 832 } 833cb (cur ,"attn_pregate" ,il ); 834 835ggml_tensor * gate_sigmoid = ggml_sigmoid (ctx0 ,gate ); 836cb (gate_sigmoid ,"gate_sigmoid" ,il ); 837 838cur = ggml_mul (ctx0 ,cur ,gate_sigmoid ); 839cb (cur ,"attn_gated" ,il ); 840 841cur = build_lora_mm (model .layers [il ].wo ,cur ,model .layers [il ].wo_s ); 842cb (cur ,"attn_output" ,il ); 843 844return cur ; 845} 846 847ggml_tensor * llama_model_qwen4exp::graph::build_layer_attn_linear ( 848llm_graph_input_rs * inp , 849ggml_tensor * cur , 850int il ) { 851const auto * mctx_cur = inp -> mctx ; 852 853const int64_t d_inner = hparams .ssm_d_inner ; 854const int64_t n_seqs = ubatch .n_seqs ; 855const int64_t head_k_dim = hparams .ssm_d_state ; 856const int64_t num_k_heads = hparams .ssm_n_group ; 857const int64_t num_v_heads = hparams .ssm_dt_rank ; 858const int64_t head_v_dim = hparams .ssm_d_state ; 859const int64_t n_seq_tokens = ubatch .n_seq_tokens ; 860 861GGML_ASSERT (n_seqs != 0 ); 862GGML_ASSERT (ubatch .equal_seqs ()); 863GGML_ASSERT (ubatch .n_tokens == n_seq_tokens * n_seqs ); 864GGML_ASSERT (head_v_dim * num_v_heads == d_inner ); 865 866auto qkvz = build_qkvz (cur ,il ); 867ggml_tensor * qkv_mixed = qkvz .first ; 868ggml_tensor * z = qkvz .second ; 869 870ggml_tensor * beta = build_lora_mm (model .layers [il ].ssm_beta ,cur ,model .layers [il ].ssm_beta_s ); 871beta = ggml_reshape_4d (ctx0 ,beta ,1 ,num_v_heads ,n_seq_tokens ,n_seqs ); 872cb (beta ,"beta" ,il ); 873 874beta = ggml_sigmoid (ctx0 ,beta ); 875cb (beta ,"beta_sigmoid" ,il ); 876 877ggml_tensor * alpha = build_lora_mm (model .layers [il ].ssm_alpha ,cur ,model .layers [il ].ssm_alpha_s ); 878alpha = ggml_reshape_3d (ctx0 ,alpha ,num_v_heads ,n_seq_tokens ,n_seqs ); 879cb (alpha ,"alpha" ,il ); 880 881ggml_tensor * alpha_biased = ggml_add (ctx0 ,alpha ,model .layers [il ].ssm_dt ); 882ggml_tensor * alpha_softplus = ggml_softplus (ctx0 ,alpha_biased ); 883cb (alpha_softplus ,"a_softplus" ,il ); 884 885ggml_tensor * gate = ggml_mul (ctx0 ,alpha_softplus ,model .layers [il ].ssm_a );// -A_log.exp() * softplus 886cb (gate ,"gate" ,il ); 887 888gate = ggml_reshape_4d (ctx0 ,gate ,1 ,num_v_heads ,n_seq_tokens ,n_seqs ); 889 890ggml_tensor * conv_states_all = mctx_cur -> get_r_l (il ); 891ggml_tensor * ssm_states_all = mctx_cur -> get_s_l (il ); 892 893ggml_tensor * conv_kernel = model .layers [il ].ssm_conv1d ; 894const int64_t conv_kernel_size = conv_kernel -> ne [0 ]; 895 896// the channels must match how load_arch_tensors sizes wqkv, not ssm_d_inner 897const int64_t conv_channels = head_k_dim * num_k_heads * 2 + head_v_dim * num_v_heads ; 898 899ggml_tensor * conv_input = build_conv_state_at (inp ,conv_states_all ,qkv_mixed , 900conv_kernel_size - 1 ,conv_channels ,il ); 901 902ggml_tensor * state = build_rs (inp ,ssm_states_all ,hparams .n_embd_s (),n_seqs ); 903state = ggml_reshape_4d (ctx0 ,state ,head_v_dim ,head_v_dim ,num_v_heads ,n_seqs ); 904cb (state ,"state_predelta" ,il ); 905 906ggml_tensor * conv_output_proper = ggml_ssm_conv (ctx0 ,conv_input ,conv_kernel ); 907cb (conv_output_proper ,"conv_output_raw" ,il ); 908 909ggml_tensor * conv_output_silu = ggml_silu (ctx0 ,conv_output_proper ); 910cb (conv_output_silu ,"conv_output_silu" ,il ); 911 912ggml_tensor * conv_qkv_mix = conv_output_silu ; 913 914int64_t nb1_qkv = ggml_row_size (conv_qkv_mix -> type ,conv_channels ); 915 916// Extract the convolved Q, K, V from conv_output 917ggml_tensor * q_conv = ggml_view_4d (ctx0 ,conv_qkv_mix ,head_k_dim ,num_k_heads ,n_seq_tokens ,n_seqs , 918ggml_row_size (conv_qkv_mix -> type ,head_k_dim ), 919nb1_qkv , 920nb1_qkv * n_seq_tokens , 9210 ); 922 923ggml_tensor * k_conv = ggml_view_4d (ctx0 ,conv_qkv_mix ,head_k_dim ,num_k_heads ,n_seq_tokens ,n_seqs , 924ggml_row_size (conv_qkv_mix -> type ,head_k_dim ), 925nb1_qkv , 926nb1_qkv * n_seq_tokens , 927head_k_dim * num_k_heads * ggml_element_size (conv_qkv_mix )); 928 929ggml_tensor * v_conv = ggml_view_4d (ctx0 ,conv_qkv_mix ,head_v_dim ,num_v_heads ,n_seq_tokens ,n_seqs , 930ggml_row_size (conv_qkv_mix -> type ,head_v_dim ), 931nb1_qkv , 932nb1_qkv * n_seq_tokens , 933ggml_row_size (conv_qkv_mix -> type ,2 * head_k_dim * num_k_heads )); 934 935cb (q_conv ,"q_conv" ,il ); 936cb (k_conv ,"k_conv" ,il ); 937cb (v_conv ,"v_conv" ,il ); 938 939 940const float eps_norm = hparams .f_norm_rms_eps ; 941 942q_conv = build_gdn_l2_norm (ctx0 ,q_conv ,eps_norm ); 943k_conv = build_gdn_l2_norm (ctx0 ,k_conv ,eps_norm ); 944 945// repeat to match shapes when head keys != value keys; unneeded with the fused GDN 946if (num_k_heads != num_v_heads && (!cparams .fused_gdn_ar || !cparams .fused_gdn_ch )) { 947GGML_ASSERT (num_v_heads %num_k_heads == 0 ); 948q_conv = ggml_repeat_4d (ctx0 ,q_conv ,head_k_dim ,num_v_heads ,n_seq_tokens ,n_seqs ); 949k_conv = ggml_repeat_4d (ctx0 ,k_conv ,head_k_dim ,num_v_heads ,n_seq_tokens ,n_seqs ); 950 } 951 952cb (q_conv ,"q_conv_predelta" ,il ); 953cb (k_conv ,"k_conv_predelta" ,il ); 954cb (v_conv ,"v_conv_predelta" ,il ); 955 956ggml_tensor * output = build_recurrent_attn (inp ,ssm_states_all ,q_conv ,k_conv ,v_conv ,gate ,beta ,state ,il ); 957 958ggml_tensor * z_2d = ggml_reshape_4d (ctx0 ,z ,head_v_dim ,num_v_heads ,n_seq_tokens ,n_seqs ); 959 960// gated normalization, as self.norm(core_attn_out, z) in the reference 961ggml_tensor * attn_out_norm = build_norm_gated (output ,model .layers [il ].ssm_norm ,z_2d ,il ); 962 963ggml_tensor * final_output = ggml_reshape_3d (ctx0 ,attn_out_norm ,head_v_dim * num_v_heads ,n_seq_tokens ,n_seqs ); 964cb (final_output ,"final_output" ,il ); 965 966cur = build_lora_mm (model .layers [il ].ssm_out ,final_output ,model .layers [il ].ssm_out_s ); 967cb (cur ,"linear_attn_out" ,il ); 968 969cur = ggml_reshape_2d (ctx0 ,cur ,n_embd ,n_seq_tokens * n_seqs ); 970 971return cur ; 972} 973 974ggml_tensor * llama_model_qwen4exp::graph::build_layer_ffn (ggml_tensor * cur ,const int il ) { 975GGML_ASSERT (model .layers [il ].ffn_gate_inp != nullptr ); 976 977ggml_tensor * moe_out = 978build_moe_ffn (cur , 979model .layers [il ].ffn_gate_inp , 980model .layers [il ].ffn_up_exps , 981model .layers [il ].ffn_gate_exps , 982model .layers [il ].ffn_down_exps , 983nullptr , 984n_expert ,n_expert_used , 985LLM_FFN_SILU , true, 986hparams .expert_weights_scale , 987LLAMA_EXPERT_GATING_FUNC_TYPE_SOFTMAX ,il , 988nullptr ,model .layers [il ].ffn_gate_up_exps , 989model .layers [il ].ffn_up_exps_s , 990model .layers [il ].ffn_gate_exps_s , 991model .layers [il ].ffn_down_exps_s ); 992cb (moe_out ,"ffn_moe_out" ,il ); 993 994// shared experts, as in the Qwen3Next reference 995if (model .layers [il ].ffn_up_shexp != nullptr ) { 996ggml_tensor * ffn_shexp = 997build_ffn (cur , 998model .layers [il ].ffn_up_shexp ,NULL ,model .layers [il ].ffn_up_shexp_s , 999model .layers [il ].ffn_gate_shexp ,NULL ,model .layers [il ].ffn_gate_shexp_s , 1000model .layers [il ].ffn_down_shexp ,NULL ,model .layers [il ].ffn_down_shexp_s , 1001NULL , 1002LLM_FFN_SILU ,LLM_FFN_PAR ,il ); 1003cb (ffn_shexp ,"ffn_shexp" ,il ); 1004 1005// shared expert has its own sigmoided gate (ffn_gate_inp_shexp, one value per token) 1006ggml_tensor * shared_gate = build_lora_mm (model .layers [il ].ffn_gate_inp_shexp ,cur ); 1007cb (shared_gate ,"shared_expert_gate" ,il ); 1008 1009shared_gate = ggml_sigmoid (ctx0 ,shared_gate ); 1010cb (shared_gate ,"shared_expert_gate_sigmoid" ,il ); 1011 1012ffn_shexp = ggml_mul (ctx0 ,ffn_shexp ,shared_gate ); 1013cb (ffn_shexp ,"ffn_shexp_gated" ,il ); 1014 1015cur = ggml_add (ctx0 ,moe_out ,ffn_shexp ); 1016cb (cur ,"ffn_out" ,il ); 1017 }else { 1018cur = moe_out ; 1019 } 1020 1021return cur ; 1022} 1023 1024// PLE n-gram hash embedding: each token gathers ple_n_heads rows of a shared table. 1025// mixed_n = (t[p]*m[0]) ^ ... ^ (t[p-n+1]*m[n-1]); row = mixed_n % vocab[h] + offset[h] 1026// The hash runs host-side because ggml has no int64 and no xor. EOS resets the window. 1027 1028class llm_graph_input_ple :public llm_graph_input_i { 1029public : 1030llm_graph_input_ple (const llama_model_qwen4exp & pmodel , 1031const llama_kv_cache_context * mctx ) :pmodel (pmodel ),mctx (mctx ) {} 1032virtual ~llm_graph_input_ple ()= default ; 1033 1034void set_input (const llama_ubatch * ubatch )override ; 1035 1036bool can_reuse (const llm_graph_params & params )override { 1037mctx = static_cast < const llama_memory_hybrid_idx_context *> (params .mctx )-> get_attn (); 1038return rows -> ne [0 ]== (int64_t )pmodel .hparams .ple_n_heads * params .ubatch .n_tokens ; 1039 } 1040 1041ggml_tensor * rows = nullptr ;// I32 [ple_n_heads * n_tokens] 1042 1043const llama_model_qwen4exp & pmodel ; 1044 1045// the predecessor tokens live in the attention KV cells (ext.tok) 1046const llama_kv_cache_context * mctx ; 1047 1048// scratch, reused across set_input() calls 1049 std::vector < llama_token > prev ; 1050}; 1051 1052void llm_graph_input_ple::set_input (const llama_ubatch * ubatch ) { 1053const auto & hp = pmodel .hparams ; 1054 1055// an image arrives as an embd batch, so ubatch->token is null, but every position still needs a row for ggml_get_rows 1056// stand in the image token id that the reference hashes, or EOS if the file has no such key 1057// gemma3n and gemma4 do the same with a hardcoded row 0 of per_layer_token_embd. 1058const llama_token img_tok = hp .ple_image_token_id != 0 1059 ? (llama_token )hp .ple_image_token_id 1060 : (llama_token )hp .ple_eos_token_id ; 1061auto tok_of = [& ](int64_t k )-> llama_token { 1062return ubatch -> token ?ubatch -> token [k ] :img_tok ; 1063 }; 1064 1065const int64_t n_tokens = ubatch -> n_tokens ; 1066const int64_t n_gram = hp .ple_ngram_size ; 1067const int64_t n_heads = hp .ple_n_heads ; 1068const int64_t per_gram = hp .ple_heads_per_ngram ; 1069const int64_t eos = hp .ple_eos_token_id ; 1070const int64_t n_prev = n_gram - 1 ; 1071 1072 std::vector < int32_t > idx (n_heads * n_tokens ); 1073 1074GGML_ASSERT (mctx != nullptr ); 1075 1076for (int64_t i = 0 ;i < n_tokens ;++ i ) { 1077// the preceding tokens would be ambiguous, see get_prev_tokens() 1078GGML_ASSERT (ubatch -> n_seq_id [i ]== 1 && "PLE n-gram embeddings do not support tokens shared by multiple sequences" ); 1079 } 1080 1081// predecessors come from the KV cells (ext.tok); apply_ubatch() already stored this ubatch, so its own tokens count too 1082mctx -> get_prev_tokens (* ubatch ,n_prev ,prev ); 1083 1084for (int64_t i = 0 ;i < n_tokens ;++ i ) { 1085// an EOS in the window resets everything at or before it 1086// a missing predecessor (before the sequence start, or no cached cell) reads as EOS 1087// the EOS of the token itself does not cut its own context, as in the reference 1088 std::vector < int64_t > ctx (n_gram ); 1089ctx [0 ]= tok_of (i ); 1090bool cut = false; 1091for (int64_t s = 1 ;s < n_gram ;++ s ) { 1092// predecessor s positions back; prev[] is oldest-first, missing entries are LLAMA_TOKEN_NULL 1093const llama_token t = cut ?LLAMA_TOKEN_NULL :prev [i * n_prev + (n_prev - s )]; 1094cut = cut || t < 0 || t == eos ; 1095ctx [s ]= cut ?eos :t ; 1096 } 1097 1098for (int64_t n = 2 ;n <=n_gram ;++ n ) { 1099uint64_t mixed = (uint64_t )ctx [0 ]* hp .ple_layer_multipliers [0 ]; 1100for (int64_t j = 1 ;j < n ;++ j ) { 1101mixed ^= (uint64_t )ctx [j ]* hp .ple_layer_multipliers [j ]; 1102 } 1103const int64_t base = (n - 2 )* per_gram ; 1104for (int64_t g = 0 ;g < per_gram ;++ g ) { 1105const int64_t h_i = base + g ; 1106idx [i * n_heads + h_i ]= 1107 (int32_t ) (mixed %hp .ple_head_vocab_sizes [h_i ]+ hp .ple_head_offsets [h_i ]); 1108 } 1109 } 1110 } 1111 1112ggml_backend_tensor_set (rows ,idx .data (),0 ,idx .size ()* ggml_element_size (rows )); 1113} 1114 1115// Read a conv history out of its own recurrent row and write the new tail back. 1116// The shared build_conv_state cannot do this: qwen4exp has two such rows per layer. 1117ggml_tensor * llama_model_qwen4exp::graph::build_conv_state_at ( 1118llm_graph_input_rs * inp , 1119ggml_tensor * conv_states_all , 1120ggml_tensor * x , 1121int64_t state_cols , 1122int64_t channels , 1123int il ) { 1124const auto * mctx_cur = inp -> mctx ; 1125 1126const auto kv_head = mctx_cur -> get_head (); 1127 1128const int64_t n_seqs = ubatch .n_seqs ; 1129const int64_t row_total = conv_states_all -> ne [0 ]; 1130 1131// the row is exactly this convolution's state, so the gather is reused as a whole 1132GGML_ASSERT (state_cols * channels == row_total ); 1133 1134auto it = rs_rows .find (conv_states_all ); 1135if (it == rs_rows .end ()) { 1136it = rs_rows .emplace (conv_states_all ,build_rs (inp ,conv_states_all ,row_total ,n_seqs )).first ; 1137 } 1138ggml_tensor * rows = it -> second ; 1139 1140ggml_tensor * state = ggml_reshape_3d (ctx0 ,rows ,state_cols ,channels ,n_seqs ); 1141cb (state ,"conv_state_at" ,il ); 1142 1143ggml_tensor * conv_input = ggml_concat (ctx0 ,state ,ggml_transpose (ctx0 ,x ),0 ); 1144 1145// [TAG_RECURRENT_ROLLBACK_SPLITS] keep the last state_cols columns once per rollback slot, 1146// slot s ending s tokens earlier so a rollback of s tokens reads a history that never saw them 1147const size_t row_size = ggml_row_size (conv_states_all -> type ,row_total ); 1148const uint32_t mem_size = mctx_cur -> get_size (); 1149 1150const int64_t n_slots = (int64_t )cparams .n_rs_seq + 1 ; 1151 1152for (int64_t slot = 0 ;slot < n_slots ;++ slot ) { 1153const int64_t s_idx = std::max < int64_t > (0 ,conv_input -> ne [0 ]- state_cols - slot ); 1154 1155ggml_tensor * tail = ggml_view_3d (ctx0 ,conv_input , 1156state_cols ,channels ,n_seqs , 1157conv_input -> nb [1 ],conv_input -> nb [2 ], 1158ggml_row_size (conv_input -> type ,s_idx )); 1159 1160ggml_tensor * dst = ggml_view_2d (ctx0 ,conv_states_all , 1161state_cols * channels ,n_seqs , 1162conv_states_all -> nb [1 ], 1163 (slot * mem_size + kv_head )* row_size ); 1164 1165ggml_build_forward_expand (gf ,ggml_cpy (ctx0 ,ggml_cont (ctx0 ,tail ),dst )); 1166 } 1167 1168return conv_input ; 1169} 1170 1171ggml_tensor * llama_model_qwen4exp::graph::build_inp_ple ( 1172const llama_memory_hybrid_idx_context * mctx_hyb ) { 1173const int64_t n_heads = hparams .ple_n_heads ; 1174 1175// the attention cells see every ubatch regardless of the layer types 1176auto ple_inp = std::make_unique < llm_graph_input_ple > ( 1177static_cast < const llama_model_qwen4exp &> (model ),mctx_hyb -> get_attn ()); 1178 1179ple_inp -> rows = ggml_new_tensor_1d (ctx0 ,GGML_TYPE_I32 ,n_heads * n_tokens ); 1180ggml_set_input (ple_inp -> rows ); 1181ggml_tensor * rows = ple_inp -> rows ; 1182res -> add_input (std::move (ple_inp )); 1183 1184// gather then flatten the heads: get_rows lays the head dimension out slowest, as the reference does 1185ggml_tensor * emb = ggml_get_rows (ctx0 ,model .per_layer_tok_embd ,rows ); 1186emb = ggml_reshape_2d (ctx0 ,emb ,hparams .ple_head_dim * n_heads ,n_tokens ); 1187cb (emb ,"ple_embd" ,-1 ); 1188 1189return emb ; 1190} 1191 1192ggml_tensor * llama_model_qwen4exp::graph::build_ple ( 1193llm_graph_input_rs * inp , 1194ggml_tensor * emb , 1195ggml_tensor * hidden , 1196int il ) { 1197const int64_t hc = hparams .dsv4_hc_mult ; 1198const int64_t hc_dim = hc * n_embd ; 1199 1200ggml_tensor * key = build_lora_mm (model .layers [il ].ple_key ,emb ); 1201ggml_tensor * value = build_lora_mm (model .layers [il ].ple_value ,emb ); 1202 1203// both norms group over one hc stream, with a [n_embd, hc] weight 1204auto grouped_norm = [& ](ggml_tensor * x ,ggml_tensor * w ) { 1205ggml_tensor * t = ggml_reshape_3d (ctx0 ,x ,n_embd ,hc ,n_tokens ); 1206return ggml_mul (ctx0 ,ggml_rms_norm (ctx0 ,t ,hparams .f_norm_rms_eps ),w ); 1207 }; 1208 1209key = grouped_norm (key ,model .layers [il ].ple_norm_key ); 1210ggml_tensor * query = grouped_norm (hidden ,model .layers [il ].ple_norm_query ); 1211 1212// per-stream dot product, then a signed square root before the sigmoid 1213ggml_tensor * s = ggml_sum_rows (ctx0 ,ggml_mul (ctx0 ,key ,query )); 1214s = ggml_scale (ctx0 ,s ,1.0f /sqrtf ((float )n_embd )); 1215 1216ggml_tensor * mag = ggml_sqrt (ctx0 ,ggml_clamp (ctx0 ,ggml_abs (ctx0 ,s ),1e-6f ,INFINITY )); 1217ggml_tensor * gate = ggml_sigmoid (ctx0 ,ggml_mul (ctx0 ,ggml_sgn (ctx0 ,s ),mag )); 1218cb (gate ,"ple_gate" ,il ); 1219 1220// [n_embd, 1, T] value broadcast across the hc streams, scaled by the gate 1221ggml_tensor * v3 = ggml_reshape_3d (ctx0 ,value ,n_embd ,1 ,n_tokens ); 1222v3 = ggml_repeat_4d (ctx0 ,v3 ,n_embd ,hc ,n_tokens ,1 ); 1223 1224ggml_tensor * gated = ggml_mul (ctx0 ,v3 ,gate ); 1225cb (gated ,"ple_gated_value" ,il ); 1226 1227ggml_tensor * normalized = grouped_norm ( 1228ggml_reshape_2d (ctx0 ,gated ,hc_dim ,n_tokens ), 1229model .layers [il ].ple_norm_conv ); 1230normalized = ggml_reshape_2d (ctx0 ,normalized ,hc_dim ,n_tokens ); 1231 1232// depthwise causal conv, dilated by the n-gram size, as a sum of shifted copies 1233// ggml_conv_1d_dw is documented as unreliable: 1234// out[c, t] = sum_k w[k, c] * x[c, t - (K-1-k)*dilation] 1235// The history of the earlier ubatches is prepended, so a chunked prefill matches a single-shot one. 1236const int64_t kern = hparams .ple_conv_kernel ; 1237const int64_t dil = hparams .ple_ngram_size ; 1238const int64_t hist = (kern - 1 )* dil ; 1239 1240// the conv history is per sequence, so the input carries the sequence axis too 1241const int64_t n_seqs = ubatch .n_seqs ; 1242const int64_t n_seq_tokens = ubatch .n_seq_tokens ; 1243 1244// [hist + n_seq_tokens, hc_dim, n_seqs], tokens on ne[0] 1245ggml_tensor * padded = build_conv_state_at (inp ,inp -> mctx -> get_p_l (il ), 1246ggml_reshape_3d (ctx0 ,normalized ,hc_dim ,n_seq_tokens ,n_seqs ), 1247hist ,hc_dim ,il ); 1248 1249ggml_tensor * conv_out = nullptr ; 1250for (int64_t k = 0 ;k < kern ;++ k ) { 1251// tap k reads (kern-1-k)*dilation positions back 1252const int64_t start = hist - (kern - 1 - k )* dil ; 1253 1254ggml_tensor * shifted = ggml_cont (ctx0 , 1255ggml_transpose (ctx0 , 1256ggml_view_3d (ctx0 ,padded ,n_seq_tokens ,hc_dim ,n_seqs , 1257padded -> nb [1 ],padded -> nb [2 ], 1258ggml_row_size (padded -> type ,start )))); 1259 1260// column k of the [kern, hc_dim] kernel is one weight per channel 1261ggml_tensor * wk = ggml_cont (ctx0 , 1262ggml_view_2d (ctx0 ,model .layers [il ].ple_conv1d ,1 ,hc_dim , 1263model .layers [il ].ple_conv1d -> nb [1 ], 1264k * model .layers [il ].ple_conv1d -> nb [0 ])); 1265// this kernel keeps the file type, so cast it before it multiplies an f32 activation 1266wk = ggml_reshape_1d (ctx0 ,wk ,hc_dim ); 1267if (wk -> type != GGML_TYPE_F32 ) { 1268wk = ggml_cast (ctx0 ,wk ,GGML_TYPE_F32 ); 1269 } 1270 1271ggml_tensor * term = ggml_mul (ctx0 ,shifted ,wk ); 1272conv_out = conv_out ?ggml_add (ctx0 ,conv_out ,term ) :term ; 1273 } 1274 1275conv_out = ggml_silu (ctx0 ,conv_out ); 1276conv_out = ggml_reshape_3d (ctx0 ,ggml_cont (ctx0 ,conv_out ),n_embd ,hc ,n_tokens ); 1277cb (conv_out ,"ple_conv_out" ,il ); 1278 1279return ggml_add (ctx0 ,hidden ,ggml_add (ctx0 ,gated ,conv_out )); 1280}