← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
topk_kernels.c File Reference

Top-K selection kernels for MoE router dispatch. More...

#include <stdint.h>
#include <stddef.h>
#include <float.h>
#include <math.h>
#include "bf16_utils.h"

Go to the source code of this file.

Functions

int argmax_f32 (const float *scores, int n)
 Find index of maximum value.
 
static double ck_moe_llama_softmax_row (float *probabilities, const float *logits, int n_experts, float max_value)
 
static void ck_topk_insert_desc (int idx, float val, int *indices, float *values, int k)
 
static void group_limited_topk_router_f32_impl (const float *scores, const float *correction_bias, int *indices, float *weights, int rows, int n_experts, int top_k, int n_group, int topk_group, int norm_topk_prob, float routed_scaling_factor, int apply_sigmoid)
 
void group_limited_topk_router_sigmoid_f32 (const float *logits, const float *correction_bias, int *indices, float *weights, int rows, int n_experts, int top_k, int n_group, int topk_group, int norm_topk_prob, float routed_scaling_factor)
 
int moe_softmax_topk_router_llama_f32_workspace (const float *logits, int *indices, float *weights, int rows, int n_experts, int top_k, float routed_scaling_factor, void *workspace, size_t workspace_bytes)
 
int moe_softmax_topk_router_pytorch_bf16_workspace (const float *logits, int *indices, float *weights, int rows, int n_experts, int top_k, float routed_scaling_factor, void *workspace, size_t workspace_bytes)
 
size_t moe_softmax_topk_router_workspace_bytes (int n_experts)
 
void nemotron_group_limited_topk_router_f32 (const float *scores, const float *correction_bias, int *indices, float *weights, int rows, int n_experts, int top_k, int n_group, int topk_group, int norm_topk_prob, float routed_scaling_factor)
 
void speculative_commit_one_i32 (int accepted, int verified_token, int *token_buffer, int *token_count, int max_tokens, int *target_position, int *draft_position, int *accepted_count, int *rejected_count)
 Commit one verified speculative token and update decode counters.
 
void speculative_verify_greedy_f32 (const float *target_logits, int vocab_size, int draft_token, int *accepted, int *verified_token)
 Greedy one-token speculative verification.
 
void topk_batched_f32 (const float *scores, int num_tokens, int n_experts, int k, int *indices, float *weights)
 Batched top-K selection for multiple tokens.
 
void topk_f32 (const float *scores, int n, int k, int *indices, float *values)
 Find top-K indices and values from a score vector.
 
void topk_softmax_backward_f32 (const int *indices, const float *weights, const float *d_weights, float *d_scores, int num_tokens, int n_experts_or_keys, int k)
 Backward for hard top-k followed by softmax over selected values.
 
void topk_softmax_f32 (const float *scores, int n, int k, int *indices, float *weights)
 Find top-K indices with softmax-normalized weights.
 

Detailed Description

Top-K selection kernels for MoE router dispatch.

CK-ENGINE KERNEL RULES:

  1. NO malloc/free - memory via bump allocator, pointers passed in
  2. NO OpenMP - parallelization at orchestrator/codegen layer
  3. API must define: inputs, outputs, workspace, and memory layouts
  4. Pure computation - deterministic, no side effects

After changes: make test && make llamacpp-parity-full

Provides efficient top-K selection from a score vector. Used in Mixture-of-Experts models to select which experts process each token.

Operations:

  • topk_f32: Find top-K indices and values from N scores
  • topk_softmax_f32: Top-K with softmax normalization of selected scores

Definition in file topk_kernels.c.

Function Documentation

◆ argmax_f32()

int argmax_f32 ( const float *  scores,
int  n 
)

Find index of maximum value.

Parameters
scoresInput scores [n]
nNumber of scores
Returns
Index of maximum value

Definition at line 500 of file topk_kernels.c.

501{
502 if (!scores || n <= 0) {
503 return -1;
504 }
505
506 int max_idx = 0;
507 float max_val = scores[0];
508
509#ifdef __AVX512F__
510 /* AVX-512 vectorized argmax for large arrays */
511 if (n >= 16) {
512 __m512 vmax = _mm512_set1_ps(-FLT_MAX);
513 __m512i vidx = _mm512_setzero_si512();
514 __m512i vcur_max_idx = _mm512_setzero_si512();
515
516 int i = 0;
517 for (; i + 16 <= n; i += 16) {
518 __m512 v = _mm512_loadu_ps(&scores[i]);
519 __m512i cur_idx = _mm512_add_epi32(
520 _mm512_set1_epi32(i),
521 _mm512_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
522 );
523
524 __mmask16 gt_mask = _mm512_cmp_ps_mask(v, vmax, _CMP_GT_OQ);
525 vmax = _mm512_mask_blend_ps(gt_mask, vmax, v);
526 vcur_max_idx = _mm512_mask_blend_epi32(gt_mask, vcur_max_idx, cur_idx);
527 }
528
529 /* Horizontal reduction */
530 float vals[16];
531 int idxs[16];
532 _mm512_storeu_ps(vals, vmax);
533 _mm512_storeu_si512(idxs, vcur_max_idx);
534
535 max_val = vals[0];
536 max_idx = idxs[0];
537 for (int j = 1; j < 16; j++) {
538 if (vals[j] > max_val) {
539 max_val = vals[j];
540 max_idx = idxs[j];
541 }
542 }
543
544 /* Handle remainder */
545 for (; i < n; i++) {
546 if (scores[i] > max_val) {
547 max_val = scores[i];
548 max_idx = i;
549 }
550 }
551
552 return max_idx;
553 }
554#endif
555
556 /* Scalar fallback */
557 for (int i = 1; i < n; i++) {
558 if (scores[i] > max_val) {
559 max_val = scores[i];
560 max_idx = i;
561 }
562 }
563
564 return max_idx;
565}

Referenced by speculative_verify_greedy_f32().

◆ ck_moe_llama_softmax_row()

static double ck_moe_llama_softmax_row ( float *  probabilities,
const float *  logits,
int  n_experts,
float  max_value 
)
static

Definition at line 282 of file topk_kernels.c.

286{
287 double sum = 0.0;
288 int expert = 0;
289#if defined(__AVX512F__) && defined(__AVX512DQ__)
290 for (; expert + 15 < n_experts; expert += 16) {
291 const __m512 value = ck_moe_ggml_expf512(_mm512_sub_ps(
292 _mm512_loadu_ps(logits + expert), _mm512_set1_ps(max_value)));
293 _mm512_storeu_ps(probabilities + expert, value);
294 sum += (double)_mm512_reduce_add_ps(value);
295 }
296#elif defined(__AVX2__) && defined(__FMA__)
297 for (; expert + 7 < n_experts; expert += 8) {
298 const __m256 value = ck_moe_ggml_expf256(_mm256_sub_ps(
299 _mm256_loadu_ps(logits + expert), _mm256_set1_ps(max_value)));
300 _mm256_storeu_ps(probabilities + expert, value);
301 __m128 half = _mm_add_ps(
302 _mm256_extractf128_ps(value, 1), _mm256_castps256_ps128(value));
303 half = _mm_add_ps(half, _mm_movehl_ps(half, half));
304 half = _mm_add_ss(half, _mm_movehdup_ps(half));
305 sum += (double)_mm_cvtss_f32(half);
306 }
307#endif
308 for (; expert < n_experts; ++expert) {
309 const float value = expf(logits[expert] - max_value);
310 probabilities[expert] = value;
311 sum += (double)value;
312 }
313 return sum;
314}

Referenced by moe_softmax_topk_router_llama_f32_workspace().

◆ ck_topk_insert_desc()

static void ck_topk_insert_desc ( int  idx,
float  val,
int *  indices,
float *  values,
int  k 
)
static

Definition at line 668 of file topk_kernels.c.

669{
670 for (int pos = 0; pos < k; ++pos) {
671 if (indices[pos] < 0 || val > values[pos] || (val == values[pos] && idx < indices[pos])) {
672 for (int j = k - 1; j > pos; --j) {
673 indices[j] = indices[j - 1];
674 values[j] = values[j - 1];
675 }
676 indices[pos] = idx;
677 values[pos] = val;
678 return;
679 }
680 }
681}

Referenced by group_limited_topk_router_f32_impl().

◆ group_limited_topk_router_f32_impl()

static void group_limited_topk_router_f32_impl ( const float *  scores,
const float *  correction_bias,
int *  indices,
float *  weights,
int  rows,
int  n_experts,
int  top_k,
int  n_group,
int  topk_group,
int  norm_topk_prob,
float  routed_scaling_factor,
int  apply_sigmoid 
)
static

Definition at line 683 of file topk_kernels.c.

695{
696 if (!scores || !indices || !weights || rows <= 0 || n_experts <= 0 ||
697 top_k <= 0 || n_group <= 0 || topk_group <= 0) {
698 return;
699 }
700 if (top_k > n_experts) top_k = n_experts;
701 if (n_group > n_experts) n_group = n_experts;
702 if (topk_group > n_group) topk_group = n_group;
703 const int experts_per_group = n_experts / n_group;
704 if (experts_per_group <= 0 || experts_per_group * n_group != n_experts) {
705 return;
706 }
707
708 for (int r = 0; r < rows; ++r) {
709 const float *row_probs = scores + (size_t)r * (size_t)n_experts;
710 float row_scores[n_experts];
711 for (int e = 0; e < n_experts; ++e) {
712 row_scores[e] = apply_sigmoid
713 ? (1.0f / (1.0f + expf(-row_probs[e])))
714 : row_probs[e];
715 }
716 int *row_indices = indices + (size_t)r * (size_t)top_k;
717 float *row_weights = weights + (size_t)r * (size_t)top_k;
718
719 int selected_groups[topk_group];
720 float selected_group_scores[topk_group];
721 for (int i = 0; i < topk_group; ++i) {
722 selected_groups[i] = -1;
723 selected_group_scores[i] = -FLT_MAX;
724 }
725
726 for (int g = 0; g < n_group; ++g) {
727 float best0 = -FLT_MAX;
728 float best1 = -FLT_MAX;
729 const int start = g * experts_per_group;
730 for (int j = 0; j < experts_per_group; ++j) {
731 const int e = start + j;
732 const float v = row_scores[e] + (correction_bias ? correction_bias[e] : 0.0f);
733 if (v > best0) {
734 best1 = best0;
735 best0 = v;
736 } else if (v > best1) {
737 best1 = v;
738 }
739 }
740 const float group_score = best0 + ((experts_per_group >= 2) ? best1 : 0.0f);
741 ck_topk_insert_desc(g, group_score, selected_groups, selected_group_scores, topk_group);
742 }
743
744 int out_idx[top_k];
745 float out_choice[top_k];
746 for (int i = 0; i < top_k; ++i) {
747 out_idx[i] = -1;
748 out_choice[i] = -FLT_MAX;
749 }
750
751 for (int sg = 0; sg < topk_group; ++sg) {
752 const int g = selected_groups[sg];
753 if (g < 0) continue;
754 const int start = g * experts_per_group;
755 for (int j = 0; j < experts_per_group; ++j) {
756 const int e = start + j;
757 const float v = row_scores[e] + (correction_bias ? correction_bias[e] : 0.0f);
758 ck_topk_insert_desc(e, v, out_idx, out_choice, top_k);
759 }
760 }
761
762 float denom = 1.0e-20f;
763 for (int i = 0; i < top_k; ++i) {
764 const int e = out_idx[i];
765 const float w = (e >= 0 && e < n_experts) ? row_scores[e] : 0.0f;
766 row_indices[i] = e;
767 row_weights[i] = w;
768 denom += w;
769 }
770 for (int i = 0; i < top_k; ++i) {
771 float w = row_weights[i];
772 if (norm_topk_prob) {
773 w /= denom;
774 }
775 row_weights[i] = w * routed_scaling_factor;
776 }
777 }
778}
static void ck_topk_insert_desc(int idx, float val, int *indices, float *values, int k)
uint32_t start
Definition utf8.c:214

References ck_topk_insert_desc(), and start.

Referenced by group_limited_topk_router_sigmoid_f32(), and nemotron_group_limited_topk_router_f32().

◆ group_limited_topk_router_sigmoid_f32()

void group_limited_topk_router_sigmoid_f32 ( const float *  logits,
const float *  correction_bias,
int *  indices,
float *  weights,
int  rows,
int  n_experts,
int  top_k,
int  n_group,
int  topk_group,
int  norm_topk_prob,
float  routed_scaling_factor 
)

Definition at line 798 of file topk_kernels.c.

809{
811 logits, correction_bias, indices, weights, rows, n_experts, top_k,
812 n_group, topk_group, norm_topk_prob, routed_scaling_factor, 1
813 );
814}
static void group_limited_topk_router_f32_impl(const float *scores, const float *correction_bias, int *indices, float *weights, int rows, int n_experts, int top_k, int n_group, int topk_group, int norm_topk_prob, float routed_scaling_factor, int apply_sigmoid)

References group_limited_topk_router_f32_impl().

◆ moe_softmax_topk_router_llama_f32_workspace()

int moe_softmax_topk_router_llama_f32_workspace ( const float *  logits,
int *  indices,
float *  weights,
int  rows,
int  n_experts,
int  top_k,
float  routed_scaling_factor,
void *  workspace,
size_t  workspace_bytes 
)

Definition at line 316 of file topk_kernels.c.

326{
327 const size_t required = moe_softmax_topk_router_workspace_bytes(n_experts);
328 if (!logits || !indices || !weights || !workspace || rows <= 0 ||
329 n_experts <= 0 || top_k <= 0 || top_k > n_experts ||
330 !isfinite(routed_scaling_factor) || required == 0 ||
331 workspace_bytes < required) {
332 return -1;
333 }
334
335 float *probabilities = (float *)workspace;
336 for (int row = 0; row < rows; ++row) {
337 const float *row_logits = logits + (size_t)row * (size_t)n_experts;
338 int *row_indices = indices + (size_t)row * (size_t)top_k;
339 float *row_weights = weights + (size_t)row * (size_t)top_k;
340 float max_value = -INFINITY;
341 for (int expert = 0; expert < n_experts; ++expert) {
342 if (!isfinite(row_logits[expert])) {
343 return -2;
344 }
345 if (row_logits[expert] > max_value) {
346 max_value = row_logits[expert];
347 }
348 }
349
350 const double softmax_sum = ck_moe_llama_softmax_row(
351 probabilities, row_logits, n_experts, max_value);
352 const float inverse_softmax_sum = (float)(1.0 / softmax_sum);
353 for (int expert = 0; expert < n_experts; ++expert) {
354 probabilities[expert] *= inverse_softmax_sum;
355 }
356
357 topk_f32(probabilities, n_experts, top_k, row_indices, NULL);
358 double selected_sum_f64 = 0.0;
359 for (int slot = 0; slot < top_k; ++slot) {
360 row_weights[slot] = probabilities[row_indices[slot]];
361 selected_sum_f64 += (double)row_weights[slot];
362 }
363 float selected_sum = (float)selected_sum_f64;
364 if (selected_sum < 6.103515625e-5f) {
365 selected_sum = 6.103515625e-5f;
366 }
367 for (int slot = 0; slot < top_k; ++slot) {
368 row_weights[slot] =
369 (row_weights[slot] / selected_sum) * routed_scaling_factor;
370 }
371 }
372 return 0;
373}
static double ck_moe_llama_softmax_row(float *probabilities, const float *logits, int n_experts, float max_value)
size_t moe_softmax_topk_router_workspace_bytes(int n_experts)
void topk_f32(const float *scores, int n, int k, int *indices, float *values)
Find top-K indices and values from a score vector.

References ck_moe_llama_softmax_row(), moe_softmax_topk_router_workspace_bytes(), and topk_f32().

Referenced by moe_softmax_topk_router_pytorch_bf16_workspace().

◆ moe_softmax_topk_router_pytorch_bf16_workspace()

int moe_softmax_topk_router_pytorch_bf16_workspace ( const float *  logits,
int *  indices,
float *  weights,
int  rows,
int  n_experts,
int  top_k,
float  routed_scaling_factor,
void *  workspace,
size_t  workspace_bytes 
)

Definition at line 375 of file topk_kernels.c.

385{
387 logits, indices, weights, rows, n_experts, top_k,
388 routed_scaling_factor, workspace, workspace_bytes);
389 if (status != 0) {
390 return status;
391 }
392 for (size_t index = 0; index < (size_t)rows * (size_t)top_k; ++index) {
393 weights[index] = bf16_to_float(float_to_bf16(weights[index]));
394 }
395 return 0;
396}
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 moe_softmax_topk_router_llama_f32_workspace(const float *logits, int *indices, float *weights, int rows, int n_experts, int top_k, float routed_scaling_factor, void *workspace, size_t workspace_bytes)

References bf16_to_float(), float_to_bf16(), and moe_softmax_topk_router_llama_f32_workspace().

◆ moe_softmax_topk_router_workspace_bytes()

size_t moe_softmax_topk_router_workspace_bytes ( int  n_experts)

Definition at line 177 of file topk_kernels.c.

178{
179 if (n_experts <= 0) {
180 return 0;
181 }
182 return ((size_t)n_experts * sizeof(float) + 63u) & ~(size_t)63u;
183}

Referenced by moe_softmax_topk_router_llama_f32_workspace().

◆ nemotron_group_limited_topk_router_f32()

void nemotron_group_limited_topk_router_f32 ( const float *  scores,
const float *  correction_bias,
int *  indices,
float *  weights,
int  rows,
int  n_experts,
int  top_k,
int  n_group,
int  topk_group,
int  norm_topk_prob,
float  routed_scaling_factor 
)

Definition at line 780 of file topk_kernels.c.

791{
793 scores, correction_bias, indices, weights, rows, n_experts, top_k,
794 n_group, topk_group, norm_topk_prob, routed_scaling_factor, 0
795 );
796}

References group_limited_topk_router_f32_impl().

◆ speculative_commit_one_i32()

void speculative_commit_one_i32 ( int  accepted,
int  verified_token,
int *  token_buffer,
int *  token_count,
int  max_tokens,
int *  target_position,
int *  draft_position,
int *  accepted_count,
int *  rejected_count 
)

Commit one verified speculative token and update decode counters.

This is the minimal state transition for the first Gemma4 assistant bridge: greedy, one draft token, target remains authoritative. For this milestone the draft cache is kept synchronized with the target position after each token. Multi-token speculative decoding can later replace this with prefix accept and partial draft-cache rollback.

Definition at line 611 of file topk_kernels.c.

620{
621 int next_count = token_count ? *token_count : 0;
622 if (token_buffer && token_count && next_count >= 0 && next_count < max_tokens) {
623 token_buffer[next_count] = verified_token;
624 next_count += 1;
625 *token_count = next_count;
626 }
627
628 if (target_position) {
629 *target_position += 1;
630 if (draft_position) {
631 *draft_position = *target_position;
632 }
633 } else if (draft_position) {
634 *draft_position += 1;
635 }
636
637 if (accepted) {
638 if (accepted_count) {
639 *accepted_count += 1;
640 }
641 } else {
642 if (rejected_count) {
643 *rejected_count += 1;
644 }
645 }
646}

◆ speculative_verify_greedy_f32()

void speculative_verify_greedy_f32 ( const float *  target_logits,
int  vocab_size,
int  draft_token,
int *  accepted,
int *  verified_token 
)

Greedy one-token speculative verification.

The draft model proposes draft_token. The target model is authoritative: if draft_token equals argmax(target_logits), the candidate is accepted and emitted. Otherwise the target argmax is emitted and the draft path must be reset or rewound by the runtime loop.

Parameters
target_logitsTarget/backbone logits [vocab_size]
vocab_sizeNumber of logits
draft_tokenCandidate token from draft/assistant model
acceptedOutput scalar: 1 if accepted, 0 otherwise
verified_tokenOutput scalar: accepted draft token or target argmax

Definition at line 585 of file topk_kernels.c.

590{
591 const int target_token = argmax_f32(target_logits, vocab_size);
592 const int ok = (target_token >= 0 && draft_token == target_token) ? 1 : 0;
593
594 if (accepted) {
595 *accepted = ok;
596 }
597 if (verified_token) {
598 *verified_token = ok ? draft_token : target_token;
599 }
600}
int argmax_f32(const float *scores, int n)
Find index of maximum value.
int vocab_size
Definition true_bpe.h:193

References argmax_f32(), and vocab_size.

◆ topk_batched_f32()

void topk_batched_f32 ( const float *  scores,
int  num_tokens,
int  n_experts,
int  k,
int *  indices,
float *  weights 
)

Batched top-K selection for multiple tokens.

Parameters
scoresInput scores [num_tokens, n_experts]
num_tokensNumber of tokens
n_expertsNumber of experts
kNumber of experts to select per token
indicesOutput: selected expert indices [num_tokens, k]
weightsOutput: routing weights [num_tokens, k] (can be NULL for no softmax)

Definition at line 465 of file topk_kernels.c.

471{
472 if (!scores || !indices || num_tokens <= 0 || n_experts <= 0 || k <= 0) {
473 return;
474 }
475
476 for (int t = 0; t < num_tokens; t++) {
477 const float *token_scores = scores + t * n_experts;
478 int *token_indices = indices + t * k;
479
480 if (weights) {
481 float *token_weights = weights + t * k;
482 topk_softmax_f32(token_scores, n_experts, k, token_indices, token_weights);
483 } else {
484 topk_f32(token_scores, n_experts, k, token_indices, NULL);
485 }
486 }
487}
void topk_softmax_f32(const float *scores, int n, int k, int *indices, float *weights)
Find top-K indices with softmax-normalized weights.

References topk_f32(), and topk_softmax_f32().

◆ topk_f32()

void topk_f32 ( const float *  scores,
int  n,
int  k,
int *  indices,
float *  values 
)

Find top-K indices and values from a score vector.

Parameters
scoresInput scores [n]
nNumber of scores (e.g., number of experts)
kNumber of top scores to select
indicesOutput: indices of top-K scores [k], sorted descending by value
valuesOutput: top-K score values [k], sorted descending (can be NULL)

Definition at line 51 of file topk_kernels.c.

56{
57 if (!scores || !indices || n <= 0 || k <= 0) {
58 return;
59 }
60
61 /* Clamp k to n */
62 if (k > n) {
63 k = n;
64 }
65
66 /* Initialize with first k elements */
67 float local_values[k];
68 for (int i = 0; i < k; i++) {
69 indices[i] = i;
70 local_values[i] = scores[i];
71 }
72
73 /* Find the minimum in our current top-k */
74 int min_idx = 0;
75 for (int i = 1; i < k; i++) {
76 if (local_values[i] < local_values[min_idx]) {
77 min_idx = i;
78 }
79 }
80
81 /* Scan remaining elements */
82 for (int i = k; i < n; i++) {
83 if (scores[i] > local_values[min_idx]) {
84 /* Replace the minimum */
85 indices[min_idx] = i;
86 local_values[min_idx] = scores[i];
87
88 /* Find new minimum */
89 min_idx = 0;
90 for (int j = 1; j < k; j++) {
91 if (local_values[j] < local_values[min_idx]) {
92 min_idx = j;
93 }
94 }
95 }
96 }
97
98 /* Sort results in descending order (simple insertion sort for small k) */
99 for (int i = 1; i < k; i++) {
100 float val = local_values[i];
101 int idx = indices[i];
102 int j = i - 1;
103 while (j >= 0 && local_values[j] < val) {
104 local_values[j + 1] = local_values[j];
105 indices[j + 1] = indices[j];
106 j--;
107 }
108 local_values[j + 1] = val;
109 indices[j + 1] = idx;
110 }
111
112 /* Copy values if output requested */
113 if (values) {
114 for (int i = 0; i < k; i++) {
115 values[i] = local_values[i];
116 }
117 }
118}

Referenced by moe_softmax_topk_router_llama_f32_workspace(), topk_batched_f32(), and topk_softmax_f32().

◆ topk_softmax_backward_f32()

void topk_softmax_backward_f32 ( const int *  indices,
const float *  weights,
const float *  d_weights,
float *  d_scores,
int  num_tokens,
int  n_experts_or_keys,
int  k 
)

Backward for hard top-k followed by softmax over selected values.

Matches PyTorch behavior for: values, indices = torch.topk(scores, k, dim=-1) weights = torch.softmax(values, dim=-1)

The hard selected indices are treated as fixed for this backward pass. Gradients are scattered only to selected scores; unselected scores are zero.

Definition at line 408 of file topk_kernels.c.

415{
416 if (!indices || !weights || !d_weights || !d_scores ||
417 num_tokens <= 0 || n_experts_or_keys <= 0 || k <= 0) {
418 return;
419 }
420
421 const size_t total = (size_t)num_tokens * (size_t)n_experts_or_keys;
422 for (size_t i = 0; i < total; ++i) {
423 d_scores[i] = 0.0f;
424 }
425
426 for (int t = 0; t < num_tokens; ++t) {
427 const int *row_indices = indices + (size_t)t * (size_t)k;
428 const float *row_weights = weights + (size_t)t * (size_t)k;
429 const float *row_d_weights = d_weights + (size_t)t * (size_t)k;
430 float *row_d_scores = d_scores + (size_t)t * (size_t)n_experts_or_keys;
431
432 float dot = 0.0f;
433 for (int i = 0; i < k; ++i) {
434 const int idx = row_indices[i];
435 if (idx >= 0 && idx < n_experts_or_keys) {
436 dot += row_weights[i] * row_d_weights[i];
437 }
438 }
439
440 for (int i = 0; i < k; ++i) {
441 const int idx = row_indices[i];
442 if (idx >= 0 && idx < n_experts_or_keys) {
443 row_d_scores[idx] += row_weights[i] * (row_d_weights[i] - dot);
444 }
445 }
446 }
447}

Referenced by deepseek_dsa_topk_softmax_backward_f32().

◆ topk_softmax_f32()

void topk_softmax_f32 ( const float *  scores,
int  n,
int  k,
int *  indices,
float *  weights 
)

Find top-K indices with softmax-normalized weights.

Parameters
scoresInput scores [n] (router logits)
nNumber of scores
kNumber of top scores to select
indicesOutput: indices of top-K scores [k]
weightsOutput: softmax-normalized weights for selected [k], sum to 1.0

Definition at line 136 of file topk_kernels.c.

141{
142 if (!scores || !indices || !weights || n <= 0 || k <= 0) {
143 return;
144 }
145
146 if (k > n) {
147 k = n;
148 }
149
150 /* First get top-K indices and values */
151 float values[k];
152 topk_f32(scores, n, k, indices, values);
153
154 /* Compute softmax over the selected values */
155 /* Find max for numerical stability */
156 float max_val = values[0];
157 for (int i = 1; i < k; i++) {
158 if (values[i] > max_val) {
159 max_val = values[i];
160 }
161 }
162
163 /* Compute exp and sum */
164 float sum = 0.0f;
165 for (int i = 0; i < k; i++) {
166 weights[i] = expf(values[i] - max_val);
167 sum += weights[i];
168 }
169
170 /* Normalize */
171 float inv_sum = 1.0f / sum;
172 for (int i = 0; i < k; i++) {
173 weights[i] *= inv_sum;
174 }
175}

References topk_f32().

Referenced by topk_batched_f32().