/src/llama.cpp/src/models/qwen3next.cpp
Line | Count | Source |
1 | | #include "models.h" |
2 | | #include "llama-memory-recurrent.h" |
3 | | |
4 | 0 | void llama_model_qwen3next::load_arch_hparams(llama_model_loader & ml) { |
5 | 0 | ml.get_key(LLM_KV_EXPERT_FEED_FORWARD_LENGTH, hparams.n_ff_exp, false); |
6 | 0 | ml.get_key(LLM_KV_EXPERT_SHARED_FEED_FORWARD_LENGTH, hparams.n_ff_shexp, false); |
7 | 0 | ml.get_key(LLM_KV_ATTENTION_LAYERNORM_RMS_EPS, hparams.f_norm_rms_eps); |
8 | | |
9 | | // Load linear attention (gated delta net) parameters |
10 | 0 | ml.get_key(LLM_KV_SSM_CONV_KERNEL, hparams.ssm_d_conv); |
11 | 0 | ml.get_key(LLM_KV_SSM_INNER_SIZE, hparams.ssm_d_inner); |
12 | 0 | ml.get_key(LLM_KV_SSM_STATE_SIZE, hparams.ssm_d_state); |
13 | 0 | ml.get_key(LLM_KV_SSM_TIME_STEP_RANK, hparams.ssm_dt_rank); |
14 | 0 | ml.get_key(LLM_KV_SSM_GROUP_COUNT, hparams.ssm_n_group); |
15 | | |
16 | | // Mark recurrent layers (linear attention layers) |
17 | 0 | if (!ml.get_key_or_arr(LLM_KV_ATTENTION_RECURRENT_LAYERS, hparams.is_recr_impl, hparams.n_layer_all, false)) { |
18 | 0 | uint32_t full_attn_interval = 4; |
19 | 0 | ml.get_key(LLM_KV_FULL_ATTENTION_INTERVAL, full_attn_interval, false); |
20 | 0 | for (uint32_t i = 0; i < hparams.n_layer_all; ++i) { |
21 | 0 | hparams.is_recr_impl[i] = (i < hparams.n_layer()) && ((i + 1) % full_attn_interval != 0); |
22 | 0 | } |
23 | 0 | } |
24 | |
|
25 | 0 | switch (hparams.n_layer()) { |
26 | 0 | case 48: type = LLM_TYPE_80B_A3B; break; |
27 | 0 | default: type = LLM_TYPE_UNKNOWN; |
28 | 0 | } |
29 | 0 | } |
30 | | |
31 | 0 | void llama_model_qwen3next::load_arch_tensors(llama_model_loader &) { |
32 | 0 | LLAMA_LOAD_LOCALS; |
33 | |
|
34 | 0 | if (n_expert == 0) { |
35 | 0 | throw std::runtime_error(arch_name() + " model cannot have zero experts"); |
36 | 0 | } |
37 | | |
38 | 0 | tok_embd = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), { n_embd, n_vocab }, 0); |
39 | | |
40 | | // output |
41 | 0 | output_norm = create_tensor(tn(LLM_TENSOR_OUTPUT_NORM, "weight"), { n_embd }, 0); |
42 | 0 | output = create_tensor(tn(LLM_TENSOR_OUTPUT, "weight"), { n_embd, n_vocab }, TENSOR_NOT_REQUIRED); |
43 | | |
44 | | // if output is NULL, init from the input tok embed |
45 | 0 | if (output == NULL) { |
46 | 0 | output = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), { n_embd, n_vocab }, TENSOR_DUPLICATED); |
47 | 0 | } |
48 | |
|
49 | 0 | const int64_t n_ff_exp = hparams.n_ff_exp ? hparams.n_ff_exp : n_ff / n_expert_used; |
50 | | |
51 | | // Calculate dimensions from hyperparameters |
52 | 0 | const int64_t head_k_dim = hparams.ssm_d_state; |
53 | 0 | const int64_t head_v_dim = hparams.ssm_d_state; |
54 | 0 | const int64_t n_k_heads = hparams.ssm_n_group; |
55 | 0 | const int64_t n_v_heads = hparams.ssm_dt_rank; |
56 | 0 | const int64_t key_dim = head_k_dim * n_k_heads; |
57 | 0 | const int64_t value_dim = head_v_dim * n_v_heads; |
58 | 0 | const int64_t conv_dim = key_dim * 2 + value_dim; |
59 | | |
60 | | // Calculate projection sizes |
61 | 0 | const int64_t qkvz_dim = key_dim * 2 + value_dim * 2; |
62 | 0 | const int64_t ba_dim = n_v_heads * 2; |
63 | |
|
64 | 0 | for (int i = 0; i < n_layer; ++i) { |
65 | 0 | auto & layer = layers[i]; |
66 | 0 | const uint32_t n_ff_shexp = hparams.n_ff_shexp > 0 ? hparams.n_ff_shexp : hparams.n_ff(i); |
67 | |
|
68 | 0 | layer.attn_norm = create_tensor(tn(LLM_TENSOR_ATTN_NORM, "weight", i), { n_embd }, 0); |
69 | 0 | layer.attn_post_norm = create_tensor(tn(LLM_TENSOR_ATTN_POST_NORM, "weight", i), { n_embd }, 0); |
70 | |
|
71 | 0 | if (!hparams.is_recr(i)) { |
72 | | // Attention layers |
73 | 0 | create_tensor_qkv(layer, i, n_embd, n_embd_head_k * n_head * 2, n_embd_k_gqa, n_embd_v_gqa, 0); |
74 | 0 | layer.wo = create_tensor(tn(LLM_TENSOR_ATTN_OUT, "weight", i), { n_embd_head_k * n_head, n_embd }, 0); |
75 | | |
76 | | // Q/K normalization for attention layers |
77 | 0 | layer.attn_q_norm = create_tensor(tn(LLM_TENSOR_ATTN_Q_NORM, "weight", i), { n_embd_head_k }, 0); |
78 | 0 | layer.attn_k_norm = create_tensor(tn(LLM_TENSOR_ATTN_K_NORM, "weight", i), { n_embd_head_k }, 0); |
79 | 0 | } else { |
80 | | // Linear attention (gated delta net) specific tensors |
81 | | // Create tensors with calculated dimensions |
82 | | // note: ssm_in is used by legacy GGUF |
83 | 0 | layer.ssm_in = create_tensor(tn(LLM_TENSOR_SSM_IN, "weight", i), { n_embd, qkvz_dim }, TENSOR_NOT_REQUIRED); |
84 | 0 | layer.wqkv = create_tensor(tn(LLM_TENSOR_ATTN_QKV, "weight", i), { n_embd, key_dim * 2 + value_dim }, TENSOR_NOT_REQUIRED); |
85 | 0 | layer.wqkv_gate = create_tensor(tn(LLM_TENSOR_ATTN_GATE, "weight", i), { n_embd, value_dim }, TENSOR_NOT_REQUIRED); |
86 | 0 | layer.ssm_conv1d = create_tensor(tn(LLM_TENSOR_SSM_CONV1D, "weight", i), { hparams.ssm_d_conv, conv_dim }, 0); |
87 | 0 | layer.ssm_dt = create_tensor(tn(LLM_TENSOR_SSM_DT, "bias", i), { hparams.ssm_dt_rank }, 0); |
88 | 0 | layer.ssm_a = create_tensor(tn(LLM_TENSOR_SSM_A_NOSCAN, i), { hparams.ssm_dt_rank }, 0); |
89 | 0 | layer.ssm_beta_alpha = create_tensor(tn(LLM_TENSOR_SSM_BETA_ALPHA, "weight", i), { n_embd, ba_dim }, 0); |
90 | 0 | layer.ssm_norm = create_tensor(tn(LLM_TENSOR_SSM_NORM, "weight", i), { head_v_dim }, 0); |
91 | 0 | layer.ssm_out = create_tensor(tn(LLM_TENSOR_SSM_OUT, "weight", i), { value_dim, n_embd }, 0); |
92 | 0 | } |
93 | |
|
94 | 0 | layer.ffn_gate_inp = create_tensor(tn(LLM_TENSOR_FFN_GATE_INP, "weight", i), { n_embd, n_expert }, 0); |
95 | 0 | layer.ffn_down_exps = create_tensor(tn(LLM_TENSOR_FFN_DOWN_EXPS, "weight", i), { n_ff_exp, n_embd, n_expert }, 0); |
96 | 0 | create_tensor_gate_up_exps(layer, i, n_embd, n_ff_exp, n_expert, 0); |
97 | | |
98 | | // Shared experts |
99 | 0 | layer.ffn_gate_inp_shexp = create_tensor(tn(LLM_TENSOR_FFN_GATE_INP_SHEXP, "weight", i), { n_embd }, 0); |
100 | 0 | layer.ffn_gate_shexp = create_tensor(tn(LLM_TENSOR_FFN_GATE_SHEXP, "weight", i), { n_embd, n_ff_shexp }, 0); |
101 | 0 | layer.ffn_up_shexp = create_tensor(tn(LLM_TENSOR_FFN_UP_SHEXP, "weight", i), { n_embd, n_ff_shexp }, 0); |
102 | 0 | layer.ffn_down_shexp = create_tensor(tn(LLM_TENSOR_FFN_DOWN_SHEXP, "weight", i), { n_ff_shexp, n_embd }, 0); |
103 | 0 | } |
104 | 0 | } |
105 | | |
106 | 0 | std::unique_ptr<llm_graph_context> llama_model_qwen3next::build_arch_graph(const llm_graph_params & params) const { |
107 | 0 | return std::make_unique<graph>(*this, params); |
108 | 0 | } |
109 | | |
110 | | llama_model_qwen3next::graph::graph(const llama_model & model, const llm_graph_params & params) : |
111 | 0 | llm_build_delta_net_base(params), model(model) { |
112 | 0 | ggml_tensor * cur; |
113 | 0 | ggml_tensor * inpL; |
114 | |
|
115 | 0 | inpL = build_inp_embd(model.tok_embd); |
116 | 0 | cb(inpL, "model.embed_tokens", -1); |
117 | |
|
118 | 0 | auto * inp = build_inp_mem_hybrid(); |
119 | |
|
120 | 0 | ggml_tensor * inp_pos = build_inp_pos(); |
121 | 0 | ggml_tensor * inp_out_ids = build_inp_out_ids(); |
122 | |
|
123 | 0 | for (int il = 0; il < n_layer; ++il) { |
124 | 0 | res->t_layer_inp[il] = inpL; |
125 | |
|
126 | 0 | ggml_tensor * inpSA = inpL; |
127 | |
|
128 | 0 | cur = build_norm(inpL, model.layers[il].attn_norm, nullptr, LLM_NORM_RMS, il); |
129 | 0 | cb(cur, "attn_norm", il); |
130 | |
|
131 | 0 | ggml_build_forward_expand(gf, cur); |
132 | | |
133 | | // Determine layer type and build appropriate attention mechanism |
134 | 0 | if (hparams.is_recr(il)) { |
135 | | // Linear attention layer (gated delta net) |
136 | 0 | cur = build_layer_attn_linear(inp->get_recr(), cur, il); |
137 | 0 | } else { |
138 | | // Full attention layer |
139 | 0 | cur = build_layer_attn(inp->get_attn(), cur, inp_pos, il); |
140 | 0 | } |
141 | |
|
142 | 0 | if (il == n_layer - 1 && inp_out_ids) { |
143 | 0 | cur = ggml_get_rows(ctx0, cur, inp_out_ids); |
144 | 0 | inpSA = ggml_get_rows(ctx0, inpSA, inp_out_ids); |
145 | 0 | } |
146 | | |
147 | | // Residual connection |
148 | 0 | cur = ggml_add(ctx0, cur, inpSA); |
149 | 0 | cb(cur, "attn_residual", il); |
150 | | |
151 | | // Save the tensor before post-attention norm for residual connection |
152 | 0 | ggml_tensor * ffn_residual = cur; |
153 | | |
154 | | // Post-attention norm |
155 | 0 | ggml_tensor * attn_post_norm = build_norm(cur, model.layers[il].attn_post_norm, nullptr, LLM_NORM_RMS, il); |
156 | 0 | cb(attn_post_norm, "attn_post_norm", il); |
157 | | |
158 | | // FFN layer (MoE or dense) - without residual connection |
159 | 0 | cur = build_layer_ffn(attn_post_norm, il); |
160 | 0 | cb(cur, "ffn_out", il); |
161 | | |
162 | | // Residual connection for FFN - add to the tensor from before post_attention_layernorm |
163 | 0 | cur = ggml_add(ctx0, cur, ffn_residual); |
164 | 0 | cb(cur, "post_moe", il); |
165 | |
|
166 | 0 | cur = build_cvec(cur, il); |
167 | 0 | cb(cur, "l_out", il); |
168 | | |
169 | | // Input for next layer |
170 | 0 | inpL = cur; |
171 | 0 | } |
172 | 0 | cur = inpL; |
173 | | |
174 | | // Final norm |
175 | 0 | cur = build_norm(cur, model.output_norm, nullptr, LLM_NORM_RMS, -1); |
176 | |
|
177 | 0 | cb(cur, "result_norm", -1); |
178 | 0 | res->t_embd = cur; |
179 | | |
180 | | // LM head |
181 | 0 | cur = build_lora_mm(model.output, cur, model.output_s); |
182 | |
|
183 | 0 | cb(cur, "result_output", -1); |
184 | 0 | res->t_logits = cur; |
185 | |
|
186 | 0 | ggml_build_forward_expand(gf, cur); |
187 | 0 | } |
188 | | |
189 | | // utility to get one slice from the third dimension |
190 | | // input dim: [x, y, c, b] |
191 | | // output dim: [x, y, 1, b] |
192 | 0 | static ggml_tensor * get_slice_2d(ggml_context * ctx0, ggml_tensor * t, int64_t c) { |
193 | 0 | return ggml_view_4d(ctx0, t, t->ne[0], t->ne[1], 1, t->ne[3], |
194 | 0 | t->nb[1], t->nb[2], t->nb[3], t->nb[2] * c); |
195 | 0 | } |
196 | | |
197 | | ggml_tensor * llama_model_qwen3next::graph::build_norm_gated( |
198 | | ggml_tensor * input, |
199 | | ggml_tensor * weights, |
200 | | ggml_tensor * gate, |
201 | 0 | int layer) { |
202 | 0 | ggml_tensor * normalized = build_norm(input, weights, nullptr, LLM_NORM_RMS, layer); |
203 | 0 | ggml_tensor * gated_silu = ggml_silu(ctx0, gate); |
204 | |
|
205 | 0 | return ggml_mul(ctx0, normalized, gated_silu); |
206 | 0 | } |
207 | | |
208 | | ggml_tensor * llama_model_qwen3next::graph::build_layer_attn( |
209 | | llm_graph_input_attn_kv * inp, |
210 | | ggml_tensor * cur, |
211 | | ggml_tensor * inp_pos, |
212 | 0 | int il) { |
213 | 0 | const int64_t n_embd_head = hparams.n_embd_head_v(); |
214 | 0 | GGML_ASSERT(n_embd_head == hparams.n_embd_head_k()); |
215 | | |
216 | | // Order: joint QG projection, QG split, Q norm, KV projection, K norm, RoPE, attention |
217 | | |
218 | | // Qwen3Next uses a single Q projection that outputs query + gate |
219 | 0 | ggml_tensor * Qcur_full = build_lora_mm(model.layers[il].wq, cur); |
220 | 0 | cb(Qcur_full, "Qcur_full", il); |
221 | |
|
222 | 0 | Qcur_full = ggml_reshape_4d(ctx0, Qcur_full, n_embd_head * 2, n_head, n_tokens, 1); |
223 | | |
224 | | // Split Q projection into query and gate |
225 | | // The split should be along dimension 0 (the feature dimension) |
226 | 0 | ggml_tensor * Qcur = ggml_view_4d(ctx0, Qcur_full, n_embd_head, n_head, n_tokens, 1, |
227 | 0 | Qcur_full->nb[1], Qcur_full->nb[2], Qcur_full->nb[3], 0); |
228 | 0 | cb(Qcur, "Qcur_view", il); |
229 | |
|
230 | 0 | ggml_tensor * gate = |
231 | 0 | ggml_view_4d(ctx0, Qcur_full, n_embd_head, n_head, n_tokens, 1, |
232 | 0 | Qcur_full->nb[1], Qcur_full->nb[2], Qcur_full->nb[3], n_embd_head * ggml_element_size(Qcur_full)); |
233 | 0 | cb(gate, "gate", il); |
234 | |
|
235 | 0 | ggml_tensor * Kcur = build_lora_mm(model.layers[il].wk, cur); |
236 | 0 | cb(Kcur, "Kcur", il); |
237 | |
|
238 | 0 | ggml_tensor * Vcur = build_lora_mm(model.layers[il].wv, cur); |
239 | 0 | cb(Vcur, "Vcur", il); |
240 | |
|
241 | 0 | Kcur = ggml_reshape_3d(ctx0, Kcur, n_embd_head, n_head_kv, n_tokens); |
242 | 0 | Vcur = ggml_reshape_3d(ctx0, Vcur, n_embd_head, n_head_kv, n_tokens); |
243 | |
|
244 | 0 | Qcur = build_norm(Qcur, model.layers[il].attn_q_norm, nullptr, LLM_NORM_RMS, il); |
245 | 0 | cb(Qcur, "Qcur_normed", il); |
246 | |
|
247 | 0 | Kcur = build_norm(Kcur, model.layers[il].attn_k_norm, nullptr, LLM_NORM_RMS, il); |
248 | 0 | cb(Kcur, "Kcur_normed", il); |
249 | |
|
250 | 0 | Qcur = ggml_rope_ext( |
251 | 0 | ctx0, Qcur, inp_pos, nullptr, |
252 | 0 | n_rot, rope_type, n_ctx_orig, freq_base, freq_scale, |
253 | 0 | ext_factor, attn_factor, beta_fast, beta_slow); |
254 | |
|
255 | 0 | Kcur = ggml_rope_ext( |
256 | 0 | ctx0, Kcur, inp_pos, nullptr, |
257 | 0 | n_rot, rope_type, n_ctx_orig, freq_base, |
258 | 0 | freq_scale, ext_factor, attn_factor, beta_fast, beta_slow); |
259 | |
|
260 | 0 | cb(Qcur, "Qcur", il); |
261 | 0 | cb(Kcur, "Kcur", il); |
262 | 0 | cb(Vcur, "Vcur", il); |
263 | |
|
264 | 0 | const float kq_scale = hparams.f_attention_scale == 0.0f ? 1.0f / sqrtf(float(n_embd_head)) : hparams.f_attention_scale; |
265 | |
|
266 | 0 | cur = build_attn(inp, |
267 | 0 | nullptr, nullptr, nullptr, |
268 | 0 | Qcur, Kcur, Vcur, nullptr, nullptr, nullptr, kq_scale, il); |
269 | 0 | cb(cur, "attn_pregate", il); |
270 | | |
271 | | // TODO: CUDA is missing non-contiguous unary ops. when implemented: remove this cont |
272 | 0 | gate = ggml_cont_2d(ctx0, gate, n_embd_head * n_head, n_tokens); |
273 | |
|
274 | 0 | gate = ggml_sigmoid(ctx0, gate); |
275 | 0 | cb(gate, "gate_sigmoid", il); |
276 | |
|
277 | 0 | gate = ggml_reshape_2d(ctx0, gate, n_embd_head * n_head, n_tokens); |
278 | |
|
279 | 0 | cur = ggml_mul(ctx0, cur, gate); |
280 | 0 | cb(cur, "attn_gated", il); |
281 | |
|
282 | 0 | cur = build_lora_mm(model.layers[il].wo, cur, model.layers[il].wo_s); |
283 | 0 | cb(cur, "attn_output", il); |
284 | |
|
285 | 0 | return cur; |
286 | 0 | } |
287 | | |
288 | | std::pair<ggml_tensor *, ggml_tensor *> llama_model_qwen3next::graph::build_qkvz( |
289 | | ggml_tensor * input, |
290 | 0 | int il) { |
291 | 0 | const int64_t d_inner = hparams.ssm_d_inner; |
292 | 0 | const int64_t n_seqs = ubatch.n_seqs; |
293 | 0 | const int64_t head_k_dim = hparams.ssm_d_state; |
294 | 0 | const int64_t num_k_heads = hparams.ssm_n_group; |
295 | 0 | const int64_t num_v_heads = hparams.ssm_dt_rank; |
296 | 0 | const int64_t head_v_dim = d_inner / num_v_heads; |
297 | 0 | const int64_t n_seq_tokens = ubatch.n_seq_tokens; |
298 | |
|
299 | 0 | if (model.layers[il].wqkv) { |
300 | | // optimized path |
301 | 0 | ggml_tensor * qkv_mixed = build_lora_mm(model.layers[il].wqkv, input); |
302 | 0 | qkv_mixed = ggml_reshape_3d(ctx0, qkv_mixed, qkv_mixed->ne[0], n_seq_tokens, n_seqs); |
303 | 0 | cb(qkv_mixed, "linear_attn_qkv_mixed", il); |
304 | |
|
305 | 0 | ggml_tensor * z = build_lora_mm(model.layers[il].wqkv_gate, input); |
306 | 0 | cb(z, "z", il); |
307 | |
|
308 | 0 | return { qkv_mixed, z }; |
309 | 0 | } else { |
310 | | // legacy (slower) path |
311 | 0 | ggml_tensor * mixed_qkvz = build_lora_mm(model.layers[il].ssm_in, input); |
312 | 0 | cb(mixed_qkvz, "linear_attn_mixed_qkvz", il); |
313 | |
|
314 | 0 | int64_t qkvz_new_dim = 2 * head_k_dim + 2 * head_v_dim * (num_v_heads / num_k_heads); |
315 | 0 | ggml_tensor * mixed_qkvz_reshaped = ggml_reshape_4d(ctx0, mixed_qkvz, qkvz_new_dim, num_k_heads, n_seq_tokens, n_seqs); |
316 | | |
317 | | // Split mixed_qkvz into query, key, value, z |
318 | 0 | int64_t split_sizes_qkvz[4] = { |
319 | 0 | head_k_dim, // query size |
320 | 0 | head_k_dim, // key size |
321 | 0 | head_v_dim * num_v_heads / num_k_heads, // value size |
322 | 0 | head_v_dim * num_v_heads / num_k_heads // z size |
323 | 0 | }; |
324 | |
|
325 | 0 | ggml_tensor * query = |
326 | 0 | ggml_view_4d(ctx0, mixed_qkvz_reshaped, split_sizes_qkvz[0], num_k_heads, n_seq_tokens, n_seqs, |
327 | 0 | mixed_qkvz_reshaped->nb[1], mixed_qkvz_reshaped->nb[2], mixed_qkvz_reshaped->nb[3], 0); |
328 | 0 | cb(query, "q", il); |
329 | |
|
330 | 0 | ggml_tensor * key = ggml_view_4d(ctx0, mixed_qkvz_reshaped, split_sizes_qkvz[1], num_k_heads, n_seq_tokens, n_seqs, |
331 | 0 | mixed_qkvz_reshaped->nb[1], mixed_qkvz_reshaped->nb[2], mixed_qkvz_reshaped->nb[3], |
332 | 0 | split_sizes_qkvz[0] * ggml_element_size(mixed_qkvz_reshaped)); |
333 | 0 | cb(key, "k", il); |
334 | |
|
335 | 0 | ggml_tensor * value = |
336 | 0 | ggml_view_4d(ctx0, mixed_qkvz_reshaped, split_sizes_qkvz[2], num_k_heads, n_seq_tokens, n_seqs, |
337 | 0 | mixed_qkvz_reshaped->nb[1], mixed_qkvz_reshaped->nb[2], mixed_qkvz_reshaped->nb[3], |
338 | 0 | (split_sizes_qkvz[0] + split_sizes_qkvz[1]) * ggml_element_size(mixed_qkvz_reshaped)); |
339 | 0 | cb(value, "v", il); |
340 | |
|
341 | 0 | ggml_tensor * z = ggml_view_4d(ctx0, mixed_qkvz_reshaped, split_sizes_qkvz[3], num_k_heads, n_seq_tokens, n_seqs, |
342 | 0 | mixed_qkvz_reshaped->nb[1], mixed_qkvz_reshaped->nb[2], mixed_qkvz_reshaped->nb[3], |
343 | 0 | (split_sizes_qkvz[0] + split_sizes_qkvz[1] + split_sizes_qkvz[2]) * ggml_element_size(mixed_qkvz_reshaped)); |
344 | 0 | z = ggml_cont(ctx0, z); |
345 | 0 | cb(z, "z", il); |
346 | | |
347 | | // After creating query, key, and value_reshaped, reshape each to flatten the head dimensions |
348 | | // query: [head_k_dim, num_k_heads, n_tokens, n_seqs] -> [head_k_dim * num_k_heads, n_tokens, n_seqs] |
349 | 0 | ggml_tensor * query_flat = ggml_cont_3d(ctx0, query, head_k_dim * num_k_heads, n_seq_tokens, n_seqs); |
350 | 0 | cb(query_flat, "query_flat", il); |
351 | | |
352 | | // key: [head_k_dim, num_k_heads, n_tokens, n_seqs] -> [head_k_dim * num_k_heads, n_tokens, n_seqs] |
353 | 0 | ggml_tensor * key_flat = ggml_cont_3d(ctx0, key, head_k_dim * num_k_heads, n_seq_tokens, n_seqs); |
354 | 0 | cb(key_flat, "key_flat", il); |
355 | | |
356 | | // value_reshaped: [head_v_dim, num_v_heads, n_tokens, n_seqs] -> [head_v_dim * num_v_heads, n_tokens, n_seqs] |
357 | 0 | ggml_tensor * value_flat = ggml_cont_3d(ctx0, value, head_v_dim * num_v_heads, n_seq_tokens, n_seqs); |
358 | 0 | cb(value_flat, "value_flat", il); |
359 | | |
360 | | // Now concatenate along the feature dimension (dim 0) to get [conv_dim, n_tokens, n_seqs] |
361 | 0 | ggml_tensor * qkv_mixed = ggml_concat(ctx0, query_flat, key_flat, 0); |
362 | 0 | qkv_mixed = ggml_concat(ctx0, qkv_mixed, value_flat, 0); |
363 | 0 | cb(qkv_mixed, "qkv_mixed", il); |
364 | |
|
365 | 0 | return { qkv_mixed, z }; |
366 | 0 | } |
367 | 0 | } |
368 | | |
369 | | ggml_tensor * llama_model_qwen3next::graph::build_layer_attn_linear( |
370 | | llm_graph_input_rs * inp, |
371 | | ggml_tensor * cur, |
372 | 0 | int il) { |
373 | 0 | const auto * mctx_cur = inp->mctx; |
374 | |
|
375 | 0 | const int64_t d_inner = hparams.ssm_d_inner; |
376 | 0 | const int64_t n_seqs = ubatch.n_seqs; |
377 | 0 | const int64_t head_k_dim = hparams.ssm_d_state; |
378 | 0 | const int64_t num_k_heads = hparams.ssm_n_group; |
379 | 0 | const int64_t num_v_heads = hparams.ssm_dt_rank; |
380 | 0 | const int64_t head_v_dim = d_inner / num_v_heads; |
381 | 0 | const int64_t n_seq_tokens = ubatch.n_seq_tokens; |
382 | |
|
383 | 0 | GGML_ASSERT(n_seqs != 0); |
384 | 0 | GGML_ASSERT(ubatch.equal_seqs()); |
385 | 0 | GGML_ASSERT(ubatch.n_tokens == n_seq_tokens * n_seqs); |
386 | | |
387 | | // Input projections |
388 | 0 | auto qkvz = build_qkvz(cur, il); |
389 | 0 | ggml_tensor * qkv_mixed = qkvz.first; |
390 | 0 | ggml_tensor * z = qkvz.second; |
391 | |
|
392 | 0 | ggml_tensor * mixed_ba = build_lora_mm(model.layers[il].ssm_beta_alpha, cur); |
393 | 0 | cb(mixed_ba, "linear_attn_mixed_ba", il); |
394 | | |
395 | | // Reshape mixed_ba: [batch, seq_len, hidden_size] -> [batch, seq_len, num_k_heads, 2*num_v_heads/num_k_heads] |
396 | 0 | int64_t ba_new_dim = 2 * num_v_heads / num_k_heads; |
397 | 0 | ggml_tensor * mixed_ba_reshaped = ggml_reshape_4d(ctx0, mixed_ba, ba_new_dim, num_k_heads, n_seq_tokens, n_seqs); |
398 | | |
399 | | // Split mixed_ba into b and a (beta and alpha parameters) |
400 | 0 | int64_t split_sizes_ba[2] = { |
401 | 0 | num_v_heads / num_k_heads, // beta size |
402 | 0 | num_v_heads / num_k_heads // alpha size |
403 | 0 | }; |
404 | |
|
405 | 0 | ggml_tensor * b = ggml_view_4d(ctx0, mixed_ba_reshaped, split_sizes_ba[0], num_k_heads, n_seq_tokens, n_seqs, |
406 | 0 | mixed_ba_reshaped->nb[1], mixed_ba_reshaped->nb[2], mixed_ba_reshaped->nb[3], 0); |
407 | 0 | cb(b, "b", il); |
408 | |
|
409 | 0 | ggml_tensor * a = ggml_view_4d(ctx0, mixed_ba_reshaped, split_sizes_ba[1], num_k_heads, n_seq_tokens, n_seqs, |
410 | 0 | mixed_ba_reshaped->nb[1], mixed_ba_reshaped->nb[2], mixed_ba_reshaped->nb[3], |
411 | 0 | split_sizes_ba[0] * ggml_element_size(mixed_ba_reshaped)); |
412 | 0 | cb(a, "a", il); |
413 | | |
414 | | // TODO: CUDA is missing non-contiguous unary ops. when implemented: remove this cont |
415 | 0 | b = ggml_cont(ctx0, b); |
416 | |
|
417 | 0 | ggml_tensor * beta = ggml_sigmoid(ctx0, b); |
418 | | |
419 | | // Reshape a to merge head dimensions: [batch, seq_len, num_k_heads, num_v_heads/num_k_heads] -> [batch, seq_len, num_v_heads] |
420 | 0 | ggml_tensor * alpha = ggml_cont_3d(ctx0, a, num_v_heads, n_seq_tokens, n_seqs); |
421 | |
|
422 | 0 | ggml_tensor * alpha_biased = ggml_add(ctx0, alpha, model.layers[il].ssm_dt); |
423 | 0 | ggml_tensor * alpha_softplus = ggml_softplus(ctx0, alpha_biased); |
424 | 0 | cb(alpha_softplus, "a_softplus", il); |
425 | |
|
426 | 0 | ggml_tensor * gate = ggml_mul(ctx0, alpha_softplus, model.layers[il].ssm_a); // -A_log.exp() * softplus |
427 | 0 | cb(gate, "gate", il); |
428 | |
|
429 | 0 | beta = ggml_reshape_4d(ctx0, beta, 1, num_v_heads, n_seq_tokens, n_seqs); |
430 | 0 | gate = ggml_reshape_4d(ctx0, gate, 1, num_v_heads, n_seq_tokens, n_seqs); |
431 | |
|
432 | 0 | ggml_tensor * conv_states_all = mctx_cur->get_r_l(il); |
433 | 0 | ggml_tensor * ssm_states_all = mctx_cur->get_s_l(il); |
434 | |
|
435 | 0 | ggml_tensor * conv_kernel = model.layers[il].ssm_conv1d; |
436 | 0 | const int64_t conv_kernel_size = conv_kernel->ne[0]; |
437 | 0 | const int64_t conv_channels = d_inner + 2 * hparams.ssm_n_group * hparams.ssm_d_state; |
438 | |
|
439 | 0 | ggml_tensor * conv_input = build_conv_state(inp, conv_states_all, qkv_mixed, conv_kernel_size, conv_channels, il); |
440 | |
|
441 | 0 | ggml_tensor * state = build_rs(inp, ssm_states_all, hparams.n_embd_s(), n_seqs); |
442 | 0 | state = ggml_reshape_4d(ctx0, state, head_v_dim, head_v_dim, num_v_heads, n_seqs); |
443 | 0 | cb(state, "state_predelta", il); |
444 | |
|
445 | 0 | ggml_tensor * conv_output_proper = ggml_ssm_conv(ctx0, conv_input, conv_kernel); |
446 | 0 | cb(conv_output_proper, "conv_output_raw", il); |
447 | |
|
448 | 0 | ggml_tensor * conv_output_silu = ggml_silu(ctx0, conv_output_proper); |
449 | 0 | cb(conv_output_silu, "conv_output_silu", il); |
450 | |
|
451 | 0 | ggml_tensor * conv_qkv_mix = conv_output_silu; |
452 | | |
453 | | // Calculate the total conv dimension |
454 | 0 | int64_t qkv_dim = head_k_dim * num_k_heads * 2 + head_v_dim * num_v_heads; |
455 | 0 | int64_t nb1_qkv = ggml_row_size(conv_qkv_mix->type, qkv_dim); |
456 | | |
457 | | // Extract the convolved Q, K, V from conv_output |
458 | 0 | ggml_tensor * q_conv = ggml_view_4d(ctx0, conv_qkv_mix, head_k_dim, num_k_heads, n_seq_tokens, n_seqs, |
459 | 0 | ggml_row_size(conv_qkv_mix->type, head_k_dim), |
460 | 0 | nb1_qkv, |
461 | 0 | nb1_qkv * n_seq_tokens, |
462 | 0 | 0); |
463 | |
|
464 | 0 | ggml_tensor * k_conv = ggml_view_4d(ctx0, conv_qkv_mix, head_k_dim, num_k_heads, n_seq_tokens, n_seqs, |
465 | 0 | ggml_row_size(conv_qkv_mix->type, head_k_dim), |
466 | 0 | nb1_qkv, |
467 | 0 | nb1_qkv * n_seq_tokens, |
468 | 0 | head_k_dim * num_k_heads * ggml_element_size(conv_qkv_mix)); |
469 | |
|
470 | 0 | ggml_tensor * v_conv = ggml_view_4d(ctx0, conv_qkv_mix, head_v_dim, num_v_heads, n_seq_tokens, n_seqs, |
471 | 0 | ggml_row_size(conv_qkv_mix->type, head_v_dim), |
472 | 0 | nb1_qkv, |
473 | 0 | nb1_qkv * n_seq_tokens, |
474 | 0 | ggml_row_size(conv_qkv_mix->type, 2 * head_k_dim * num_k_heads)); |
475 | |
|
476 | 0 | cb(q_conv, "q_conv", il); |
477 | 0 | cb(k_conv, "k_conv", il); |
478 | 0 | cb(v_conv, "v_conv", il); |
479 | |
|
480 | 0 | const float eps_norm = hparams.f_norm_rms_eps; |
481 | |
|
482 | 0 | q_conv = ggml_l2_norm(ctx0, q_conv, eps_norm); |
483 | 0 | k_conv = ggml_l2_norm(ctx0, k_conv, eps_norm); |
484 | | |
485 | | //q_conv = ggml_cont_4d(ctx0, q_conv, head_k_dim, num_k_heads, n_seq_tokens, n_seqs); |
486 | | //k_conv = ggml_cont_4d(ctx0, k_conv, head_k_dim, num_k_heads, n_seq_tokens, n_seqs); |
487 | | //v_conv = ggml_cont_4d(ctx0, v_conv, head_v_dim, num_v_heads, n_seq_tokens, n_seqs); |
488 | | |
489 | | // if head keys and value keys are different, repeat to force tensors into matching shapes |
490 | | // TODO: avoid repeats for fused GDN, needs broadcast configuration for GDN op [TAG_GGML_GDN_BCAST] |
491 | 0 | if (num_k_heads != num_v_heads) { |
492 | 0 | GGML_ASSERT(num_v_heads % num_k_heads == 0); |
493 | 0 | int64_t repeat_factor = num_v_heads / num_k_heads; |
494 | | |
495 | | // repeat interleave: reshape to (repeat part, 1, remaining part...), do repeat, then reshape back |
496 | 0 | ggml_tensor * q_reshaped = ggml_reshape_4d(ctx0, q_conv, head_k_dim, 1, num_k_heads, n_seq_tokens * n_seqs); |
497 | 0 | ggml_tensor * k_reshaped = ggml_reshape_4d(ctx0, k_conv, head_k_dim, 1, num_k_heads, n_seq_tokens * n_seqs); |
498 | | |
499 | | // Repeat along the third dimension (the new dimension with size 1) |
500 | 0 | ggml_tensor * q_repeated = |
501 | 0 | ggml_repeat_4d(ctx0, q_reshaped, head_k_dim, repeat_factor, num_k_heads, n_seq_tokens * n_seqs); |
502 | 0 | ggml_tensor * k_repeated = |
503 | 0 | ggml_repeat_4d(ctx0, k_reshaped, head_k_dim, repeat_factor, num_k_heads, n_seq_tokens * n_seqs); |
504 | | |
505 | | // Reshape back to merge the head and repeat dimensions |
506 | | // From [head_dim, repeat_factor, num_k_heads, n_seq_tokens * n_seqs] |
507 | | // Back to [head_dim, repeat_factor * num_k_heads, n_seq_tokens, n_seqs] |
508 | 0 | q_conv = ggml_reshape_4d(ctx0, q_repeated, head_k_dim, num_k_heads * repeat_factor, n_seq_tokens, n_seqs); |
509 | 0 | k_conv = ggml_reshape_4d(ctx0, k_repeated, head_k_dim, num_k_heads * repeat_factor, n_seq_tokens, n_seqs); |
510 | 0 | } |
511 | |
|
512 | 0 | cb(q_conv, "q_conv_predelta", il); |
513 | 0 | cb(k_conv, "k_conv_predelta", il); |
514 | 0 | cb(v_conv, "v_conv_predelta", il); |
515 | |
|
516 | 0 | ggml_tensor * output = build_recurrent_attn(inp, ssm_states_all, q_conv, k_conv, v_conv, gate, beta, state, il); |
517 | | |
518 | | // z: [head_dim, n_heads, n_tokens, n_seqs] -> [n_heads * n_tokens * n_seqs, head_dim] |
519 | 0 | ggml_tensor * z_2d = ggml_reshape_4d(ctx0, z, head_v_dim, num_v_heads, n_seq_tokens, n_seqs); |
520 | | |
521 | | // Apply gated normalization: self.norm(core_attn_out, z) |
522 | 0 | ggml_tensor * attn_out_norm = build_norm_gated(output, model.layers[il].ssm_norm, z_2d, il); |
523 | | |
524 | | // Final reshape: [head_dim, n_heads, n_tokens, n_seqs] -> [n_tokens, n_seqs, n_heads * head_dim] |
525 | 0 | ggml_tensor * final_output = ggml_reshape_3d(ctx0, attn_out_norm, head_v_dim * num_v_heads, n_seq_tokens, n_seqs); |
526 | 0 | cb(final_output, "final_output", il); |
527 | | |
528 | | // Output projection |
529 | 0 | cur = build_lora_mm(model.layers[il].ssm_out, final_output); |
530 | 0 | cb(cur, "linear_attn_out", il); |
531 | | |
532 | | // Reshape back to original dimensions |
533 | 0 | cur = ggml_reshape_2d(ctx0, cur, n_embd, n_seq_tokens * n_seqs); |
534 | |
|
535 | 0 | return cur; |
536 | 0 | } |
537 | | |
538 | 0 | ggml_tensor * llama_model_qwen3next::graph::build_layer_ffn(ggml_tensor * cur, const int il) { |
539 | | // Check if this is an MoE layer |
540 | 0 | if (model.layers[il].ffn_gate_inp != nullptr) { |
541 | | // MoE branch |
542 | 0 | ggml_tensor * moe_out = |
543 | 0 | build_moe_ffn(cur, |
544 | 0 | model.layers[il].ffn_gate_inp, |
545 | 0 | model.layers[il].ffn_up_exps, |
546 | 0 | model.layers[il].ffn_gate_exps, |
547 | 0 | model.layers[il].ffn_down_exps, |
548 | 0 | nullptr, |
549 | 0 | n_expert, n_expert_used, |
550 | 0 | LLM_FFN_SILU, true, |
551 | 0 | hparams.expert_weights_scale, |
552 | 0 | LLAMA_EXPERT_GATING_FUNC_TYPE_SOFTMAX, il, |
553 | 0 | nullptr, model.layers[il].ffn_gate_up_exps); |
554 | 0 | cb(moe_out, "ffn_moe_out", il); |
555 | | |
556 | | // Add shared experts if present - following Qwen3Next reference implementation |
557 | 0 | if (model.layers[il].ffn_up_shexp != nullptr) { |
558 | 0 | ggml_tensor * ffn_shexp = |
559 | 0 | build_ffn(cur, |
560 | 0 | model.layers[il].ffn_up_shexp, NULL, NULL, |
561 | 0 | model.layers[il].ffn_gate_shexp, NULL, NULL, |
562 | 0 | model.layers[il].ffn_down_shexp, NULL, NULL, |
563 | 0 | NULL, |
564 | 0 | LLM_FFN_SILU, LLM_FFN_PAR, il); |
565 | 0 | cb(ffn_shexp, "ffn_shexp", il); |
566 | | |
567 | | // Apply shared expert gating as in the reference implementation |
568 | | // The shared expert has its own gate that is sigmoided |
569 | | // Note: ffn_gate_inp_shexp is the shared expert gate (outputs 1 value per token) |
570 | 0 | ggml_tensor * shared_gate = build_lora_mm(model.layers[il].ffn_gate_inp_shexp, cur); |
571 | 0 | cb(shared_gate, "shared_expert_gate", il); |
572 | |
|
573 | 0 | shared_gate = ggml_sigmoid(ctx0, shared_gate); |
574 | 0 | cb(shared_gate, "shared_expert_gate_sigmoid", il); |
575 | |
|
576 | 0 | ffn_shexp = ggml_mul(ctx0, ffn_shexp, shared_gate); |
577 | 0 | cb(ffn_shexp, "ffn_shexp_gated", il); |
578 | |
|
579 | 0 | cur = ggml_add(ctx0, moe_out, ffn_shexp); |
580 | 0 | cb(cur, "ffn_out", il); |
581 | 0 | } else { |
582 | 0 | cur = moe_out; |
583 | 0 | } |
584 | 0 | } else { |
585 | | // Dense FFN branch (not currently used I believe) |
586 | 0 | cur = build_ffn(cur, |
587 | 0 | model.layers[il].ffn_up, NULL, NULL, |
588 | 0 | model.layers[il].ffn_gate, NULL, NULL, |
589 | 0 | model.layers[il].ffn_down, NULL, NULL, |
590 | | NULL, |
591 | 0 | LLM_FFN_SILU, LLM_FFN_PAR, il); |
592 | 0 | cb(cur, "ffn_out", il); |
593 | 0 | } |
594 | 0 | return cur; |
595 | 0 | } |