← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
deltanet_kernels.c
Go to the documentation of this file.
1/**
2 * @file deltanet_kernels.c
3 * @brief FP32 Gated DeltaNet kernels for Qwen3.5-style recurrent attention.
4 *
5 * CK-ENGINE KERNEL RULES:
6 * =======================
7 * 1. NO malloc/free - memory via bump allocator, pointers passed in
8 * 2. NO OpenMP - parallelization at orchestrator/codegen layer
9 * 3. API must define: inputs, outputs, workspace, and memory layouts
10 * 4. Pure computation - deterministic, no side effects
11 *
12 * After changes: make test && make llamacpp-parity-full
13 *
14 * This file implements the single-token recurrent update used by the
15 * qwen3next / Gated DeltaNet path in llama.cpp.
16 *
17 * Per head, matching llama.cpp qwen35/qwen3next autoregressive DeltaNet:
18 * q_scaled = q / sqrt(state_dim) // q and k arrive pre-normalized
19 * k_hat = k
20 * beta_s = sigmoid(beta)
21 * gate = exp(g)
22 * S = gate * S_prev
23 * kv_mem = S^T * k_hat
24 * delta = (v - kv_mem) * beta_s
25 * S_new = S + outer(k_hat, delta)
26 * out = S_new^T * q_scaled
27 *
28 * Design:
29 * - *_ref is the scalar reference implementation.
30 * - *_avx keeps a simple 1-row vector walk.
31 * - *_avx2 precomputes scaled q rows and unrolls the state sweep in
32 * row pairs to reduce loop overhead and layout churn.
33 * - The public dispatcher selects the best compiled ISA unless strict parity
34 * is enabled, in which case it falls back to *_ref.
35 */
36
37#ifndef _GNU_SOURCE
38#define _GNU_SOURCE
39#endif
40
41#include "bf16_utils.h"
42#include "ckernel_engine.h"
43
44#include <dlfcn.h>
45#include <math.h>
46#include <pthread.h>
47#include <stdio.h>
48#include <stddef.h>
49#include <stdlib.h>
50#include <string.h>
51
52#if defined(__AVX__) || defined(__AVX2__) || defined(__AVX512F__)
53#include <immintrin.h>
54#endif
55
56#define CK_DELTANET_MAX_STACK_DIM 4096
57#define CK_DELTANET_LLAMA_CHUNK_SIZE 64
58#define CK_DELTANET_LLAMA_CHUNK_MAX_DIM 256
59
60#if defined(__GNUC__) || defined(__clang__)
61#define CK_DELTANET_NOINLINE __attribute__((noinline))
62#else
63#define CK_DELTANET_NOINLINE
64#endif
65
66typedef float (*ck_deltanet_libm_f32_fn)(float);
68static void *ck_deltanet_libm_handle = NULL;
69static pthread_once_t ck_deltanet_libm_once = PTHREAD_ONCE_INIT;
70
72{
73 ck_deltanet_libm_handle = dlopen("libm.so.6", RTLD_NOW | RTLD_LOCAL);
77 }
79 fprintf(stderr,
80 "HARD KERNEL CONTRACT FAULT: llama.cpp DeltaNet requires "
81 "expf from libm.so.6\n");
82 abort();
83 }
84}
85
86static inline float ck_deltanet_sigmoidf(float x)
87{
88 return 1.0f / (1.0f + expf(-x));
89}
90
91static inline float ck_deltanet_llama_sigmoidf(float x)
92{
94 return 1.0f / (1.0f + ck_deltanet_llama_expf(-x));
95}
96
97#if defined(__AVX512F__)
98typedef __m512 (*ck_deltanet_sleef_expf16_fn)(__m512);
99static ck_deltanet_sleef_expf16_fn ck_deltanet_pytorch_expf16 = NULL;
100static void *ck_deltanet_sleef_handle = NULL;
101#endif
102
103typedef void (*ck_deltanet_mkl_vsexp_fn)(int, const float *, float *);
105static void *ck_deltanet_mkl_handle = NULL;
106static pthread_once_t ck_deltanet_pytorch_primitives_once = PTHREAD_ONCE_INIT;
107
109{
110 const char *mkl_library = getenv("CK_MKL_LIBRARY");
111 if (mkl_library && *mkl_library) {
112 ck_deltanet_mkl_handle = dlopen(mkl_library, RTLD_NOW | RTLD_LOCAL);
116 ck_deltanet_mkl_handle, "vsExp");
117 }
118 } else {
120 (ck_deltanet_mkl_vsexp_fn)dlsym(RTLD_DEFAULT, "vsExp");
121 }
122
123#if defined(__AVX512F__)
124 const char *library = getenv("CK_SLEEF_LIBRARY");
125 if (library && *library) {
126 ck_deltanet_sleef_handle = dlopen(library, RTLD_NOW | RTLD_LOCAL);
127 if (ck_deltanet_sleef_handle) {
128 ck_deltanet_pytorch_expf16 =
129 (ck_deltanet_sleef_expf16_fn)dlsym(
130 ck_deltanet_sleef_handle, "Sleef_expf16_u10");
131 }
132 } else {
133 ck_deltanet_pytorch_expf16 =
134 (ck_deltanet_sleef_expf16_fn)dlsym(
135 RTLD_DEFAULT, "Sleef_expf16_u10");
136 }
137#endif
138}
139
140static void ck_deltanet_pytorch_gate_values(const float *g,
141 const float *beta,
142 float *gate_values,
143 float *beta_values,
144 int num_heads)
145{
146 pthread_once(
150 fprintf(stderr,
151 "HARD KERNEL CONTRACT FAULT: PyTorch DeltaNet requires "
152 "MKL vsExp; set CK_MKL_LIBRARY\n");
153 abort();
154 }
155 ck_deltanet_pytorch_vsexp(num_heads, g, gate_values);
156
157 int h = 0;
158#if defined(__AVX512F__)
159 if (ck_deltanet_pytorch_expf16) {
160 const __m512 one = _mm512_set1_ps(1.0f);
161 for (; h + 15 < num_heads; h += 16) {
162 const __m512 bv = _mm512_loadu_ps(beta + h);
163 const __m512 beta_exp = ck_deltanet_pytorch_expf16(
164 _mm512_sub_ps(_mm512_setzero_ps(), bv));
165 const __m512 beta_sigmoid = _mm512_div_ps(
166 one, _mm512_add_ps(one, beta_exp));
167 float beta_lanes[16];
168 _mm512_storeu_ps(beta_lanes, beta_sigmoid);
169 for (int lane = 0; lane < 16; ++lane) {
170 beta_values[h + lane] = bf16_to_float(
171 float_to_bf16(beta_lanes[lane]));
172 }
173 }
174 }
175#endif
176 for (; h < num_heads; ++h) {
177 beta_values[h] = bf16_to_float(float_to_bf16(
178 ck_deltanet_sigmoidf(beta[h])));
179 }
180}
181
183 const float *beta,
184 float *gate_values,
185 float *beta_values,
186 int num_heads)
187{
188 if (!g || !beta || !gate_values || !beta_values || num_heads <= 0 ||
189 num_heads > CK_DELTANET_MAX_STACK_DIM) {
190 return;
191 }
193 g, beta, gate_values, beta_values, num_heads);
194}
195
197 const float *k,
198 const float *v,
199 const float *g,
200 const float *beta,
201 const float *state_in,
202 float *state_out,
203 float *out,
204 int num_heads,
205 int state_dim,
206 float norm_eps);
207
208#if defined(__AVX2__)
209#if defined(__AVX512F__)
210static inline float ck_deltanet_gcc_reduce_add_ps(__m512 value)
211{
212 const __m256 hi = _mm512_extractf32x8_ps(value, 1);
213 const __m256 lo = _mm512_castps512_ps256(value);
214 const __m256 sum8 = _mm256_add_ps(hi, lo);
215 const __m128 hi4 = _mm256_extractf128_ps(sum8, 1);
216 const __m128 lo4 = _mm256_castps256_ps128(sum8);
217 const __m128 sum4 = _mm_add_ps(hi4, lo4);
218 const __m128 swapped = _mm_shuffle_ps(sum4, sum4, _MM_SHUFFLE(1, 0, 3, 2));
219 const __m128 sum2 = _mm_add_ps(sum4, swapped);
220 return _mm_cvtss_f32(_mm_add_ss(sum2, _mm_shuffle_ps(sum2, sum2, 1)));
221}
222#endif
223
224static CK_DELTANET_NOINLINE float ck_deltanet_llama_avx2_dot(
225 const float *x, const float *y, int n)
226{
227#if defined(__AVX512F__)
228 const int np = n & ~63;
229 __m512 sum[4] = {
230 _mm512_setzero_ps(), _mm512_setzero_ps(),
231 _mm512_setzero_ps(), _mm512_setzero_ps()
232 };
233 for (int i = 0; i < np; i += 64) {
234 for (int j = 0; j < 4; ++j) {
235 const __m512 xv = _mm512_loadu_ps(x + i + j * 16);
236 const __m512 yv = _mm512_loadu_ps(y + i + j * 16);
237 sum[j] = _mm512_fmadd_ps(xv, yv, sum[j]);
238 }
239 }
240 sum[0] = _mm512_add_ps(sum[0], sum[2]);
241 sum[1] = _mm512_add_ps(sum[1], sum[3]);
242 sum[0] = _mm512_add_ps(sum[0], sum[1]);
243 float result = ck_deltanet_gcc_reduce_add_ps(sum[0]);
244#else
245 const int np = n & ~31;
246 __m256 sum[4] = {
247 _mm256_setzero_ps(), _mm256_setzero_ps(),
248 _mm256_setzero_ps(), _mm256_setzero_ps()
249 };
250 for (int i = 0; i < np; i += 32) {
251 for (int j = 0; j < 4; ++j) {
252 const __m256 xv = _mm256_loadu_ps(x + i + j * 8);
253 const __m256 yv = _mm256_loadu_ps(y + i + j * 8);
254#if defined(__FMA__)
255 sum[j] = _mm256_fmadd_ps(xv, yv, sum[j]);
256#else
257 sum[j] = _mm256_add_ps(_mm256_mul_ps(xv, yv), sum[j]);
258#endif
259 }
260 }
261 sum[0] = _mm256_add_ps(sum[0], sum[2]);
262 sum[1] = _mm256_add_ps(sum[1], sum[3]);
263 sum[0] = _mm256_add_ps(sum[0], sum[1]);
264 const __m128 halves = _mm_add_ps(
265 _mm256_castps256_ps128(sum[0]), _mm256_extractf128_ps(sum[0], 1));
266 const __m128 pairs = _mm_hadd_ps(halves, halves);
267 float result = _mm_cvtss_f32(_mm_hadd_ps(pairs, pairs));
268#endif
269 for (int i = np; i < n; ++i) {
270 result += x[i] * y[i];
271 }
272 return result;
273}
274
275static inline float ck_deltanet_llama_scale(int state_dim)
276{
277#if defined(__SSE__)
278 const __m128 dim = _mm_set_ss((float) state_dim);
279 return _mm_cvtss_f32(_mm_div_ss(_mm_set_ss(1.0f), _mm_sqrt_ss(dim)));
280#else
281 return 1.0f / sqrtf((float) state_dim);
282#endif
283}
284
285static inline __m256 ck_deltanet_fmadd8(__m256 a, __m256 b, __m256 acc)
286{
287#if defined(__FMA__)
288 return _mm256_fmadd_ps(a, b, acc);
289#else
290 return _mm256_add_ps(_mm256_mul_ps(a, b), acc);
291#endif
292}
293
294/*
295 * llama.cpp evaluates multi-token scalar-gate DeltaNet in 64-token chunks.
296 * This is algebraically equivalent to the recurrent update above, but its
297 * reduction tree is observably different after many recurrent layers.
298 *
299 * Layouts below are row-major:
300 * token vectors [chunk, state_dim]
301 * recurrent S [key_dim, value_dim]
302 * temporal mats [chunk, chunk]
303 *
304 * The fixed upper bound keeps the provider allocation-free. Qwen3.5/3.6 use
305 * state_dim=128; unsupported shapes retain the sequential fallback.
306 */
307static void gated_deltanet_llama_chunk64_head(
308 const float *q,
309 const float *k,
310 const float *v,
311 const float *g,
312 const float *beta,
313 const float *state_in,
314 float *state_out,
315 float *out,
316 int rows,
317 int num_heads,
318 int group_count,
319 int head,
320 int state_dim)
321{
323 const int group = head % group_count;
324 const size_t qk_row_stride = (size_t)group_count * (size_t)state_dim;
325 const size_t value_row_stride = (size_t)num_heads * (size_t)state_dim;
326 const size_t gate_row_stride = (size_t)num_heads;
327 const size_t state_count = (size_t)state_dim * (size_t)state_dim;
328 const float scale = 1.0f / sqrtf((float)state_dim);
329
330 float gcum[C];
331 float beta_chunk[C];
332 float decay[C * C];
333 float transform[C * C];
334 float q_chunk[C * CK_DELTANET_LLAMA_CHUNK_MAX_DIM];
335 float k_chunk[C * CK_DELTANET_LLAMA_CHUNK_MAX_DIM];
336 float value_beta[C * CK_DELTANET_LLAMA_CHUNK_MAX_DIM];
337 float k_cumdecay[C * CK_DELTANET_LLAMA_CHUNK_MAX_DIM];
338 float v_new[C * CK_DELTANET_LLAMA_CHUNK_MAX_DIM];
339 float matrix_work[C * CK_DELTANET_LLAMA_CHUNK_MAX_DIM];
340 float work_row[C];
341 float gate_exp[C];
342
343 float *state = state_out + (size_t)head * state_count;
344 const float *initial = state_in + (size_t)head * state_count;
345 if (state != initial) {
346 for (size_t i = 0; i < state_count; ++i) {
347 state[i] = initial[i];
348 }
349 }
350
351 for (int chunk_start = 0; chunk_start < rows; chunk_start += C) {
352 const int valid = rows - chunk_start < C ? rows - chunk_start : C;
353
354 for (int i = 0; i < C; ++i) {
355 const int token = chunk_start + i;
356 const int present = i < valid;
357 const float gate_value = present
358 ? g[(size_t)token * gate_row_stride + (size_t)head]
359 : 0.0f;
360 gcum[i] = gate_value + (i ? gcum[i - 1] : 0.0f);
361 gate_exp[i] = expf(gcum[i]);
362
363 float beta_value = 0.0f;
364 const float *q_src = NULL;
365 const float *k_src = NULL;
366 const float *v_src = NULL;
367 if (present) {
368 beta_value = ck_deltanet_sigmoidf(
369 beta[(size_t)token * gate_row_stride + (size_t)head]);
370 q_src = q + (size_t)token * qk_row_stride +
371 (size_t)group * (size_t)state_dim;
372 k_src = k + (size_t)token * qk_row_stride +
373 (size_t)group * (size_t)state_dim;
374 v_src = v + (size_t)token * value_row_stride +
375 (size_t)head * (size_t)state_dim;
376 }
377 beta_chunk[i] = beta_value;
378 for (int d = 0; d < state_dim; ++d) {
379 const size_t offset = (size_t)i * (size_t)state_dim + (size_t)d;
380 q_chunk[offset] = present ? q_src[d] * scale : 0.0f;
381 k_chunk[offset] = present ? k_src[d] : 0.0f;
382 value_beta[offset] = present ? v_src[d] * beta_value : 0.0f;
383 k_cumdecay[offset] =
384 present ? k_src[d] * beta_value * gate_exp[i] : 0.0f;
385 }
386 }
387
388 /*
389 * kb[i,j] = beta_i * <k_i,k_j> * exp(gcum_i-gcum_j).
390 * llama solves (I + tril(kb,-1)) X = -tril(kb,-1), then adds I.
391 * Forward substitution is performed a column at a time to retain the
392 * same dependency order as ggml_solve_tri.
393 */
394 for (int i = 0; i < C; ++i) {
395 for (int j = 0; j < C; ++j) {
396 const float d = j <= i ? expf(gcum[i] - gcum[j]) : 0.0f;
397 decay[(size_t)i * C + (size_t)j] = d;
398 transform[(size_t)i * C + (size_t)j] = j < i
399 ? ck_deltanet_llama_avx2_dot(
400 k_chunk + (size_t)i * (size_t)state_dim,
401 k_chunk + (size_t)j * (size_t)state_dim,
402 state_dim) * beta_chunk[i] * d
403 : 0.0f;
404 }
405 }
406 for (int i = 0; i < C; ++i) {
407 for (int j = 0; j < i; ++j) {
408 work_row[j] = transform[(size_t)i * C + (size_t)j];
409 }
410 for (int col = 0; col <= i; ++col) {
411 const float rhs = i == col ? 1.0f : 0.0f;
412 float solved = rhs;
413 for (int j = 0; j < i; ++j) {
414 solved -= work_row[j] * transform[(size_t)j * C + (size_t)col];
415 }
416 transform[(size_t)i * C + (size_t)col] = solved;
417 }
418 }
419
420 /*
421 * transformed V and cumulative-decay K use the solved temporal
422 * transform. Subtract the contribution of the incoming state to form
423 * V_new, matching llama's v_t_new node.
424 */
425 for (int i = 0; i < C; ++i) {
426 int d = 0;
427 for (; d + 7 < state_dim; d += 8) {
428 __m256 value_sum = _mm256_setzero_ps();
429 __m256 key_sum = _mm256_setzero_ps();
430 for (int j = 0; j < C; ++j) {
431 const __m256 coefficient = _mm256_set1_ps(
432 transform[(size_t)i * C + (size_t)j]);
433 value_sum = ck_deltanet_fmadd8(
434 coefficient,
435 _mm256_loadu_ps(value_beta +
436 (size_t)j * (size_t)state_dim + (size_t)d),
437 value_sum);
438 key_sum = ck_deltanet_fmadd8(
439 coefficient,
440 _mm256_loadu_ps(k_cumdecay +
441 (size_t)j * (size_t)state_dim + (size_t)d),
442 key_sum);
443 }
444 _mm256_storeu_ps(v_new +
445 (size_t)i * (size_t)state_dim + (size_t)d, value_sum);
446 _mm256_storeu_ps(matrix_work +
447 (size_t)i * (size_t)state_dim + (size_t)d, key_sum);
448 }
449 for (; d < state_dim; ++d) {
450 float value_sum = 0.0f;
451 float key_sum = 0.0f;
452 for (int j = 0; j < C; ++j) {
453 const float coefficient = transform[(size_t)i * C + (size_t)j];
454 value_sum += coefficient *
455 value_beta[(size_t)j * (size_t)state_dim + (size_t)d];
456 key_sum += coefficient *
457 k_cumdecay[(size_t)j * (size_t)state_dim + (size_t)d];
458 }
459 v_new[(size_t)i * (size_t)state_dim + (size_t)d] = value_sum;
460 matrix_work[(size_t)i * (size_t)state_dim + (size_t)d] = key_sum;
461 }
462 }
463 for (int i = 0; i < C; ++i) {
464 int d = 0;
465 for (; d + 7 < state_dim; d += 8) {
466 __m256 v_prime = _mm256_setzero_ps();
467 for (int r = 0; r < state_dim; ++r) {
468 v_prime = ck_deltanet_fmadd8(
469 _mm256_set1_ps(matrix_work[
470 (size_t)i * (size_t)state_dim + (size_t)r]),
471 _mm256_loadu_ps(state +
472 (size_t)r * (size_t)state_dim + (size_t)d),
473 v_prime);
474 }
475 float *dst = v_new +
476 (size_t)i * (size_t)state_dim + (size_t)d;
477 _mm256_storeu_ps(dst, _mm256_sub_ps(_mm256_loadu_ps(dst), v_prime));
478 }
479 for (; d < state_dim; ++d) {
480 float v_prime = 0.0f;
481 for (int r = 0; r < state_dim; ++r) {
482 v_prime +=
483 matrix_work[(size_t)i * (size_t)state_dim + (size_t)r] *
484 state[(size_t)r * (size_t)state_dim + (size_t)d];
485 }
486 v_new[(size_t)i * (size_t)state_dim + (size_t)d] -= v_prime;
487 }
488 }
489
490 for (int i = 0; i < valid; ++i) {
491 float *out_token = out +
492 (size_t)(chunk_start + i) * value_row_stride +
493 (size_t)head * (size_t)state_dim;
494 for (int j = 0; j <= i; ++j) {
495 work_row[j] = ck_deltanet_llama_avx2_dot(
496 q_chunk + (size_t)i * (size_t)state_dim,
497 k_chunk + (size_t)j * (size_t)state_dim,
498 state_dim) * decay[(size_t)i * C + (size_t)j];
499 }
500 int d = 0;
501 for (; d + 7 < state_dim; d += 8) {
502 __m256 result = _mm256_setzero_ps();
503 for (int r = 0; r < state_dim; ++r) {
504 const float q_gate =
505 q_chunk[(size_t)i * (size_t)state_dim + (size_t)r] *
506 gate_exp[i];
507 result = ck_deltanet_fmadd8(
508 _mm256_set1_ps(q_gate),
509 _mm256_loadu_ps(state +
510 (size_t)r * (size_t)state_dim + (size_t)d),
511 result);
512 }
513 for (int j = 0; j <= i; ++j) {
514 result = ck_deltanet_fmadd8(
515 _mm256_set1_ps(work_row[j]),
516 _mm256_loadu_ps(v_new +
517 (size_t)j * (size_t)state_dim + (size_t)d),
518 result);
519 }
520 _mm256_storeu_ps(out_token + d, result);
521 }
522 for (; d < state_dim; ++d) {
523 float result = 0.0f;
524 for (int r = 0; r < state_dim; ++r) {
525 result +=
526 q_chunk[(size_t)i * (size_t)state_dim + (size_t)r] *
527 gate_exp[i] *
528 state[(size_t)r * (size_t)state_dim + (size_t)d];
529 }
530 for (int j = 0; j <= i; ++j) {
531 result += work_row[j] *
532 v_new[(size_t)j * (size_t)state_dim + (size_t)d];
533 }
534 out_token[d] = result;
535 }
536 }
537
538 const float last_decay = gate_exp[C - 1];
539 for (int i = 0; i < C; ++i) {
540 gate_exp[i] = expf(gcum[C - 1] - gcum[i]);
541 }
542 for (int r = 0; r < state_dim; ++r) {
543 int d = 0;
544 for (; d + 7 < state_dim; d += 8) {
545 float *state_row = state +
546 (size_t)r * (size_t)state_dim + (size_t)d;
547 __m256 updated = _mm256_mul_ps(
548 _mm256_loadu_ps(state_row), _mm256_set1_ps(last_decay));
549 for (int i = 0; i < C; ++i) {
550 const float key_gate =
551 k_chunk[(size_t)i * (size_t)state_dim + (size_t)r] *
552 gate_exp[i];
553 updated = ck_deltanet_fmadd8(
554 _mm256_set1_ps(key_gate),
555 _mm256_loadu_ps(v_new +
556 (size_t)i * (size_t)state_dim + (size_t)d),
557 updated);
558 }
559 _mm256_storeu_ps(state_row, updated);
560 }
561 for (; d < state_dim; ++d) {
562 float updated =
563 state[(size_t)r * (size_t)state_dim + (size_t)d] * last_decay;
564 for (int i = 0; i < C; ++i) {
565 updated +=
566 k_chunk[(size_t)i * (size_t)state_dim + (size_t)r] *
567 gate_exp[i] *
568 v_new[(size_t)i * (size_t)state_dim + (size_t)d];
569 }
570 state[(size_t)r * (size_t)state_dim + (size_t)d] = updated;
571 }
572 }
573 }
574}
575
576#endif
577
579 const float *q,
580 const float *k,
581 const float *v,
582 const float *g,
583 const float *beta,
584 const float *state_in,
585 float *state_out,
586 float *out,
587 int num_heads,
588 int group_count,
589 int state_dim,
590 float norm_eps,
591 int head_begin,
592 int head_end,
593 int pytorch_bf16_boundaries)
594{
595#if defined(__AVX2__)
596 (void) norm_eps;
597 if (!q || !k || !v || !g || !beta || !state_in || !state_out || !out ||
598 num_heads <= 0 || num_heads > CK_DELTANET_MAX_STACK_DIM ||
599 group_count <= 0 || num_heads % group_count != 0 ||
600 state_dim <= 0 || state_dim > CK_DELTANET_MAX_STACK_DIM ||
601 head_begin < 0 || head_end < head_begin || head_end > num_heads) {
602 return;
603 }
604 const float scale = ck_deltanet_llama_scale(state_dim);
605 const size_t vector_stride = (size_t) state_dim;
606 const size_t state_stride = (size_t) state_dim * (size_t) state_dim;
607 float column[CK_DELTANET_MAX_STACK_DIM];
608 float q_scaled[CK_DELTANET_MAX_STACK_DIM];
609
610 for (int h = head_begin; h < head_end; ++h) {
611 /* llama.cpp ggml_repeat_4d tiles compact Q/K heads (0..G-1 repeated),
612 * while the PyTorch Qwen3-Next reference uses repeat_interleave so
613 * each compact head owns H/G adjacent value heads. These layouts
614 * are distinct numerical contracts even though their buffer shapes
615 * are identical. */
616 const int group = pytorch_bf16_boundaries
617 ? h / (num_heads / group_count)
618 : h % group_count;
619 const float *q_head = q + (size_t) group * vector_stride;
620 const float *k_head = k + (size_t) group * vector_stride;
621 const float *v_head = v + (size_t) h * vector_stride;
622 const float *state_prev = state_in + (size_t) h * state_stride;
623 float *state_cur = state_out + (size_t) h * state_stride;
624 float *out_head = out + (size_t) h * vector_stride;
625 float gate;
626 float beta_s;
627 if (pytorch_bf16_boundaries) {
628 gate = expf(g[h]);
629 beta_s = ck_deltanet_sigmoidf(beta[h]);
630 } else {
632 gate = ck_deltanet_llama_expf(g[h]);
633 beta_s = ck_deltanet_llama_sigmoidf(beta[h]);
634 }
635 if (pytorch_bf16_boundaries) {
636 beta_s = bf16_to_float(float_to_bf16(beta_s));
637 for (int row = 0; row < state_dim; ++row) {
638 q_scaled[row] = q_head[row] * scale;
639 }
640 }
641
642 for (int row = 0; row < state_dim; ++row) {
643 const size_t row_offset = (size_t) row * (size_t) state_dim;
644 for (int col = 0; col < state_dim; ++col) {
645 state_cur[row_offset + (size_t) col] =
646 state_prev[row_offset + (size_t) col] * gate;
647 }
648 }
649
650 for (int col = 0; col < state_dim; ++col) {
651 for (int row = 0; row < state_dim; ++row) {
652 column[row] = state_cur[(size_t) row * (size_t) state_dim + (size_t) col];
653 }
654 const float memory = ck_deltanet_llama_avx2_dot(column, k_head, state_dim);
655 const float delta = (v_head[col] - memory) * beta_s;
656 for (int row = 0; row < state_dim; ++row) {
657 const size_t offset = (size_t) row * (size_t) state_dim + (size_t) col;
658#if defined(__FMA__)
659 const float updated = fmaf(k_head[row], delta, state_cur[offset]);
660#else
661 const float updated = state_cur[offset] + k_head[row] * delta;
662#endif
663 state_cur[offset] = updated;
664 column[row] = updated;
665 }
666 if (pytorch_bf16_boundaries) {
667 out_head[col] =
668 ck_deltanet_llama_avx2_dot(column, q_scaled, state_dim);
669 } else {
670 out_head[col] =
671 ck_deltanet_llama_avx2_dot(column, q_head, state_dim) * scale;
672 }
673 }
674 }
675#else
676 if (group_count != num_heads) {
677 return;
678 }
680 q, k, v, g, beta, state_in, state_out, out,
681 num_heads, state_dim, norm_eps);
682#endif
683}
684
685/*
686 * llama.cpp stores the recurrent matrix transposed so each logical state
687 * column is contiguous. Keeping that physical layout across prefill and
688 * decode removes the gather/scatter copy from every state update while
689 * preserving the same per-column dot and FMA order.
690 */
692 const float *q,
693 const float *k,
694 const float *v,
695 const float *g,
696 const float *beta,
697 const float *state_in,
698 float *state_out,
699 float *out,
700 int num_heads,
701 int group_count,
702 int state_dim,
703 float norm_eps,
704 int head_begin,
705 int head_end)
706{
707#if defined(__AVX2__)
708 (void) norm_eps;
709 if (!q || !k || !v || !g || !beta || !state_in || !state_out || !out ||
710 num_heads <= 0 || group_count <= 0 || num_heads % group_count != 0 ||
711 state_dim <= 0 || state_dim > CK_DELTANET_MAX_STACK_DIM ||
712 head_begin < 0 || head_end < head_begin || head_end > num_heads) {
713 return;
714 }
715 const float scale = ck_deltanet_llama_scale(state_dim);
716 const size_t vector_stride = (size_t) state_dim;
717 const size_t state_stride = (size_t) state_dim * (size_t) state_dim;
718
720 for (int h = head_begin; h < head_end; ++h) {
721 const int group = h % group_count;
722 const float *q_head = q + (size_t) group * vector_stride;
723 const float *k_head = k + (size_t) group * vector_stride;
724 const float *v_head = v + (size_t) h * vector_stride;
725 const float *state_prev = state_in + (size_t) h * state_stride;
726 float *state_cur = state_out + (size_t) h * state_stride;
727 float *out_head = out + (size_t) h * vector_stride;
728 const float gate = ck_deltanet_llama_expf(g[h]);
729 const float beta_s = ck_deltanet_llama_sigmoidf(beta[h]);
730
731 for (int col = 0; col < state_dim; ++col) {
732 const float *prev_col = state_prev + (size_t) col * vector_stride;
733 float *cur_col = state_cur + (size_t) col * vector_stride;
734 int row = 0;
735 const __m256 gate8 = _mm256_set1_ps(gate);
736 for (; row + 7 < state_dim; row += 8) {
737 const __m256 scaled = _mm256_mul_ps(
738 _mm256_loadu_ps(prev_col + row), gate8);
739 _mm256_storeu_ps(cur_col + row, scaled);
740 }
741 for (; row < state_dim; ++row) {
742 cur_col[row] = prev_col[row] * gate;
743 }
744
745 const float memory =
746 ck_deltanet_llama_avx2_dot(cur_col, k_head, state_dim);
747 const float delta = (v_head[col] - memory) * beta_s;
748 row = 0;
749 const __m256 delta8 = _mm256_set1_ps(delta);
750 for (; row + 7 < state_dim; row += 8) {
751 const __m256 updated = _mm256_fmadd_ps(
752 _mm256_loadu_ps(k_head + row), delta8,
753 _mm256_loadu_ps(cur_col + row));
754 _mm256_storeu_ps(cur_col + row, updated);
755 }
756 for (; row < state_dim; ++row) {
757 cur_col[row] = fmaf(k_head[row], delta, cur_col[row]);
758 }
759 out_head[col] =
760 ck_deltanet_llama_avx2_dot(cur_col, q_head, state_dim) * scale;
761 }
762 }
763#else
764 /* Portable scalar path (ARM and other non-AVX2 targets): identical
765 * transposed state layout and update order as the AVX2 path above.
766 * Without this the recurrent core silently no-ops on ARM and qwen35-
767 * class models emit garbage (state never updates, out stays stale). */
768 (void) norm_eps;
769 if (!q || !k || !v || !g || !beta || !state_in || !state_out || !out ||
770 num_heads <= 0 || group_count <= 0 || num_heads % group_count != 0 ||
771 state_dim <= 0 || state_dim > CK_DELTANET_MAX_STACK_DIM ||
772 head_begin < 0 || head_end < head_begin || head_end > num_heads) {
773 return;
774 }
775 /* ck_deltanet_llama_scale() is inside an AVX2-only block; use its
776 * portable non-SSE equivalent here. */
777 const float scale = 1.0f / sqrtf((float) state_dim);
778 const size_t vector_stride = (size_t) state_dim;
779 const size_t state_stride = (size_t) state_dim * (size_t) state_dim;
780
782 for (int h = head_begin; h < head_end; ++h) {
783 const int group = h % group_count;
784 const float *q_head = q + (size_t) group * vector_stride;
785 const float *k_head = k + (size_t) group * vector_stride;
786 const float *v_head = v + (size_t) h * vector_stride;
787 const float *state_prev = state_in + (size_t) h * state_stride;
788 float *state_cur = state_out + (size_t) h * state_stride;
789 float *out_head = out + (size_t) h * vector_stride;
790 const float gate = ck_deltanet_llama_expf(g[h]);
791 const float beta_s = ck_deltanet_llama_sigmoidf(beta[h]);
792
793 for (int col = 0; col < state_dim; ++col) {
794 const float *prev_col = state_prev + (size_t) col * vector_stride;
795 float *cur_col = state_cur + (size_t) col * vector_stride;
796 float memory = 0.0f;
797 float dot = 0.0f;
798 for (int row = 0; row < state_dim; ++row) {
799 cur_col[row] = prev_col[row] * gate;
800 }
801 for (int row = 0; row < state_dim; ++row) {
802 memory += cur_col[row] * k_head[row];
803 }
804 const float delta = (v_head[col] - memory) * beta_s;
805 for (int row = 0; row < state_dim; ++row) {
806 cur_col[row] = fmaf(k_head[row], delta, cur_col[row]);
807 }
808 for (int row = 0; row < state_dim; ++row) {
809 dot += cur_col[row] * q_head[row];
810 }
811 out_head[col] = dot * scale;
812 }
813 }
814#endif
815}
816
818 const float *k,
819 const float *v,
820 const float *g,
821 const float *beta,
822 const float *state_in,
823 float *state_out,
824 float *out,
825 int num_heads,
826 int group_count,
827 int state_dim,
828 float norm_eps)
829{
831 q, k, v, g, beta, state_in, state_out, out,
832 num_heads, group_count, state_dim, norm_eps, 0, num_heads);
833}
834
835/*
836 * Orchestrator-facing range entry point. Heads own disjoint state/output
837 * slices, so a threadpool can partition them without changing any per-head
838 * reduction tree. Keep dispatch out of the numerical kernel itself.
839 */
841 const float *q,
842 const float *k,
843 const float *v,
844 const float *g,
845 const float *beta,
846 const float *state_in,
847 float *state_out,
848 float *out,
849 int num_heads,
850 int group_count,
851 int state_dim,
852 float norm_eps,
853 int head_begin,
854 int head_end)
855{
857 q, k, v, g, beta, state_in, state_out, out,
858 num_heads, group_count, state_dim, norm_eps,
859 head_begin, head_end);
860}
861
862static int ck_deltanet_ceil_log2(int value)
863{
864 int result = 0;
865 int power = 1;
866 while (power < value) {
867 power <<= 1;
868 ++result;
869 }
870 return result;
871}
872
873#if defined(__GNUC__) && !defined(__clang__)
874__attribute__((optimize("fp-contract=off")))
875#endif
876static void ck_deltanet_pytorch_outer_sum(const float *matrix,
877 const float *row_weights,
878 float *output,
879 int state_dim)
880{
881 const int num_levels = 4;
882 int level_power = ck_deltanet_ceil_log2(state_dim) / num_levels;
883 if (level_power < 4) {
884 level_power = 4;
885 }
886 const int level_step = 1 << level_power;
887 const int level_mask = level_step - 1;
888 int col = 0;
889
890#if defined(__AVX512F__)
891 /* PyTorch vectorized_outer_sum reduces four adjacent vectors together. */
892 for (; col + 63 < state_dim; col += 64) {
893 __m512 acc[4][4];
894 for (int level = 0; level < num_levels; ++level) {
895 for (int block = 0; block < 4; ++block) {
896 acc[level][block] = _mm512_setzero_ps();
897 }
898 }
899
900 int i = 0;
901 for (; i + level_step <= state_dim;) {
902 for (int j = 0; j < level_step; ++j, ++i) {
903 const float *row = matrix + (size_t)i * (size_t)state_dim + col;
904 const __m512 weight = _mm512_set1_ps(row_weights[i]);
905 for (int block = 0; block < 4; ++block) {
906 const __m512 product = _mm512_mul_ps(
907 _mm512_loadu_ps(row + block * 16), weight);
908 acc[0][block] = _mm512_add_ps(acc[0][block], product);
909 }
910 }
911
912 for (int level = 1; level < num_levels; ++level) {
913 for (int block = 0; block < 4; ++block) {
914 acc[level][block] = _mm512_add_ps(
915 acc[level][block], acc[level - 1][block]);
916 acc[level - 1][block] = _mm512_setzero_ps();
917 }
918 const int mask = level_mask << (level * level_power);
919 if ((i & mask) != 0) {
920 break;
921 }
922 }
923 }
924
925 for (; i < state_dim; ++i) {
926 const float *row = matrix + (size_t)i * (size_t)state_dim + col;
927 const __m512 weight = _mm512_set1_ps(row_weights[i]);
928 for (int block = 0; block < 4; ++block) {
929 const __m512 product = _mm512_mul_ps(
930 _mm512_loadu_ps(row + block * 16), weight);
931 acc[0][block] = _mm512_add_ps(acc[0][block], product);
932 }
933 }
934
935 for (int level = 1; level < num_levels; ++level) {
936 for (int block = 0; block < 4; ++block) {
937 acc[0][block] = _mm512_add_ps(
938 acc[0][block], acc[level][block]);
939 }
940 }
941 for (int block = 0; block < 4; ++block) {
942 _mm512_storeu_ps(output + col + block * 16, acc[0][block]);
943 }
944 }
945#elif defined(__AVX2__)
946 for (; col + 31 < state_dim; col += 32) {
947 __m256 acc[4][4];
948 for (int level = 0; level < num_levels; ++level) {
949 for (int block = 0; block < 4; ++block) {
950 acc[level][block] = _mm256_setzero_ps();
951 }
952 }
953
954 int i = 0;
955 for (; i + level_step <= state_dim;) {
956 for (int j = 0; j < level_step; ++j, ++i) {
957 const float *row = matrix + (size_t)i * (size_t)state_dim + col;
958 const __m256 weight = _mm256_set1_ps(row_weights[i]);
959 for (int block = 0; block < 4; ++block) {
960 const __m256 product = _mm256_mul_ps(
961 _mm256_loadu_ps(row + block * 8), weight);
962 acc[0][block] = _mm256_add_ps(acc[0][block], product);
963 }
964 }
965 for (int level = 1; level < num_levels; ++level) {
966 for (int block = 0; block < 4; ++block) {
967 acc[level][block] = _mm256_add_ps(
968 acc[level][block], acc[level - 1][block]);
969 acc[level - 1][block] = _mm256_setzero_ps();
970 }
971 const int mask = level_mask << (level * level_power);
972 if ((i & mask) != 0) {
973 break;
974 }
975 }
976 }
977 for (; i < state_dim; ++i) {
978 const float *row = matrix + (size_t)i * (size_t)state_dim + col;
979 const __m256 weight = _mm256_set1_ps(row_weights[i]);
980 for (int block = 0; block < 4; ++block) {
981 const __m256 product = _mm256_mul_ps(
982 _mm256_loadu_ps(row + block * 8), weight);
983 acc[0][block] = _mm256_add_ps(acc[0][block], product);
984 }
985 }
986 for (int level = 1; level < num_levels; ++level) {
987 for (int block = 0; block < 4; ++block) {
988 acc[0][block] = _mm256_add_ps(
989 acc[0][block], acc[level][block]);
990 }
991 }
992 for (int block = 0; block < 4; ++block) {
993 _mm256_storeu_ps(output + col + block * 8, acc[0][block]);
994 }
995 }
996#endif
997
998 /* Production Qwen dimensions are covered above. Keep a deterministic
999 * scalar fallback for uncommon tail widths. */
1000 for (; col < state_dim; ++col) {
1001 float sum = 0.0f;
1002 for (int row = 0; row < state_dim; ++row) {
1003 sum += matrix[(size_t)row * (size_t)state_dim + col] *
1004 row_weights[row];
1005 }
1006 output[col] = sum;
1007 }
1008}
1009
1010#if defined(__GNUC__) && !defined(__clang__)
1011__attribute__((optimize("fp-contract=off")))
1012#endif
1014 const float *q,
1015 const float *k,
1016 const float *v,
1017 const float *g,
1018 const float *beta,
1019 const float *state_in,
1020 float *state_out,
1021 float *out,
1022 float *debug_decayed_state,
1023 float *debug_memory,
1024 float *debug_delta,
1025 int num_heads,
1026 int group_count,
1027 int state_dim,
1028 float norm_eps)
1029{
1030 (void)norm_eps;
1031 if (!q || !k || !v || !g || !beta || !state_in || !state_out || !out ||
1032 num_heads <= 0 || group_count <= 0 || num_heads % group_count != 0 ||
1033 state_dim <= 0 || state_dim > CK_DELTANET_MAX_STACK_DIM) {
1034 return;
1035 }
1036
1037 const size_t vector_stride = (size_t)state_dim;
1038 const size_t state_stride = vector_stride * vector_stride;
1039 const int heads_per_group = num_heads / group_count;
1040 const float sqrt_dim = sqrtf((float)state_dim);
1041 float gate_values[CK_DELTANET_MAX_STACK_DIM];
1042 float beta_values[CK_DELTANET_MAX_STACK_DIM];
1043 float memory[CK_DELTANET_MAX_STACK_DIM];
1044 float delta[CK_DELTANET_MAX_STACK_DIM];
1045 float q_scaled[CK_DELTANET_MAX_STACK_DIM];
1047 g, beta, gate_values, beta_values, num_heads);
1048
1049 for (int h = 0; h < num_heads; ++h) {
1050 const int group = h / heads_per_group;
1051 const float *q_head = q + (size_t)group * vector_stride;
1052 const float *k_head = k + (size_t)group * vector_stride;
1053 const float *v_head = v + (size_t)h * vector_stride;
1054 const float *state_prev = state_in + (size_t)h * state_stride;
1055 float *state_cur = state_out + (size_t)h * state_stride;
1056 float *out_head = out + (size_t)h * vector_stride;
1057
1058 for (int col = 0; col < state_dim; ++col) {
1059 q_scaled[col] = q_head[col] / sqrt_dim;
1060 }
1061
1062 /* Materialize the decayed state before the separately ordered sum. */
1063 for (int row = 0; row < state_dim; ++row) {
1064 const size_t row_offset = (size_t)row * vector_stride;
1065 int col = 0;
1066#if defined(__AVX2__)
1067 const __m256 gate8 = _mm256_set1_ps(gate_values[h]);
1068 for (; col + 7 < state_dim; col += 8) {
1069 const __m256 state = _mm256_mul_ps(
1070 _mm256_loadu_ps(state_prev + row_offset + (size_t)col),
1071 gate8);
1072 _mm256_storeu_ps(state_cur + row_offset + (size_t)col, state);
1073 }
1074#endif
1075 for (; col < state_dim; ++col) {
1076 const size_t offset = row_offset + (size_t)col;
1077 const float state = state_prev[offset] * gate_values[h];
1078 state_cur[offset] = state;
1079 }
1080 }
1081
1083 state_cur, k_head, memory, state_dim);
1084
1085 if (debug_decayed_state) {
1086 memcpy(
1087 debug_decayed_state + (size_t)h * state_stride,
1088 state_cur,
1089 state_stride * sizeof(float));
1090 }
1091 if (debug_memory) {
1092 memcpy(
1093 debug_memory + (size_t)h * vector_stride,
1094 memory,
1095 vector_stride * sizeof(float));
1096 }
1097
1098 for (int col = 0; col < state_dim; ++col) {
1099 delta[col] = (v_head[col] - memory[col]) * beta_values[h];
1100 }
1101 if (debug_delta) {
1102 memcpy(
1103 debug_delta + (size_t)h * vector_stride,
1104 delta,
1105 vector_stride * sizeof(float));
1106 }
1107
1108 for (int row = 0; row < state_dim; ++row) {
1109 const size_t row_offset = (size_t)row * vector_stride;
1110 const float key = k_head[row];
1111 int col = 0;
1112#if defined(__AVX2__)
1113 const __m256 key8 = _mm256_set1_ps(key);
1114 for (; col + 7 < state_dim; col += 8) {
1115 const __m256 update = _mm256_mul_ps(
1116 key8, _mm256_loadu_ps(delta + col));
1117 const __m256 state = _mm256_add_ps(
1118 _mm256_loadu_ps(state_cur + row_offset + (size_t)col),
1119 update);
1120 _mm256_storeu_ps(state_cur + row_offset + (size_t)col, state);
1121 }
1122#endif
1123 for (; col < state_dim; ++col) {
1124 const size_t offset = row_offset + (size_t)col;
1125 const float state = state_cur[offset] + key * delta[col];
1126 state_cur[offset] = state;
1127 }
1128 }
1129
1131 state_cur, q_scaled, out_head, state_dim);
1132
1133 for (int col = 0; col < state_dim; ++col) {
1134 out_head[col] = bf16_to_float(float_to_bf16(out_head[col]));
1135 }
1136 }
1137}
1138
1140 const float *k,
1141 const float *v,
1142 const float *g,
1143 const float *beta,
1144 const float *state_in,
1145 float *state_out,
1146 float *out,
1147 int num_heads,
1148 int group_count,
1149 int state_dim,
1150 float norm_eps)
1151{
1153 q, k, v, g, beta, state_in, state_out, out,
1154 NULL, NULL, NULL,
1155 num_heads, group_count, state_dim, norm_eps);
1156}
1157
1159 const float *q,
1160 const float *k,
1161 const float *v,
1162 const float *g,
1163 const float *beta,
1164 const float *state_in,
1165 float *state_out,
1166 float *out,
1167 float *decayed_state,
1168 float *memory,
1169 float *delta,
1170 int num_heads,
1171 int group_count,
1172 int state_dim,
1173 float norm_eps)
1174{
1176 q, k, v, g, beta, state_in, state_out, out,
1177 decayed_state, memory, delta,
1178 num_heads, group_count, state_dim, norm_eps);
1179}
1180
1182 const float *k,
1183 const float *v,
1184 const float *g,
1185 const float *beta,
1186 const float *state_in,
1187 float *state_out,
1188 float *out,
1189 int rows,
1190 int num_heads,
1191 int group_count,
1192 int state_dim,
1193 float norm_eps)
1194{
1195 if (!q || !k || !v || !g || !beta || !state_in || !state_out || !out ||
1196 rows <= 0 || num_heads <= 0 || group_count <= 0 ||
1197 num_heads % group_count != 0 || state_dim <= 0) {
1198 return;
1199 }
1200 const size_t qk_stride = (size_t) group_count * (size_t) state_dim;
1201 const size_t value_stride = (size_t) num_heads * (size_t) state_dim;
1202 const size_t gate_stride = (size_t) num_heads;
1203 for (int row = 0; row < rows; ++row) {
1205 q + (size_t) row * qk_stride,
1206 k + (size_t) row * qk_stride,
1207 v + (size_t) row * value_stride,
1208 g + (size_t) row * gate_stride,
1209 beta + (size_t) row * gate_stride,
1210 row == 0 ? state_in : state_out,
1211 state_out,
1212 out + (size_t) row * value_stride,
1213 num_heads, group_count, state_dim, norm_eps);
1214 }
1215}
1216
1218 const float *k,
1219 const float *v,
1220 const float *g,
1221 const float *beta,
1222 const float *state_in,
1223 float *state_out,
1224 float *out,
1225 int rows,
1226 int num_heads,
1227 int group_count,
1228 int state_dim,
1229 float norm_eps)
1230{
1231 if (!q || !k || !v || !g || !beta || !state_in || !state_out || !out ||
1232 rows <= 0 || num_heads <= 0 || group_count <= 0 ||
1233 num_heads % group_count != 0 || state_dim <= 0) {
1234 return;
1235 }
1236#if defined(__AVX2__)
1237 (void)norm_eps;
1238 if (state_dim <= CK_DELTANET_LLAMA_CHUNK_MAX_DIM) {
1239 for (int head = 0; head < num_heads; ++head) {
1240 gated_deltanet_llama_chunk64_head(
1241 q, k, v, g, beta, state_in, state_out, out,
1242 rows, num_heads, group_count, head, state_dim);
1243 }
1244 return;
1245 }
1246#endif
1248 q, k, v, g, beta, state_in, state_out, out,
1249 rows, num_heads, group_count, state_dim, norm_eps);
1250}
1251
1253 const float *k,
1254 const float *v,
1255 const float *g,
1256 const float *beta,
1257 const float *state_in,
1258 float *state_out,
1259 float *out,
1260 int rows,
1261 int num_heads,
1262 int group_count,
1263 int head,
1264 int state_dim)
1265{
1266#if defined(__AVX2__)
1267 if (!q || !k || !v || !g || !beta || !state_in || !state_out || !out ||
1268 rows <= 0 || num_heads <= 0 || group_count <= 0 ||
1269 num_heads % group_count != 0 || head < 0 || head >= num_heads ||
1270 state_dim <= 0 || state_dim > CK_DELTANET_LLAMA_CHUNK_MAX_DIM) {
1271 return;
1272 }
1273 gated_deltanet_llama_chunk64_head(
1274 q, k, v, g, beta, state_in, state_out, out,
1275 rows, num_heads, group_count, head, state_dim);
1276#else
1277 (void)q;
1278 (void)k;
1279 (void)v;
1280 (void)g;
1281 (void)beta;
1282 (void)state_in;
1283 (void)state_out;
1284 (void)out;
1285 (void)rows;
1286 (void)num_heads;
1287 (void)group_count;
1288 (void)head;
1289 (void)state_dim;
1290#endif
1291}
1292
1294 const float *q,
1295 const float *k,
1296 const float *v,
1297 const float *g,
1298 const float *beta,
1299 const float *state_in,
1300 float *state_out,
1301 float *out,
1302 int rows,
1303 int num_heads,
1304 int group_count,
1305 int state_dim,
1306 float norm_eps)
1307{
1308 if (!q || !k || !v || !g || !beta || !state_in || !state_out || !out ||
1309 rows <= 0 || num_heads <= 0 || group_count <= 0 ||
1310 num_heads % group_count != 0 || state_dim <= 0) {
1311 return;
1312 }
1313 const size_t qk_stride = (size_t)group_count * (size_t)state_dim;
1314 const size_t value_stride = (size_t)num_heads * (size_t)state_dim;
1315 const size_t gate_stride = (size_t)num_heads;
1316 for (int row = 0; row < rows; ++row) {
1318 q + (size_t)row * qk_stride,
1319 k + (size_t)row * qk_stride,
1320 v + (size_t)row * value_stride,
1321 g + (size_t)row * gate_stride,
1322 beta + (size_t)row * gate_stride,
1323 row == 0 ? state_in : state_out,
1324 state_out,
1325 out + (size_t)row * value_stride,
1326 num_heads, group_count, state_dim, norm_eps);
1327 }
1328}
1329
1331 const float *k,
1332 const float *v,
1333 const float *g,
1334 const float *beta,
1335 const float *state_in,
1336 float *state_out,
1337 float *out,
1338 int num_heads,
1339 int state_dim,
1340 float norm_eps)
1341{
1342 const float q_scale = 1.0f / sqrtf((float)state_dim);
1343 const size_t vec_stride = (size_t)state_dim;
1344 const size_t state_stride = (size_t)state_dim * (size_t)state_dim;
1345
1346 for (int h = 0; h < num_heads; ++h) {
1347 const float *q_head = q + (size_t)h * vec_stride;
1348 const float *k_head = k + (size_t)h * vec_stride;
1349 const float *v_head = v + (size_t)h * vec_stride;
1350 const float *state_prev = state_in + (size_t)h * state_stride;
1351 float *state_cur = state_out + (size_t)h * state_stride;
1352 float *out_head = out + (size_t)h * vec_stride;
1353
1354 const float beta_s = ck_deltanet_sigmoidf(beta[h]);
1355 const float gate = expf(g[h]);
1356
1357 for (int row = 0; row < state_dim; ++row) {
1358 const size_t row_off = (size_t)row * (size_t)state_dim;
1359 for (int col = 0; col < state_dim; ++col) {
1360 state_cur[row_off + (size_t)col] = state_prev[row_off + (size_t)col] * gate;
1361 }
1362 }
1363
1364 for (int col = 0; col < state_dim; ++col) {
1365 float kv_mem = 0.0f;
1366 for (int row = 0; row < state_dim; ++row) {
1367 const float k_hat = k_head[row];
1368 kv_mem += state_cur[(size_t)row * (size_t)state_dim + (size_t)col] * k_hat;
1369 }
1370
1371 const float delta = (v_head[col] - kv_mem) * beta_s;
1372 for (int row = 0; row < state_dim; ++row) {
1373 const float k_hat = k_head[row];
1374 state_cur[(size_t)row * (size_t)state_dim + (size_t)col] += k_hat * delta;
1375 }
1376 }
1377
1378 for (int col = 0; col < state_dim; ++col) {
1379 float acc = 0.0f;
1380 for (int row = 0; row < state_dim; ++row) {
1381 const float q_hat = q_head[row] * q_scale;
1382 acc += state_cur[(size_t)row * (size_t)state_dim + (size_t)col] * q_hat;
1383 }
1384 out_head[col] = acc;
1385 }
1386 }
1387}
1388
1390 const float *d_state_out,
1391 const float *q,
1392 const float *k,
1393 const float *v,
1394 const float *g,
1395 const float *beta,
1396 const float *state_in,
1397 const float *state_out,
1398 float *d_q,
1399 float *d_k,
1400 float *d_v,
1401 float *d_g,
1402 float *d_beta,
1403 float *d_state_in,
1404 int num_heads,
1405 int state_dim,
1406 float norm_eps)
1407{
1408 const float q_scale = 1.0f / sqrtf((float)state_dim);
1409 const size_t vec_stride = (size_t)state_dim;
1410 const size_t state_stride = (size_t)state_dim * (size_t)state_dim;
1411
1412 float q_hat[CK_DELTANET_MAX_STACK_DIM];
1413 float k_hat[CK_DELTANET_MAX_STACK_DIM];
1414 float kv_mem[CK_DELTANET_MAX_STACK_DIM];
1415 float delta[CK_DELTANET_MAX_STACK_DIM];
1416 float d_q_hat[CK_DELTANET_MAX_STACK_DIM];
1417 float d_k_hat[CK_DELTANET_MAX_STACK_DIM];
1418 float d_mem[CK_DELTANET_MAX_STACK_DIM];
1419
1420 for (int h = 0; h < num_heads; ++h) {
1421 const float *d_out_head = d_out + (size_t)h * vec_stride;
1422 const float *d_state_out_head = d_state_out + (size_t)h * state_stride;
1423 const float *q_head = q + (size_t)h * vec_stride;
1424 const float *k_head = k + (size_t)h * vec_stride;
1425 const float *v_head = v + (size_t)h * vec_stride;
1426 const float *state_prev = state_in + (size_t)h * state_stride;
1427 const float *state_cur = state_out + (size_t)h * state_stride;
1428 float *d_q_head = d_q + (size_t)h * vec_stride;
1429 float *d_k_head = d_k + (size_t)h * vec_stride;
1430 float *d_v_head = d_v + (size_t)h * vec_stride;
1431 float *d_state_prev = d_state_in + (size_t)h * state_stride;
1432
1433 const float beta_s = ck_deltanet_sigmoidf(beta[h]);
1434 const float gate = expf(g[h]);
1435
1436 float qk_dot = 0.0f;
1437 float out_delta_dot = 0.0f;
1438 float beta_acc = 0.0f;
1439 float gate_acc = 0.0f;
1440
1441 for (int i = 0; i < state_dim; ++i) {
1442 q_hat[i] = q_head[i] * q_scale;
1443 k_hat[i] = k_head[i];
1444 kv_mem[i] = 0.0f;
1445 d_q_hat[i] = 0.0f;
1446 d_k_hat[i] = 0.0f;
1447 d_mem[i] = 0.0f;
1448 d_v_head[i] = 0.0f;
1449 qk_dot += q_hat[i] * k_hat[i];
1450 }
1451
1452 for (int col = 0; col < state_dim; ++col) {
1453 float mem = 0.0f;
1454 for (int row = 0; row < state_dim; ++row) {
1455 mem += (state_prev[(size_t)row * (size_t)state_dim + (size_t)col] * gate) * k_hat[row];
1456 }
1457 kv_mem[col] = mem;
1458 delta[col] = (v_head[col] - mem) * beta_s;
1459 out_delta_dot += d_out_head[col] * delta[col];
1460 }
1461
1462 for (int row = 0; row < state_dim; ++row) {
1463 const size_t row_off = (size_t)row * (size_t)state_dim;
1464 float dq_acc = 0.0f;
1465 float dk_acc = q_hat[row] * out_delta_dot;
1466 for (int col = 0; col < state_dim; ++col) {
1467 const float d_state_direct = d_state_out_head[row_off + (size_t)col];
1468 dq_acc += state_cur[row_off + (size_t)col] * d_out_head[col];
1469 dk_acc += d_state_direct * delta[col];
1470 }
1471 d_q_hat[row] = dq_acc;
1472 d_k_hat[row] = dk_acc;
1473 }
1474
1475 for (int col = 0; col < state_dim; ++col) {
1476 float d_delta_acc = d_out_head[col] * qk_dot;
1477 for (int row = 0; row < state_dim; ++row) {
1478 d_delta_acc += d_state_out_head[(size_t)row * (size_t)state_dim + (size_t)col] * k_hat[row];
1479 }
1480
1481 d_v_head[col] = beta_s * d_delta_acc;
1482 d_mem[col] = -beta_s * d_delta_acc;
1483 beta_acc += d_delta_acc * (v_head[col] - kv_mem[col]);
1484 }
1485
1486 for (int row = 0; row < state_dim; ++row) {
1487 const size_t row_off = (size_t)row * (size_t)state_dim;
1488 float s_dm_acc = 0.0f;
1489 for (int col = 0; col < state_dim; ++col) {
1490 s_dm_acc += (state_prev[row_off + (size_t)col] * gate) * d_mem[col];
1491 }
1492 d_k_hat[row] += s_dm_acc;
1493 }
1494
1495 for (int row = 0; row < state_dim; ++row) {
1496 const size_t row_off = (size_t)row * (size_t)state_dim;
1497 for (int col = 0; col < state_dim; ++col) {
1498 const float d_state_total = d_state_out_head[row_off + (size_t)col]
1499 + q_hat[row] * d_out_head[col]
1500 + k_hat[row] * d_mem[col];
1501 d_state_prev[row_off + (size_t)col] = gate * d_state_total;
1502 gate_acc += d_state_total * state_prev[row_off + (size_t)col];
1503 }
1504 }
1505
1506 for (int i = 0; i < state_dim; ++i) {
1507 d_q_head[i] = d_q_hat[i] * q_scale;
1508 d_k_head[i] = d_k_hat[i];
1509 }
1510
1511 d_g[h] = gate_acc * gate;
1512 d_beta[h] = beta_acc * beta_s * (1.0f - beta_s);
1513 }
1514}
1515
1516#if defined(__AVX__)
1517static void ck_deltanet_scale_rows_avx(const float *src, float *dst, int dim, float scale)
1518{
1519 const __m256 scale_v = _mm256_set1_ps(scale);
1520 int i = 0;
1521 for (; i + 8 <= dim; i += 8) {
1522 __m256 x = _mm256_loadu_ps(src + i);
1523 _mm256_storeu_ps(dst + i, _mm256_mul_ps(x, scale_v));
1524 }
1525 for (; i < dim; ++i) {
1526 dst[i] = src[i] * scale;
1527 }
1528}
1529
1530void gated_deltanet_autoregressive_forward_avx(const float *q,
1531 const float *k,
1532 const float *v,
1533 const float *g,
1534 const float *beta,
1535 const float *state_in,
1536 float *state_out,
1537 float *out,
1538 int num_heads,
1539 int state_dim,
1540 float norm_eps)
1541{
1542 const float q_scale = 1.0f / sqrtf((float)state_dim);
1543 const size_t vec_stride = (size_t)state_dim;
1544 const size_t state_stride = (size_t)state_dim * (size_t)state_dim;
1545
1546 float q_hat[CK_DELTANET_MAX_STACK_DIM];
1547 float k_hat[CK_DELTANET_MAX_STACK_DIM];
1548 float kv_mem[CK_DELTANET_MAX_STACK_DIM];
1549 float delta[CK_DELTANET_MAX_STACK_DIM];
1550
1551 for (int h = 0; h < num_heads; ++h) {
1552 const float *q_head = q + (size_t)h * vec_stride;
1553 const float *k_head = k + (size_t)h * vec_stride;
1554 const float *v_head = v + (size_t)h * vec_stride;
1555 const float *state_prev = state_in + (size_t)h * state_stride;
1556 float *state_cur = state_out + (size_t)h * state_stride;
1557 float *out_head = out + (size_t)h * vec_stride;
1558
1559 const float gate = expf(g[h]);
1560 const float beta_s = ck_deltanet_sigmoidf(beta[h]);
1561
1562 /* q and k arrive pre-normalized by recurrent_qk_l2_norm. */
1563 ck_deltanet_scale_rows_avx(q_head, q_hat, state_dim, q_scale);
1564 ck_deltanet_scale_rows_avx(k_head, k_hat, state_dim, 1.0f);
1565
1566 const __m256 beta_v = _mm256_set1_ps(beta_s);
1567 const __m256 zero_v = _mm256_setzero_ps();
1568
1569 int col = 0;
1570 for (; col + 8 <= state_dim; col += 8) {
1571 _mm256_storeu_ps(kv_mem + col, zero_v);
1572 _mm256_storeu_ps(out_head + col, zero_v);
1573 }
1574 for (; col < state_dim; ++col) {
1575 kv_mem[col] = 0.0f;
1576 out_head[col] = 0.0f;
1577 }
1578
1579 for (int row = 0; row < state_dim; ++row) {
1580 const size_t row_off = (size_t)row * (size_t)state_dim;
1581 const __m256 k_hat_v = _mm256_set1_ps(k_hat[row]);
1582 const __m256 gate_v = _mm256_set1_ps(gate);
1583
1584 col = 0;
1585 for (; col + 8 <= state_dim; col += 8) {
1586 __m256 prev_v = _mm256_loadu_ps(state_prev + row_off + (size_t)col);
1587 __m256 cur_v = _mm256_mul_ps(prev_v, gate_v);
1588 __m256 kv_v = _mm256_loadu_ps(kv_mem + col);
1589 kv_v = _mm256_add_ps(kv_v, _mm256_mul_ps(cur_v, k_hat_v));
1590 _mm256_storeu_ps(state_cur + row_off + (size_t)col, cur_v);
1591 _mm256_storeu_ps(kv_mem + col, kv_v);
1592 }
1593 for (; col < state_dim; ++col) {
1594 const float cur = state_prev[row_off + (size_t)col] * gate;
1595 state_cur[row_off + (size_t)col] = cur;
1596 kv_mem[col] += cur * k_hat[row];
1597 }
1598 }
1599
1600 col = 0;
1601 for (; col + 8 <= state_dim; col += 8) {
1602 __m256 v_v = _mm256_loadu_ps(v_head + col);
1603 __m256 kv_v = _mm256_loadu_ps(kv_mem + col);
1604 __m256 delta_v = _mm256_mul_ps(_mm256_sub_ps(v_v, kv_v), beta_v);
1605 _mm256_storeu_ps(delta + col, delta_v);
1606 }
1607 for (; col < state_dim; ++col) {
1608 delta[col] = (v_head[col] - kv_mem[col]) * beta_s;
1609 }
1610
1611 for (int row = 0; row < state_dim; ++row) {
1612 const size_t row_off = (size_t)row * (size_t)state_dim;
1613 const __m256 k_hat_v = _mm256_set1_ps(k_hat[row]);
1614 const __m256 q_hat_v = _mm256_set1_ps(q_hat[row]);
1615
1616 col = 0;
1617 for (; col + 8 <= state_dim; col += 8) {
1618 __m256 cur_v = _mm256_loadu_ps(state_cur + row_off + (size_t)col);
1619 __m256 delta_v = _mm256_loadu_ps(delta + col);
1620 __m256 out_v = _mm256_loadu_ps(out_head + col);
1621 __m256 updated_v = _mm256_add_ps(cur_v, _mm256_mul_ps(k_hat_v, delta_v));
1622 out_v = _mm256_add_ps(out_v, _mm256_mul_ps(updated_v, q_hat_v));
1623 _mm256_storeu_ps(state_cur + row_off + (size_t)col, updated_v);
1624 _mm256_storeu_ps(out_head + col, out_v);
1625 }
1626 for (; col < state_dim; ++col) {
1627 const float updated = state_cur[row_off + (size_t)col] + k_hat[row] * delta[col];
1628 state_cur[row_off + (size_t)col] = updated;
1629 out_head[col] += updated * q_hat[row];
1630 }
1631 }
1632 }
1633}
1634#endif
1635
1636#if defined(__AVX2__)
1637static inline __m256 ck_deltanet_fmadd256(__m256 a, __m256 b, __m256 c)
1638{
1639#if defined(__FMA__)
1640 return _mm256_fmadd_ps(a, b, c);
1641#else
1642 return _mm256_add_ps(_mm256_mul_ps(a, b), c);
1643#endif
1644}
1645
1646void gated_deltanet_autoregressive_forward_avx2(const float *q,
1647 const float *k,
1648 const float *v,
1649 const float *g,
1650 const float *beta,
1651 const float *state_in,
1652 float *state_out,
1653 float *out,
1654 int num_heads,
1655 int state_dim,
1656 float norm_eps)
1657{
1658 const float q_scale = 1.0f / sqrtf((float)state_dim);
1659 const size_t vec_stride = (size_t)state_dim;
1660 const size_t state_stride = (size_t)state_dim * (size_t)state_dim;
1661
1662 float q_hat[CK_DELTANET_MAX_STACK_DIM];
1663 float k_hat[CK_DELTANET_MAX_STACK_DIM];
1664 float kv_mem[CK_DELTANET_MAX_STACK_DIM];
1665 float delta[CK_DELTANET_MAX_STACK_DIM];
1666
1667 for (int h = 0; h < num_heads; ++h) {
1668 const float *q_head = q + (size_t)h * vec_stride;
1669 const float *k_head = k + (size_t)h * vec_stride;
1670 const float *v_head = v + (size_t)h * vec_stride;
1671 const float *state_prev = state_in + (size_t)h * state_stride;
1672 float *state_cur = state_out + (size_t)h * state_stride;
1673 float *out_head = out + (size_t)h * vec_stride;
1674
1675 const float gate = expf(g[h]);
1676 const float beta_s = ck_deltanet_sigmoidf(beta[h]);
1677
1678 /* q and k arrive pre-normalized by recurrent_qk_l2_norm. */
1679 ck_deltanet_scale_rows_avx(q_head, q_hat, state_dim, q_scale);
1680 ck_deltanet_scale_rows_avx(k_head, k_hat, state_dim, 1.0f);
1681
1682 const __m256 beta_v = _mm256_set1_ps(beta_s);
1683 const __m256 zero_v = _mm256_setzero_ps();
1684
1685 int col = 0;
1686 for (; col + 8 <= state_dim; col += 8) {
1687 _mm256_storeu_ps(kv_mem + col, zero_v);
1688 _mm256_storeu_ps(out_head + col, zero_v);
1689 }
1690 for (; col < state_dim; ++col) {
1691 kv_mem[col] = 0.0f;
1692 out_head[col] = 0.0f;
1693 }
1694
1695 int row = 0;
1696 for (; row + 2 <= state_dim; row += 2) {
1697 const size_t row0_off = (size_t)row * (size_t)state_dim;
1698 const size_t row1_off = (size_t)(row + 1) * (size_t)state_dim;
1699 const __m256 k0_v = _mm256_set1_ps(k_hat[row]);
1700 const __m256 k1_v = _mm256_set1_ps(k_hat[row + 1]);
1701 const __m256 gate0_v = _mm256_set1_ps(gate);
1702 const __m256 gate1_v = _mm256_set1_ps(gate);
1703
1704 col = 0;
1705 for (; col + 8 <= state_dim; col += 8) {
1706 __m256 prev0_v = _mm256_loadu_ps(state_prev + row0_off + (size_t)col);
1707 __m256 prev1_v = _mm256_loadu_ps(state_prev + row1_off + (size_t)col);
1708 __m256 cur0_v = _mm256_mul_ps(prev0_v, gate0_v);
1709 __m256 cur1_v = _mm256_mul_ps(prev1_v, gate1_v);
1710 __m256 kv_v = _mm256_loadu_ps(kv_mem + col);
1711 kv_v = ck_deltanet_fmadd256(cur0_v, k0_v, kv_v);
1712 kv_v = ck_deltanet_fmadd256(cur1_v, k1_v, kv_v);
1713 _mm256_storeu_ps(state_cur + row0_off + (size_t)col, cur0_v);
1714 _mm256_storeu_ps(state_cur + row1_off + (size_t)col, cur1_v);
1715 _mm256_storeu_ps(kv_mem + col, kv_v);
1716 }
1717 for (; col < state_dim; ++col) {
1718 const float cur0 = state_prev[row0_off + (size_t)col] * gate;
1719 const float cur1 = state_prev[row1_off + (size_t)col] * gate;
1720 state_cur[row0_off + (size_t)col] = cur0;
1721 state_cur[row1_off + (size_t)col] = cur1;
1722 kv_mem[col] += cur0 * k_hat[row] + cur1 * k_hat[row + 1];
1723 }
1724 }
1725 for (; row < state_dim; ++row) {
1726 const size_t row_off = (size_t)row * (size_t)state_dim;
1727 const __m256 k_hat_v = _mm256_set1_ps(k_hat[row]);
1728 const __m256 gate_v = _mm256_set1_ps(gate);
1729 col = 0;
1730 for (; col + 8 <= state_dim; col += 8) {
1731 __m256 prev_v = _mm256_loadu_ps(state_prev + row_off + (size_t)col);
1732 __m256 cur_v = _mm256_mul_ps(prev_v, gate_v);
1733 __m256 kv_v = _mm256_loadu_ps(kv_mem + col);
1734 kv_v = ck_deltanet_fmadd256(cur_v, k_hat_v, kv_v);
1735 _mm256_storeu_ps(state_cur + row_off + (size_t)col, cur_v);
1736 _mm256_storeu_ps(kv_mem + col, kv_v);
1737 }
1738 for (; col < state_dim; ++col) {
1739 const float cur = state_prev[row_off + (size_t)col] * gate;
1740 state_cur[row_off + (size_t)col] = cur;
1741 kv_mem[col] += cur * k_hat[row];
1742 }
1743 }
1744
1745 col = 0;
1746 for (; col + 8 <= state_dim; col += 8) {
1747 __m256 v_v = _mm256_loadu_ps(v_head + col);
1748 __m256 kv_v = _mm256_loadu_ps(kv_mem + col);
1749 __m256 delta_v = _mm256_mul_ps(_mm256_sub_ps(v_v, kv_v), beta_v);
1750 _mm256_storeu_ps(delta + col, delta_v);
1751 }
1752 for (; col < state_dim; ++col) {
1753 delta[col] = (v_head[col] - kv_mem[col]) * beta_s;
1754 }
1755
1756 row = 0;
1757 for (; row + 2 <= state_dim; row += 2) {
1758 const size_t row0_off = (size_t)row * (size_t)state_dim;
1759 const size_t row1_off = (size_t)(row + 1) * (size_t)state_dim;
1760 const __m256 k0_v = _mm256_set1_ps(k_hat[row]);
1761 const __m256 k1_v = _mm256_set1_ps(k_hat[row + 1]);
1762 const __m256 q0_v = _mm256_set1_ps(q_hat[row]);
1763 const __m256 q1_v = _mm256_set1_ps(q_hat[row + 1]);
1764
1765 col = 0;
1766 for (; col + 8 <= state_dim; col += 8) {
1767 __m256 cur0_v = _mm256_loadu_ps(state_cur + row0_off + (size_t)col);
1768 __m256 cur1_v = _mm256_loadu_ps(state_cur + row1_off + (size_t)col);
1769 __m256 delta_v = _mm256_loadu_ps(delta + col);
1770 __m256 out_v = _mm256_loadu_ps(out_head + col);
1771 __m256 upd0_v = ck_deltanet_fmadd256(k0_v, delta_v, cur0_v);
1772 __m256 upd1_v = ck_deltanet_fmadd256(k1_v, delta_v, cur1_v);
1773 out_v = ck_deltanet_fmadd256(upd0_v, q0_v, out_v);
1774 out_v = ck_deltanet_fmadd256(upd1_v, q1_v, out_v);
1775 _mm256_storeu_ps(state_cur + row0_off + (size_t)col, upd0_v);
1776 _mm256_storeu_ps(state_cur + row1_off + (size_t)col, upd1_v);
1777 _mm256_storeu_ps(out_head + col, out_v);
1778 }
1779 for (; col < state_dim; ++col) {
1780 const float upd0 = state_cur[row0_off + (size_t)col] + k_hat[row] * delta[col];
1781 const float upd1 = state_cur[row1_off + (size_t)col] + k_hat[row + 1] * delta[col];
1782 state_cur[row0_off + (size_t)col] = upd0;
1783 state_cur[row1_off + (size_t)col] = upd1;
1784 out_head[col] += upd0 * q_hat[row] + upd1 * q_hat[row + 1];
1785 }
1786 }
1787 for (; row < state_dim; ++row) {
1788 const size_t row_off = (size_t)row * (size_t)state_dim;
1789 const __m256 k_hat_v = _mm256_set1_ps(k_hat[row]);
1790 const __m256 q_hat_v = _mm256_set1_ps(q_hat[row]);
1791 col = 0;
1792 for (; col + 8 <= state_dim; col += 8) {
1793 __m256 cur_v = _mm256_loadu_ps(state_cur + row_off + (size_t)col);
1794 __m256 delta_v = _mm256_loadu_ps(delta + col);
1795 __m256 out_v = _mm256_loadu_ps(out_head + col);
1796 __m256 updated_v = ck_deltanet_fmadd256(k_hat_v, delta_v, cur_v);
1797 out_v = ck_deltanet_fmadd256(updated_v, q_hat_v, out_v);
1798 _mm256_storeu_ps(state_cur + row_off + (size_t)col, updated_v);
1799 _mm256_storeu_ps(out_head + col, out_v);
1800 }
1801 for (; col < state_dim; ++col) {
1802 const float updated = state_cur[row_off + (size_t)col] + k_hat[row] * delta[col];
1803 state_cur[row_off + (size_t)col] = updated;
1804 out_head[col] += updated * q_hat[row];
1805 }
1806 }
1807 }
1808}
1809#endif
1810
1811#if defined(__AVX512F__)
1812static inline __m512 ck_deltanet_madd512(__m512 a, __m512 b, __m512 c)
1813{
1814 return _mm512_add_ps(_mm512_mul_ps(a, b), c);
1815}
1816
1817static void ck_deltanet_scale_rows_avx512(const float *src, float *dst, int dim, float scale)
1818{
1819 const __m512 scale_v = _mm512_set1_ps(scale);
1820 int i = 0;
1821 for (; i + 16 <= dim; i += 16) {
1822 __m512 x = _mm512_loadu_ps(src + i);
1823 _mm512_storeu_ps(dst + i, _mm512_mul_ps(x, scale_v));
1824 }
1825 for (; i < dim; ++i) {
1826 dst[i] = src[i] * scale;
1827 }
1828}
1829
1830void gated_deltanet_autoregressive_forward_avx512(const float *q,
1831 const float *k,
1832 const float *v,
1833 const float *g,
1834 const float *beta,
1835 const float *state_in,
1836 float *state_out,
1837 float *out,
1838 int num_heads,
1839 int state_dim,
1840 float norm_eps)
1841{
1842 const float q_scale = 1.0f / sqrtf((float)state_dim);
1843 const size_t vec_stride = (size_t)state_dim;
1844 const size_t state_stride = (size_t)state_dim * (size_t)state_dim;
1845
1846 float q_hat[CK_DELTANET_MAX_STACK_DIM];
1847 float k_hat[CK_DELTANET_MAX_STACK_DIM];
1848 float kv_mem[CK_DELTANET_MAX_STACK_DIM];
1849 float delta[CK_DELTANET_MAX_STACK_DIM];
1850
1851 for (int h = 0; h < num_heads; ++h) {
1852 const float *q_head = q + (size_t)h * vec_stride;
1853 const float *k_head = k + (size_t)h * vec_stride;
1854 const float *v_head = v + (size_t)h * vec_stride;
1855 const float *state_prev = state_in + (size_t)h * state_stride;
1856 float *state_cur = state_out + (size_t)h * state_stride;
1857 float *out_head = out + (size_t)h * vec_stride;
1858
1859 const float gate = expf(g[h]);
1860 const float beta_s = ck_deltanet_sigmoidf(beta[h]);
1861
1862 /* q and k arrive pre-normalized by recurrent_qk_l2_norm. */
1863 ck_deltanet_scale_rows_avx512(q_head, q_hat, state_dim, q_scale);
1864 ck_deltanet_scale_rows_avx512(k_head, k_hat, state_dim, 1.0f);
1865
1866 const __m512 beta_v = _mm512_set1_ps(beta_s);
1867 const __m512 zero_v = _mm512_setzero_ps();
1868
1869 int col = 0;
1870 for (; col + 16 <= state_dim; col += 16) {
1871 _mm512_storeu_ps(kv_mem + col, zero_v);
1872 _mm512_storeu_ps(out_head + col, zero_v);
1873 }
1874 for (; col < state_dim; ++col) {
1875 kv_mem[col] = 0.0f;
1876 out_head[col] = 0.0f;
1877 }
1878
1879 for (int row = 0; row < state_dim; ++row) {
1880 const size_t row_off = (size_t)row * (size_t)state_dim;
1881 const __m512 k_hat_v = _mm512_set1_ps(k_hat[row]);
1882 const __m512 gate_v = _mm512_set1_ps(gate);
1883 col = 0;
1884 for (; col + 16 <= state_dim; col += 16) {
1885 __m512 prev_v = _mm512_loadu_ps(state_prev + row_off + (size_t)col);
1886 __m512 cur_v = _mm512_mul_ps(prev_v, gate_v);
1887 __m512 kv_v = _mm512_loadu_ps(kv_mem + col);
1888 kv_v = ck_deltanet_madd512(cur_v, k_hat_v, kv_v);
1889 _mm512_storeu_ps(state_cur + row_off + (size_t)col, cur_v);
1890 _mm512_storeu_ps(kv_mem + col, kv_v);
1891 }
1892 for (; col < state_dim; ++col) {
1893 const float cur = state_prev[row_off + (size_t)col] * gate;
1894 state_cur[row_off + (size_t)col] = cur;
1895 kv_mem[col] += cur * k_hat[row];
1896 }
1897 }
1898
1899 col = 0;
1900 for (; col + 16 <= state_dim; col += 16) {
1901 __m512 v_v = _mm512_loadu_ps(v_head + col);
1902 __m512 kv_v = _mm512_loadu_ps(kv_mem + col);
1903 __m512 delta_v = _mm512_mul_ps(_mm512_sub_ps(v_v, kv_v), beta_v);
1904 _mm512_storeu_ps(delta + col, delta_v);
1905 }
1906 for (; col < state_dim; ++col) {
1907 delta[col] = (v_head[col] - kv_mem[col]) * beta_s;
1908 }
1909
1910 for (int row = 0; row < state_dim; ++row) {
1911 const size_t row_off = (size_t)row * (size_t)state_dim;
1912 const __m512 k_hat_v = _mm512_set1_ps(k_hat[row]);
1913 const __m512 q_hat_v = _mm512_set1_ps(q_hat[row]);
1914 col = 0;
1915 for (; col + 16 <= state_dim; col += 16) {
1916 __m512 cur_v = _mm512_loadu_ps(state_cur + row_off + (size_t)col);
1917 __m512 delta_v = _mm512_loadu_ps(delta + col);
1918 __m512 out_v = _mm512_loadu_ps(out_head + col);
1919 __m512 updated_v = ck_deltanet_madd512(k_hat_v, delta_v, cur_v);
1920 out_v = ck_deltanet_madd512(updated_v, q_hat_v, out_v);
1921 _mm512_storeu_ps(state_cur + row_off + (size_t)col, updated_v);
1922 _mm512_storeu_ps(out_head + col, out_v);
1923 }
1924 for (; col < state_dim; ++col) {
1925 const float updated = state_cur[row_off + (size_t)col] + k_hat[row] * delta[col];
1926 state_cur[row_off + (size_t)col] = updated;
1927 out_head[col] += updated * q_hat[row];
1928 }
1929 }
1930 }
1931}
1932#endif
1933
1935{
1936 const char *env = getenv("CK_DELTANET_FORCE_REF");
1937 return env && atoi(env) != 0;
1938}
1939
1941{
1943 return "REF";
1944 }
1945#if defined(__AVX512F__)
1946 return "AVX512";
1947#elif defined(__AVX2__)
1948 return "AVX2";
1949#elif defined(__AVX__)
1950 return "AVX";
1951#else
1952 return "REF";
1953#endif
1954}
1955
1957 const float *k,
1958 const float *v,
1959 const float *g,
1960 const float *beta,
1961 const float *state_in,
1962 float *state_out,
1963 float *out,
1964 int num_heads,
1965 int state_dim,
1966 float norm_eps)
1967{
1968 if (!q || !k || !v || !g || !beta || !state_in || !state_out || !out) {
1969 return;
1970 }
1971 if (num_heads <= 0 || state_dim <= 0) {
1972 return;
1973 }
1974
1975 /*
1976 * q and k arrive pre-normalized by recurrent_qk_l2_norm, so the
1977 * ISA-specialized kernels can follow the same contract as the scalar ref.
1978 */
1981 q, k, v, g, beta, state_in, state_out, out, num_heads, state_dim, norm_eps);
1982 return;
1983 }
1984#if defined(__AVX512F__)
1985 gated_deltanet_autoregressive_forward_avx512(
1986 q, k, v, g, beta, state_in, state_out, out, num_heads, state_dim, norm_eps);
1987#elif defined(__AVX2__)
1988 gated_deltanet_autoregressive_forward_avx2(
1989 q, k, v, g, beta, state_in, state_out, out, num_heads, state_dim, norm_eps);
1990#elif defined(__AVX__)
1991 gated_deltanet_autoregressive_forward_avx(
1992 q, k, v, g, beta, state_in, state_out, out, num_heads, state_dim, norm_eps);
1993#else
1995 q, k, v, g, beta, state_in, state_out, out, num_heads, state_dim, norm_eps);
1996#endif
1997}
1998
2000 const float *k,
2001 const float *v,
2002 const float *g,
2003 const float *beta,
2004 const float *state_in,
2005 float *state_out,
2006 float *out,
2007 int rows,
2008 int num_heads,
2009 int state_dim,
2010 float norm_eps)
2011{
2012 if (!q || !k || !v || !g || !beta || !state_in || !state_out || !out) {
2013 return;
2014 }
2015 if (rows <= 0 || num_heads <= 0 || state_dim <= 0) {
2016 return;
2017 }
2018
2019 const size_t vector_stride = (size_t)num_heads * (size_t)state_dim;
2020 const size_t gate_stride = (size_t)num_heads;
2021 for (int row = 0; row < rows; ++row) {
2022 const float *row_state_in = row == 0 ? state_in : state_out;
2024 q + (size_t)row * vector_stride,
2025 k + (size_t)row * vector_stride,
2026 v + (size_t)row * vector_stride,
2027 g + (size_t)row * gate_stride,
2028 beta + (size_t)row * gate_stride,
2029 row_state_in,
2030 state_out,
2031 out + (size_t)row * vector_stride,
2032 num_heads,
2033 state_dim,
2034 norm_eps);
2035 }
2036}
2037
2039 const float *d_state_out,
2040 const float *q,
2041 const float *k,
2042 const float *v,
2043 const float *g,
2044 const float *beta,
2045 const float *state_in,
2046 const float *state_out,
2047 float *d_q,
2048 float *d_k,
2049 float *d_v,
2050 float *d_g,
2051 float *d_beta,
2052 float *d_state_in,
2053 int num_heads,
2054 int state_dim,
2055 float norm_eps)
2056{
2057 if (!d_out || !d_state_out || !q || !k || !v || !g || !beta || !state_in || !state_out ||
2058 !d_q || !d_k || !d_v || !d_g || !d_beta || !d_state_in) {
2059 return;
2060 }
2061 if (num_heads <= 0 || state_dim <= 0 || state_dim > CK_DELTANET_MAX_STACK_DIM) {
2062 return;
2063 }
2064
2066 d_out,
2067 d_state_out,
2068 q,
2069 k,
2070 v,
2071 g,
2072 beta,
2073 state_in,
2074 state_out,
2075 d_q,
2076 d_k,
2077 d_v,
2078 d_g,
2079 d_beta,
2080 d_state_in,
2081 num_heads,
2082 state_dim,
2083 norm_eps);
2084}
#define RTLD_DEFAULT
static int product(size_t a, size_t b, size_t *out)
static uint16_t float_to_bf16(float f)
Definition bf16_utils.h:90
static float bf16_to_float(uint16_t v)
Definition bf16_utils.h:38
int ck_strict_parity_enabled(void)
#define CK_DELTANET_LLAMA_CHUNK_SIZE
#define CK_DELTANET_NOINLINE
void gated_deltanet_autoregressive_backward(const float *d_out, const float *d_state_out, const float *q, const float *k, const float *v, const float *g, const float *beta, const float *state_in, const float *state_out, float *d_q, float *d_k, float *d_v, float *d_g, float *d_beta, float *d_state_in, int num_heads, int state_dim, float norm_eps)
static ck_deltanet_mkl_vsexp_fn ck_deltanet_pytorch_vsexp
static pthread_once_t ck_deltanet_libm_once
void gated_deltanet_pytorch_gate_values_debug(const float *g, const float *beta, float *gate_values, float *beta_values, int num_heads)
static void ck_deltanet_pytorch_outer_sum(const float *matrix, const float *row_weights, float *output, int state_dim)
void gated_deltanet_pytorch_grouped_bf16_forward_debug(const float *q, const float *k, const float *v, const float *g, const float *beta, const float *state_in, float *state_out, float *out, float *decayed_state, float *memory, float *delta, int num_heads, int group_count, int state_dim, float norm_eps)
static void ck_bind_deltanet_pytorch_primitives(void)
void gated_deltanet_autoregressive_forward_ref(const float *q, const float *k, const float *v, const float *g, const float *beta, const float *state_in, float *state_out, float *out, int num_heads, int state_dim, float norm_eps)
static pthread_once_t ck_deltanet_pytorch_primitives_once
static int ck_deltanet_ceil_log2(int value)
static void gated_deltanet_pytorch_grouped_bf16_forward_impl(const float *q, const float *k, const float *v, const float *g, const float *beta, const float *state_in, float *state_out, float *out, float *debug_decayed_state, float *debug_memory, float *debug_delta, int num_heads, int group_count, int state_dim, float norm_eps)
void gated_deltanet_llama_avx2_prefill_forward(const float *q, const float *k, const float *v, const float *g, const float *beta, const float *state_in, float *state_out, float *out, int rows, int num_heads, int group_count, int state_dim, float norm_eps)
static ck_deltanet_libm_f32_fn ck_deltanet_llama_expf
static void gated_deltanet_llama_avx2_grouped_forward_transposed_impl(const float *q, const float *k, const float *v, const float *g, const float *beta, const float *state_in, float *state_out, float *out, int num_heads, int group_count, int state_dim, float norm_eps, int head_begin, int head_end)
void(* ck_deltanet_mkl_vsexp_fn)(int, const float *, float *)
void gated_deltanet_llama_avx2_forward_head_range(const float *q, const float *k, const float *v, const float *g, const float *beta, const float *state_in, float *state_out, float *out, int num_heads, int group_count, int state_dim, float norm_eps, int head_begin, int head_end)
static void * ck_deltanet_mkl_handle
void gated_deltanet_llama_avx2_forward(const float *q, const float *k, const float *v, const float *g, const float *beta, const float *state_in, float *state_out, float *out, int num_heads, int group_count, int state_dim, float norm_eps)
#define CK_DELTANET_LLAMA_CHUNK_MAX_DIM
static void * ck_deltanet_libm_handle
static void gated_deltanet_llama_avx2_grouped_forward_impl(const float *q, const float *k, const float *v, const float *g, const float *beta, const float *state_in, float *state_out, float *out, int num_heads, int group_count, int state_dim, float norm_eps, int head_begin, int head_end, int pytorch_bf16_boundaries)
static void ck_deltanet_pytorch_gate_values(const float *g, const float *beta, float *gate_values, float *beta_values, int num_heads)
static float ck_deltanet_sigmoidf(float x)
void gated_deltanet_pytorch_grouped_bf16_forward(const float *q, const float *k, const float *v, const float *g, const float *beta, const float *state_in, float *state_out, float *out, int num_heads, int group_count, int state_dim, float norm_eps)
void gated_deltanet_pytorch_grouped_bf16_prefill_forward(const float *q, const float *k, const float *v, const float *g, const float *beta, const float *state_in, float *state_out, float *out, int rows, int num_heads, int group_count, int state_dim, float norm_eps)
void gated_deltanet_autoregressive_forward(const float *q, const float *k, const float *v, const float *g, const float *beta, const float *state_in, float *state_out, float *out, int num_heads, int state_dim, float norm_eps)
void gated_deltanet_llama_chunk64_head_forward(const float *q, const float *k, const float *v, const float *g, const float *beta, const float *state_in, float *state_out, float *out, int rows, int num_heads, int group_count, int head, int state_dim)
#define CK_DELTANET_MAX_STACK_DIM
void gated_deltanet_autoregressive_backward_ref(const float *d_out, const float *d_state_out, const float *q, const float *k, const float *v, const float *g, const float *beta, const float *state_in, const float *state_out, float *d_q, float *d_k, float *d_v, float *d_g, float *d_beta, float *d_state_in, int num_heads, int state_dim, float norm_eps)
static void ck_bind_deltanet_llama_libm(void)
static float ck_deltanet_llama_sigmoidf(float x)
void gated_deltanet_prefill_forward(const float *q, const float *k, const float *v, const float *g, const float *beta, const float *state_in, float *state_out, float *out, int rows, int num_heads, int state_dim, float norm_eps)
static int ck_deltanet_force_ref(void)
void gated_deltanet_llama_chunk64_prefill_forward(const float *q, const float *k, const float *v, const float *g, const float *beta, const float *state_in, float *state_out, float *out, int rows, int num_heads, int group_count, int state_dim, float norm_eps)
const char * gated_deltanet_impl_name(void)
float(* ck_deltanet_libm_f32_fn)(float)
#define C(color)
Definition show_config.c:39
int32_t int32_t int32_t int32_t int32_t mask
Definition tokenizer.h:234
__attribute__((visibility("default"))) CKTokenizer *ck_tokenizer_create(CKTokenizerType type)
const char * token
Definition tokenizer.h:307