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

Vision kernels (im2patch, patch embedding, etc.) More...

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

Go to the source code of this file.

Macros

#define CK_VISION_NOINLINE
 

Functions

void add_stream_inplace (float *a, const float *b, size_t n)
 
void add_stream_reorder_2d (float *main_inout, float *aux_scratch, int grid_h, int grid_w, int embed_dim, int merge_size)
 
void feature_concat (const float *main_input, const float *branch_input, float *output, int rows, int main_dim, int branch_slice_dim, int num_branch_slices)
 
void feature_concat_2way (const float *main_input, const float *branch_input, float *output, int rows, int main_dim, int branch_slice_dim, int num_branch_slices)
 
void feature_slice_copy (const float *src, float *dst, int rows, int src_dim, int dst_dim, int dst_feature_offset)
 
void gemma4_vision_projector_prep_forward (const float *input, float *output, int tokens, int dim, float scale, float eps)
 
void im2patch (const float *image, float *patches, int C, int H, int W, int P)
 
void patch2im (const float *d_patches, float *d_image, int C, int H, int W, int P)
 
void position_embeddings_add (float *x, const float *position_embd, int num_tokens, int embed_dim, int num_positions)
 
void position_embeddings_add_at_offset (float *x, const float *position_embd, int num_tokens, int embed_dim, int num_positions, int start_position)
 
void position_embeddings_add_gemma4v_xy (float *x, const float *position_embd, int grid_h, int grid_w, int embed_dim, int source_grid_size)
 
void position_embeddings_add_tiled_2d (float *x, const float *position_embd, int grid_h, int grid_w, int embed_dim, int merge_size, int source_grid_size)
 
void position_embeddings_add_tiled_2d_align_corners (float *x, const float *position_embd, int grid_h, int grid_w, int embed_dim, int merge_size, int source_grid_size)
 
void position_embeddings_add_tiled_2d_align_corners_bf16 (float *x, const float *position_embd, int grid_h, int grid_w, int embed_dim, int merge_size, int source_grid_size)
 
void position_embeddings_add_tiled_2d_align_corners_fp32_interp_bf16 (float *x, const float *position_embd, int grid_h, int grid_w, int embed_dim, int merge_size, int source_grid_size)
 
void rowwise_bias_add (float *x, const float *bias, int rows, int dim)
 
void spatial_average_pool_contiguous (const float *input, float *output, int grid_h, int grid_w, int embed_dim, int merge_size)
 
void spatial_merge_2x2 (const float *input, float *output, int grid_h, int grid_w, int embed_dim)
 
void spatial_merge_contiguous_tiled (const float *input, float *output, int grid_h, int grid_w, int embed_dim, int merge_size)
 
static int tile_order_index_2d (int linear_idx, int grid_h, int grid_w, int merge_size)
 
static int tile_order_linear_index_2d (int y, int x, int grid_h, int grid_w, int merge_size)
 
void vision_position_ids_2d_merge (int32_t *positions, int grid_h, int grid_w, int merge_size)
 

Detailed Description

Vision kernels (im2patch, patch embedding, etc.)

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

Definition in file vision_kernels.c.

Macro Definition Documentation

◆ CK_VISION_NOINLINE

#define CK_VISION_NOINLINE

Definition at line 27 of file vision_kernels.c.

Function Documentation

◆ add_stream_inplace()

void add_stream_inplace ( float *  a,
const float *  b,
size_t  n 
)

Definition at line 713 of file vision_kernels.c.

716{
717 if (!a || !b || n == 0) {
718 return;
719 }
720
721 for (size_t i = 0; i < n; ++i) {
722 a[i] += b[i];
723 }
724}

◆ add_stream_reorder_2d()

void add_stream_reorder_2d ( float *  main_inout,
float *  aux_scratch,
int  grid_h,
int  grid_w,
int  embed_dim,
int  merge_size 
)

Definition at line 726 of file vision_kernels.c.

732{
733 if (!main_inout || !aux_scratch || grid_h <= 0 || grid_w <= 0 || embed_dim <= 0 || merge_size <= 0) {
734 return;
735 }
736
737 const int num_tokens = grid_h * grid_w;
738 const size_t total_elems = (size_t) num_tokens * (size_t) embed_dim;
739
740 for (size_t i = 0; i < total_elems; ++i) {
741 main_inout[i] += aux_scratch[i];
742 }
743
744 for (int tok = 0; tok < num_tokens; ++tok) {
745 const int src_tok = tile_order_index_2d(tok, grid_h, grid_w, merge_size);
746 const float *src_main = main_inout + (size_t) src_tok * (size_t) embed_dim;
747 float *dst = aux_scratch + (size_t) tok * (size_t) embed_dim;
748 for (int d = 0; d < embed_dim; ++d) {
749 dst[d] = src_main[d];
750 }
751 }
752
753 memcpy(main_inout, aux_scratch, total_elems * sizeof(float));
754}
static int tile_order_index_2d(int linear_idx, int grid_h, int grid_w, int merge_size)

References tile_order_index_2d().

◆ feature_concat()

void feature_concat ( const float *  main_input,
const float *  branch_input,
float *  output,
int  rows,
int  main_dim,
int  branch_slice_dim,
int  num_branch_slices 
)

Definition at line 804 of file vision_kernels.c.

811{
812 if (!main_input || !output || rows <= 0 || main_dim < 0 || branch_slice_dim < 0 || num_branch_slices < 0) {
813 return;
814 }
815
816 const int branch_total_dim = branch_slice_dim * num_branch_slices;
817 const int out_dim = main_dim + branch_total_dim;
818
819 const int in_place_expand = (main_input == output) && (out_dim > main_dim);
820 const int row_start = in_place_expand ? rows - 1 : 0;
821 const int row_end = in_place_expand ? -1 : rows;
822 const int row_step = in_place_expand ? -1 : 1;
823
824 for (int row = row_start; row != row_end; row += row_step) {
825 const float *src_main = main_input + (size_t) row * (size_t) main_dim;
826 float *dst_row = output + (size_t) row * (size_t) out_dim;
827
828 if (main_dim > 0) {
829 memmove(dst_row, src_main, (size_t) main_dim * sizeof(float));
830 }
831
832 for (int slice = 0; slice < num_branch_slices; ++slice) {
833 const float *src_branch = branch_input
834 + (size_t) slice * (size_t) rows * (size_t) branch_slice_dim
835 + (size_t) row * (size_t) branch_slice_dim;
836 float *dst_branch = dst_row + (size_t) main_dim + (size_t) slice * (size_t) branch_slice_dim;
837 if (branch_slice_dim > 0) {
838 memcpy(dst_branch, src_branch, (size_t) branch_slice_dim * sizeof(float));
839 }
840 }
841 }
842}

◆ feature_concat_2way()

void feature_concat_2way ( const float *  main_input,
const float *  branch_input,
float *  output,
int  rows,
int  main_dim,
int  branch_slice_dim,
int  num_branch_slices 
)

Definition at line 756 of file vision_kernels.c.

763{
764 if (!main_input || !branch_input || !output || rows <= 0 || main_dim <= 0 ||
765 branch_slice_dim < 0 || num_branch_slices < 0) {
766 return;
767 }
768
769 const int branch_dim = branch_slice_dim * num_branch_slices;
770 const size_t main_bytes = (size_t) main_dim * sizeof(float);
771 const size_t branch_bytes = (size_t) branch_dim * sizeof(float);
772 const size_t out_stride = (size_t) (main_dim + branch_dim);
773
774 for (int r = 0; r < rows; ++r) {
775 const float *src_main = main_input + ((size_t) r * (size_t) main_dim);
776 const float *src_branch = branch_input + ((size_t) r * (size_t) branch_dim);
777 float *dst = output + ((size_t) r * out_stride);
778 memcpy(dst, src_main, main_bytes);
779 memcpy(dst + main_dim, src_branch, branch_bytes);
780 }
781}

◆ feature_slice_copy()

void feature_slice_copy ( const float *  src,
float *  dst,
int  rows,
int  src_dim,
int  dst_dim,
int  dst_feature_offset 
)

Definition at line 783 of file vision_kernels.c.

789{
790 if (!src || !dst || rows <= 0 || src_dim <= 0 || dst_dim <= 0 || dst_feature_offset < 0) {
791 return;
792 }
793 if (dst_feature_offset + src_dim > dst_dim) {
794 return;
795 }
796
797 for (int row = 0; row < rows; ++row) {
798 const float *src_row = src + (size_t) row * (size_t) src_dim;
799 float *dst_row = dst + (size_t) row * (size_t) dst_dim + (size_t) dst_feature_offset;
800 memcpy(dst_row, src_row, (size_t) src_dim * sizeof(float));
801 }
802}

◆ gemma4_vision_projector_prep_forward()

void gemma4_vision_projector_prep_forward ( const float *  input,
float *  output,
int  tokens,
int  dim,
float  scale,
float  eps 
)

Definition at line 633 of file vision_kernels.c.

639{
640 if (input == NULL || output == NULL || tokens <= 0 || dim <= 0) return;
641 if (eps <= 0.0f) eps = 1.0e-6f;
642 for (int t = 0; t < tokens; ++t) {
643 const float *src = input + (size_t)t * (size_t)dim;
644 float *dst = output + (size_t)t * (size_t)dim;
645 double ss = 0.0;
646 for (int i = 0; i < dim; ++i) {
647 const float v = src[i] * scale;
648 ss += (double)v * (double)v;
649 }
650 const float inv_rms = 1.0f / sqrtf((float)(ss / (double)dim) + eps);
651 for (int i = 0; i < dim; ++i) {
652 dst[i] = (src[i] * scale) * inv_rms;
653 }
654 }
655}

◆ im2patch()

void im2patch ( const float *  image,
float *  patches,
int  C,
int  H,
int  W,
int  P 
)

im2patch: Transforms an image into a sequence of flattened patches.

Image Layout: [C, H, W] (Row-major: W is fastest moving) Output Layout: [num_patches, C * P * P]

num_patches = (H/P) * (W/P) P = patch_size

Definition at line 82 of file vision_kernels.c.

85{
86 int num_patches_h = H / P;
87 int num_patches_w = W / P;
88 int patch_dim = C * P * P;
89
90 // ph, pw: patch grid coordinates
91 for (int ph = 0; ph < num_patches_h; ++ph) {
92 for (int pw = 0; pw < num_patches_w; ++pw) {
93
94 int patch_idx = ph * num_patches_w + pw;
95 float *dst_patch = patches + (size_t)patch_idx * patch_dim;
96
97 // For each patch, grab pixels from all channels
98 for (int c = 0; c < C; ++c) {
99 for (int py = 0; py < P; ++py) {
100 int y = ph * P + py;
101 int x = pw * P;
102
103 // Input row start in the image
104 const float *src_row = image + (size_t)c * H * W + (size_t)y * W + x;
105
106 // Destination row in the flattened patch sequence
107 float *dst_row = dst_patch + (size_t)c * P * P + (size_t)py * P;
108
109 // Copy P pixels (one row of the patch)
110 memcpy(dst_row, src_row, P * sizeof(float));
111 }
112 }
113 }
114 }
115}
#define C(color)
Definition show_config.c:39

References C.

◆ patch2im()

void patch2im ( const float *  d_patches,
float *  d_image,
int  C,
int  H,
int  W,
int  P 
)

patch2im: Accumulates gradients from patches back into the image. (Backward pass)

d_patches: [num_patches, C * P * P] d_image: [C, H, W] (Accumulated)

Definition at line 123 of file vision_kernels.c.

126{
127 int num_patches_h = H / P;
128 int num_patches_w = W / P;
129 int patch_dim = C * P * P;
130
131 // Zero out the image first as we are accumulating gradients
132 memset(d_image, 0, (size_t)C * H * W * sizeof(float));
133
134 for (int ph = 0; ph < num_patches_h; ++ph) {
135 for (int pw = 0; pw < num_patches_w; ++pw) {
136
137 int patch_idx = ph * num_patches_w + pw;
138 const float *src_patch = d_patches + (size_t)patch_idx * patch_dim;
139
140 for (int c = 0; c < C; ++c) {
141 for (int py = 0; py < P; ++py) {
142 int y = ph * P + py;
143 int x = pw * P;
144
145 float *dst_row = d_image + (size_t)c * H * W + (size_t)y * W + x;
146 const float *src_row = src_patch + (size_t)c * P * P + (size_t)py * P;
147
148 // Add the patch gradient to the image gradient
149 for (int px = 0; px < P; ++px) {
150 dst_row[px] += src_row[px];
151 }
152 }
153 }
154 }
155 }
156}

References C.

◆ position_embeddings_add()

void position_embeddings_add ( float *  x,
const float *  position_embd,
int  num_tokens,
int  embed_dim,
int  num_positions 
)

Add learned absolute position embeddings in-place.

x layout: [num_tokens, embed_dim] position_embd: [num_positions, embed_dim]

This first v8 vision path intentionally assumes native resized embeddings are already materialized in the weight tensor, so token i maps directly to position_embd[i]. This is the correct contract for fixed-size bring-up.

Definition at line 168 of file vision_kernels.c.

173{
174 if (x == NULL || position_embd == NULL || num_tokens <= 0 || embed_dim <= 0) {
175 return;
176 }
177 const int limit = num_tokens < num_positions ? num_tokens : num_positions;
178 for (int tok = 0; tok < limit; ++tok) {
179 float *dst = x + (size_t)tok * embed_dim;
180 const float *src = position_embd + (size_t)tok * embed_dim;
181 for (int d = 0; d < embed_dim; ++d) {
182 dst[d] += src[d];
183 }
184 }
185}

◆ position_embeddings_add_at_offset()

void position_embeddings_add_at_offset ( float *  x,
const float *  position_embd,
int  num_tokens,
int  embed_dim,
int  num_positions,
int  start_position 
)

Definition at line 187 of file vision_kernels.c.

193{
194 if (x == NULL || position_embd == NULL || num_tokens <= 0 || embed_dim <= 0 ||
195 num_positions <= 0 || start_position < 0 || start_position >= num_positions) {
196 return;
197 }
198 const int available = num_positions - start_position;
199 const int limit = num_tokens < available ? num_tokens : available;
200 for (int tok = 0; tok < limit; ++tok) {
201 float *dst = x + (size_t)tok * embed_dim;
202 const float *src = position_embd + (size_t)(start_position + tok) * embed_dim;
203 for (int d = 0; d < embed_dim; ++d) {
204 dst[d] += src[d];
205 }
206 }
207}

◆ position_embeddings_add_gemma4v_xy()

void position_embeddings_add_gemma4v_xy ( float *  x,
const float *  position_embd,
int  grid_h,
int  grid_w,
int  embed_dim,
int  source_grid_size 
)

Definition at line 209 of file vision_kernels.c.

215{
216 if (x == NULL || position_embd == NULL || grid_h <= 0 || grid_w <= 0 || embed_dim <= 0) {
217 return;
218 }
219 if (source_grid_size <= 0) {
220 source_grid_size = grid_w > grid_h ? grid_w : grid_h;
221 }
222
223 const size_t table_stride = (size_t) source_grid_size * (size_t) embed_dim;
224 const float *table_x = position_embd;
225 const float *table_y = position_embd + table_stride;
226
227 for (int y = 0; y < grid_h; ++y) {
228 const int yy = y < source_grid_size ? y : (source_grid_size - 1);
229 const float *row_y = table_y + (size_t) yy * (size_t) embed_dim;
230 for (int x_pos = 0; x_pos < grid_w; ++x_pos) {
231 const int xx = x_pos < source_grid_size ? x_pos : (source_grid_size - 1);
232 const float *row_x = table_x + (size_t) xx * (size_t) embed_dim;
233 float *dst = x + ((size_t) y * (size_t) grid_w + (size_t) x_pos) * (size_t) embed_dim;
234 for (int d = 0; d < embed_dim; ++d) {
235 dst[d] += row_x[d] + row_y[d];
236 }
237 }
238 }
239}

◆ position_embeddings_add_tiled_2d()

void position_embeddings_add_tiled_2d ( float *  x,
const float *  position_embd,
int  grid_h,
int  grid_w,
int  embed_dim,
int  merge_size,
int  source_grid_size 
)

Definition at line 248 of file vision_kernels.c.

255{
256 if (x == NULL || position_embd == NULL || grid_h <= 0 || grid_w <= 0 || embed_dim <= 0 || merge_size <= 0) {
257 return;
258 }
259
260 if (source_grid_size <= 0) {
261 source_grid_size = grid_h == grid_w ? grid_h : (grid_h > grid_w ? grid_h : grid_w);
262 }
263
264 const int num_tokens = grid_h * grid_w;
265 const int source_tokens = source_grid_size * source_grid_size;
266 const int needs_resize = source_grid_size != grid_h || source_grid_size != grid_w;
267
268 if (!needs_resize) {
269 for (int tok = 0; tok < num_tokens; ++tok) {
270 const int flat = tile_order_index_2d(tok, grid_h, grid_w, merge_size);
271 if (flat < 0 || flat >= source_tokens) {
272 continue;
273 }
274 float *dst = x + (size_t) tok * (size_t) embed_dim;
275 const float *src = position_embd + (size_t) flat * (size_t) embed_dim;
276 for (int d = 0; d < embed_dim; ++d) {
277 dst[d] += src[d];
278 }
279 }
280 return;
281 }
282
283 const float sf_x = (float) grid_w / (float) source_grid_size;
284 const float sf_y = (float) grid_h / (float) source_grid_size;
285 const float pixel_offset = 0.5f;
286 const float support_x = fmaxf(1.0f, 1.0f / sf_x);
287 const float support_y = fmaxf(1.0f, 1.0f / sf_y);
288 const float invscale_x = 1.0f / support_x;
289 const float invscale_y = 1.0f / support_y;
290
291 /* Preserve ggml's channel -> row -> column loop nesting. The destination
292 * index converts the row-major interpolation result directly to CK's
293 * merge-tiled token layout without allocating an intermediate tensor. */
294 for (int d = 0; d < embed_dim; ++d) {
295 for (int dst_y = 0; dst_y < grid_h; ++dst_y) {
296 const float y_src = ((float) dst_y + pixel_offset) / sf_y;
297 int y_min = (int) (y_src - support_y + pixel_offset);
298 int y_max = (int) (y_src + support_y + pixel_offset);
299 if (y_min < 0) y_min = 0;
300 if (y_max > source_grid_size) y_max = source_grid_size;
301
302 for (int dst_x = 0; dst_x < grid_w; ++dst_x) {
303 const float x_src = ((float) dst_x + pixel_offset) / sf_x;
304 int x_min = (int) (x_src - support_x + pixel_offset);
305 int x_max = (int) (x_src + support_x + pixel_offset);
306 if (x_min < 0) x_min = 0;
307 if (x_max > source_grid_size) x_max = source_grid_size;
308 float val = 0.0f;
309 float total_weight = 0.0f;
310 for (int sy = y_min; sy < y_max; ++sy) {
311 const float wy_arg = ((float) sy - y_src + pixel_offset) * invscale_y;
312 const float wy = fmaxf(1.0f - fabsf(wy_arg), 0.0f);
313 if (wy <= 0.0f) {
314 continue;
315 }
316 for (int sx = x_min; sx < x_max; ++sx) {
317 const float wx_arg = ((float) sx - x_src + pixel_offset) * invscale_x;
318 const float wx = fmaxf(1.0f - fabsf(wx_arg), 0.0f);
319 const float weight = wx * wy;
320 if (weight <= 0.0f) {
321 continue;
322 }
323 const float sample = position_embd[((size_t) sy * (size_t) source_grid_size + (size_t) sx) * (size_t) embed_dim + (size_t) d];
324 val = fmaf(sample, weight, val);
325 total_weight += weight;
326 }
327 }
328 if (total_weight > 0.0f) {
329 val /= total_weight;
330 const int tok = tile_order_linear_index_2d(
331 dst_y, dst_x, grid_h, grid_w, merge_size);
332 x[(size_t) tok * (size_t) embed_dim + (size_t) d] += val;
333 }
334 }
335 }
336 }
337}
static int tile_order_linear_index_2d(int y, int x, int grid_h, int grid_w, int merge_size)

References tile_order_index_2d(), and tile_order_linear_index_2d().

◆ position_embeddings_add_tiled_2d_align_corners()

void position_embeddings_add_tiled_2d_align_corners ( float *  x,
const float *  position_embd,
int  grid_h,
int  grid_w,
int  embed_dim,
int  merge_size,
int  source_grid_size 
)

Definition at line 342 of file vision_kernels.c.

349{
350 if (x == NULL || position_embd == NULL || grid_h <= 0 || grid_w <= 0 ||
351 embed_dim <= 0 || merge_size <= 0 || source_grid_size <= 0) {
352 return;
353 }
354
355 const float y_scale = grid_h > 1
356 ? (float)(source_grid_size - 1) / (float)(grid_h - 1)
357 : 0.0f;
358 const float x_scale = grid_w > 1
359 ? (float)(source_grid_size - 1) / (float)(grid_w - 1)
360 : 0.0f;
361
362 for (int tok = 0; tok < grid_h * grid_w; ++tok) {
363 const int row_major = tile_order_index_2d(tok, grid_h, grid_w, merge_size);
364 const int dst_y = row_major / grid_w;
365 const int dst_x = row_major % grid_w;
366 const float src_y = (float)dst_y * y_scale;
367 const float src_x = (float)dst_x * x_scale;
368 const int y0 = (int)src_y;
369 const int x0 = (int)src_x;
370 const int y1 = y0 + 1 < source_grid_size ? y0 + 1 : y0;
371 const int x1 = x0 + 1 < source_grid_size ? x0 + 1 : x0;
372 const float dy = src_y - (float)y0;
373 const float dx = src_x - (float)x0;
374 const float w00 = (1.0f - dy) * (1.0f - dx);
375 const float w01 = (1.0f - dy) * dx;
376 const float w10 = dy * (1.0f - dx);
377 const float w11 = dy * dx;
378 const float *p00 = position_embd + ((size_t)y0 * source_grid_size + x0) * embed_dim;
379 const float *p01 = position_embd + ((size_t)y0 * source_grid_size + x1) * embed_dim;
380 const float *p10 = position_embd + ((size_t)y1 * source_grid_size + x0) * embed_dim;
381 const float *p11 = position_embd + ((size_t)y1 * source_grid_size + x1) * embed_dim;
382 float *dst = x + (size_t)tok * embed_dim;
383
384 for (int d = 0; d < embed_dim; ++d) {
385 const float pos = p00[d] * w00 + p01[d] * w01 + p10[d] * w10 + p11[d] * w11;
386 dst[d] += pos;
387 }
388 }
389}

References tile_order_index_2d().

◆ position_embeddings_add_tiled_2d_align_corners_bf16()

void position_embeddings_add_tiled_2d_align_corners_bf16 ( float *  x,
const float *  position_embd,
int  grid_h,
int  grid_w,
int  embed_dim,
int  merge_size,
int  source_grid_size 
)

Definition at line 397 of file vision_kernels.c.

404{
405 if (x == NULL || position_embd == NULL || grid_h <= 0 || grid_w <= 0 ||
406 embed_dim <= 0 || merge_size <= 0 || source_grid_size <= 0) {
407 return;
408 }
409
410 for (int tok = 0; tok < grid_h * grid_w; ++tok) {
411 const int row_major = tile_order_index_2d(tok, grid_h, grid_w, merge_size);
412 const int dst_y = row_major / grid_w;
413 const int dst_x = row_major % grid_w;
414 /* torch.linspace computes each rational coordinate before FP32 storage. */
415 const float src_y = grid_h > 1
416 ? (float)((double)dst_y * (double)(source_grid_size - 1) / (double)(grid_h - 1))
417 : 0.0f;
418 const float src_x = grid_w > 1
419 ? (float)((double)dst_x * (double)(source_grid_size - 1) / (double)(grid_w - 1))
420 : 0.0f;
421 const int y0 = (int)src_y;
422 const int x0 = (int)src_x;
423 const int y1 = y0 + 1 < source_grid_size ? y0 + 1 : y0;
424 const int x1 = x0 + 1 < source_grid_size ? x0 + 1 : x0;
425 const float dy = src_y - (float)y0;
426 const float dx = src_x - (float)x0;
427 const float w00 = bf16_to_float(float_to_bf16((1.0f - dy) * (1.0f - dx)));
428 const float w01 = bf16_to_float(float_to_bf16((1.0f - dy) * dx));
429 const float w10 = bf16_to_float(float_to_bf16(dy * (1.0f - dx)));
430 const float w11 = bf16_to_float(float_to_bf16(dy * dx));
431 const float *p00 = position_embd + ((size_t)y0 * source_grid_size + x0) * embed_dim;
432 const float *p01 = position_embd + ((size_t)y0 * source_grid_size + x1) * embed_dim;
433 const float *p10 = position_embd + ((size_t)y1 * source_grid_size + x0) * embed_dim;
434 const float *p11 = position_embd + ((size_t)y1 * source_grid_size + x1) * embed_dim;
435 float *dst = x + (size_t)tok * embed_dim;
436
437 for (int d = 0; d < embed_dim; ++d) {
438 const float v00 = bf16_to_float(float_to_bf16(p00[d] * w00));
439 const float v01 = bf16_to_float(float_to_bf16(p01[d] * w01));
440 const float v10 = bf16_to_float(float_to_bf16(p10[d] * w10));
441 const float v11 = bf16_to_float(float_to_bf16(p11[d] * w11));
442 float pos = bf16_to_float(float_to_bf16(v00 + v01));
443 pos = bf16_to_float(float_to_bf16(pos + v10));
444 pos = bf16_to_float(float_to_bf16(pos + v11));
445 const float hidden = bf16_to_float(float_to_bf16(dst[d]));
446 dst[d] = bf16_to_float(float_to_bf16(hidden + pos));
447 }
448 }
449}
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

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

◆ position_embeddings_add_tiled_2d_align_corners_fp32_interp_bf16()

void position_embeddings_add_tiled_2d_align_corners_fp32_interp_bf16 ( float *  x,
const float *  position_embd,
int  grid_h,
int  grid_w,
int  embed_dim,
int  merge_size,
int  source_grid_size 
)

Definition at line 458 of file vision_kernels.c.

466{
467 if (x == NULL || position_embd == NULL || grid_h <= 0 || grid_w <= 0 ||
468 embed_dim <= 0 || merge_size <= 0 || source_grid_size <= 0) {
469 return;
470 }
471
472 for (int tok = 0; tok < grid_h * grid_w; ++tok) {
473 const int row_major = tile_order_index_2d(tok, grid_h, grid_w, merge_size);
474 const int dst_y = row_major / grid_w;
475 const int dst_x = row_major % grid_w;
476 const float src_y = grid_h > 1
477 ? (float)dst_y * (float)(source_grid_size - 1) / (float)(grid_h - 1)
478 : 0.0f;
479 const float src_x = grid_w > 1
480 ? (float)dst_x * (float)(source_grid_size - 1) / (float)(grid_w - 1)
481 : 0.0f;
482 const int y0 = (int)src_y;
483 const int x0 = (int)src_x;
484 const int y1 = y0 + 1 < source_grid_size ? y0 + 1 : y0;
485 const int x1 = x0 + 1 < source_grid_size ? x0 + 1 : x0;
486 const float y_distance0 = fabsf(src_y - (float)y0);
487 const float y_distance1 = fabsf(src_y - (float)y0 - 1.0f);
488 const float x_distance0 = fabsf(src_x - (float)x0);
489 const float x_distance1 = fabsf(src_x - (float)x0 - 1.0f);
490 const float wy0 = fmaxf(1.0f - y_distance0, 0.0f);
491 const float wy1 = fmaxf(1.0f - y_distance1, 0.0f);
492 const float wx0 = fmaxf(1.0f - x_distance0, 0.0f);
493 const float wx1 = fmaxf(1.0f - x_distance1, 0.0f);
494 const float w00 = wy0 * wx0;
495 const float w01 = wy0 * wx1;
496 const float w10 = wy1 * wx0;
497 const float w11 = wy1 * wx1;
498 const float *p00 = position_embd + ((size_t)y0 * source_grid_size + x0) * embed_dim;
499 const float *p01 = position_embd + ((size_t)y0 * source_grid_size + x1) * embed_dim;
500 const float *p10 = position_embd + ((size_t)y1 * source_grid_size + x0) * embed_dim;
501 const float *p11 = position_embd + ((size_t)y1 * source_grid_size + x1) * embed_dim;
502 float *dst = x + (size_t)tok * embed_dim;
503
504 for (int d = 0; d < embed_dim; ++d) {
505 /* Materialize products before the reduction, as torch.sum does. */
506 volatile float v00 = p00[d] * w00;
507 volatile float v01 = p01[d] * w01;
508 volatile float v10 = p10[d] * w10;
509 volatile float v11 = p11[d] * w11;
510 volatile float pos01 = v00 + v01;
511 volatile float pos012 = pos01 + v10;
512 const float pos = pos012 + v11;
513 const float pos_bf16 = bf16_to_float(float_to_bf16(pos));
514 const float hidden = bf16_to_float(float_to_bf16(dst[d]));
515 dst[d] = bf16_to_float(float_to_bf16(hidden + pos_bf16));
516 }
517 }
518}

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

◆ rowwise_bias_add()

void rowwise_bias_add ( float *  x,
const float *  bias,
int  rows,
int  dim 
)

Definition at line 696 of file vision_kernels.c.

700{
701 if (!x || !bias || rows <= 0 || dim <= 0) {
702 return;
703 }
704
705 for (int r = 0; r < rows; ++r) {
706 float *row = x + ((size_t) r * (size_t) dim);
707 for (int c = 0; c < dim; ++c) {
708 row[c] += bias[c];
709 }
710 }
711}

◆ spatial_average_pool_contiguous()

void spatial_average_pool_contiguous ( const float *  input,
float *  output,
int  grid_h,
int  grid_w,
int  embed_dim,
int  merge_size 
)

Definition at line 657 of file vision_kernels.c.

663{
664 if (input == NULL || output == NULL || grid_h <= 0 || grid_w <= 0 || embed_dim <= 0 || merge_size <= 0) {
665 return;
666 }
667
668 const int out_h = grid_h / merge_size;
669 const int out_w = grid_w / merge_size;
670 if (out_h <= 0 || out_w <= 0) {
671 return;
672 }
673
674 const float inv_area = 1.0f / (float)(merge_size * merge_size);
675 for (int oy = 0; oy < out_h; ++oy) {
676 for (int ox = 0; ox < out_w; ++ox) {
677 float *dst = output + ((size_t)oy * (size_t)out_w + (size_t)ox) * (size_t)embed_dim;
678 memset(dst, 0, (size_t)embed_dim * sizeof(float));
679 for (int dy = 0; dy < merge_size; ++dy) {
680 const int iy = oy * merge_size + dy;
681 for (int dx = 0; dx < merge_size; ++dx) {
682 const int ix = ox * merge_size + dx;
683 const float *src = input + ((size_t)iy * (size_t)grid_w + (size_t)ix) * (size_t)embed_dim;
684 for (int c = 0; c < embed_dim; ++c) {
685 dst[c] += src[c];
686 }
687 }
688 }
689 for (int c = 0; c < embed_dim; ++c) {
690 dst[c] *= inv_area;
691 }
692 }
693 }
694}

◆ spatial_merge_2x2()

void spatial_merge_2x2 ( const float *  input,
float *  output,
int  grid_h,
int  grid_w,
int  embed_dim 
)

Merge 2x2 neighboring tokens into a single wider token.

Input layout: [grid_h * grid_w, embed_dim] Output layout: [(grid_h/2) * (grid_w/2), embed_dim * 4]

Pack order within each merged token: top-left, top-right, bottom-left, bottom-right

Definition at line 577 of file vision_kernels.c.

582{
583 if (input == NULL || output == NULL || grid_h <= 0 || grid_w <= 0 || embed_dim <= 0) {
584 return;
585 }
586
587 const int merged_h = grid_h / 2;
588 const int merged_w = grid_w / 2;
589 const size_t token_stride = (size_t)embed_dim;
590 const size_t merged_stride = (size_t)embed_dim * 4;
591
592 for (int mh = 0; mh < merged_h; ++mh) {
593 for (int mw = 0; mw < merged_w; ++mw) {
594 const int y0 = mh * 2;
595 const int x0 = mw * 2;
596 const int in_idx00 = y0 * grid_w + x0;
597 const int in_idx01 = in_idx00 + 1;
598 const int in_idx10 = in_idx00 + grid_w;
599 const int in_idx11 = in_idx10 + 1;
600 const int out_idx = mh * merged_w + mw;
601
602 const float *src00 = input + (size_t)in_idx00 * token_stride;
603 const float *src01 = input + (size_t)in_idx01 * token_stride;
604 const float *src10 = input + (size_t)in_idx10 * token_stride;
605 const float *src11 = input + (size_t)in_idx11 * token_stride;
606 float *dst = output + (size_t)out_idx * merged_stride;
607
608 memcpy(dst + 0 * embed_dim, src00, (size_t)embed_dim * sizeof(float));
609 memcpy(dst + 1 * embed_dim, src01, (size_t)embed_dim * sizeof(float));
610 memcpy(dst + 2 * embed_dim, src10, (size_t)embed_dim * sizeof(float));
611 memcpy(dst + 3 * embed_dim, src11, (size_t)embed_dim * sizeof(float));
612 }
613 }
614}

◆ spatial_merge_contiguous_tiled()

void spatial_merge_contiguous_tiled ( const float *  input,
float *  output,
int  grid_h,
int  grid_w,
int  embed_dim,
int  merge_size 
)

Definition at line 616 of file vision_kernels.c.

622{
623 if (input == NULL || output == NULL || grid_h <= 0 || grid_w <= 0 || embed_dim <= 0 || merge_size <= 0) {
624 return;
625 }
626
627 const size_t num_tokens = (size_t) grid_h * (size_t) grid_w;
628 const size_t merge_factor = (size_t) merge_size * (size_t) merge_size;
629 const size_t merged_tokens = num_tokens / merge_factor;
630 memcpy(output, input, merged_tokens * (size_t) embed_dim * merge_factor * sizeof(float));
631}

◆ tile_order_index_2d()

static int tile_order_index_2d ( int  linear_idx,
int  grid_h,
int  grid_w,
int  merge_size 
)
static

Definition at line 30 of file vision_kernels.c.

34{
35 if (linear_idx < 0 || grid_h <= 0 || grid_w <= 0 || merge_size <= 0) {
36 return 0;
37 }
38
39 int index = 0;
40 for (int y = 0; y < grid_h; y += merge_size) {
41 for (int x = 0; x < grid_w; x += merge_size) {
42 for (int dy = 0; dy < merge_size && y + dy < grid_h; ++dy) {
43 for (int dx = 0; dx < merge_size && x + dx < grid_w; ++dx) {
44 if (index == linear_idx) {
45 return (y + dy) * grid_w + x + dx;
46 }
47 ++index;
48 }
49 }
50 }
51 }
52 return 0;
53}

Referenced by add_stream_reorder_2d(), position_embeddings_add_tiled_2d(), position_embeddings_add_tiled_2d_align_corners(), position_embeddings_add_tiled_2d_align_corners_bf16(), and position_embeddings_add_tiled_2d_align_corners_fp32_interp_bf16().

◆ tile_order_linear_index_2d()

static int tile_order_linear_index_2d ( int  y,
int  x,
int  grid_h,
int  grid_w,
int  merge_size 
)
static

Definition at line 55 of file vision_kernels.c.

60{
61 const int tile_y = y / merge_size;
62 const int tile_x = x / merge_size;
63 const int tile_height = grid_h - tile_y * merge_size < merge_size
64 ? grid_h - tile_y * merge_size : merge_size;
65 const int tile_width = grid_w - tile_x * merge_size < merge_size
66 ? grid_w - tile_x * merge_size : merge_size;
67 const int rows_before = tile_y * merge_size * grid_w;
68 const int tiles_before = tile_height * tile_x * merge_size;
69 const int within_tile = (y % merge_size) * tile_width + x % merge_size;
70 return rows_before + tiles_before + within_tile;
71}

Referenced by position_embeddings_add_tiled_2d().

◆ vision_position_ids_2d_merge()

void vision_position_ids_2d_merge ( int32_t *  positions,
int  grid_h,
int  grid_w,
int  merge_size 
)

Build merged 2D vision position IDs in the layout expected by vision M-RoPE.

Output layout: [4, grid_h * grid_w] flattened as [y_stream | x_stream | y_stream_dup | x_stream_dup]

Tokens are emitted in merged-tile traversal order so the position buffer matches the same 2x2 grouping used by Qwen-style vision encoders.

Definition at line 529 of file vision_kernels.c.

533{
534 if (!positions || grid_h <= 0 || grid_w <= 0 || merge_size <= 0) {
535 return;
536 }
537
538 const int num_tokens = grid_h * grid_w;
539 int ptr = 0;
540
541 for (int y = 0; y < grid_h; y += merge_size) {
542 for (int x = 0; x < grid_w; x += merge_size) {
543 for (int dy = 0; dy < merge_size; ++dy) {
544 for (int dx = 0; dx < merge_size; ++dx) {
545 const int yy = y + dy;
546 const int xx = x + dx;
547 if (yy >= grid_h || xx >= grid_w || ptr >= num_tokens) {
548 continue;
549 }
550 positions[ptr] = yy;
551 positions[num_tokens + ptr] = xx;
552 positions[2 * num_tokens + ptr] = yy;
553 positions[3 * num_tokens + ptr] = xx;
554 ++ptr;
555 }
556 }
557 }
558 }
559
560 for (; ptr < num_tokens; ++ptr) {
561 positions[ptr] = 0;
562 positions[num_tokens + ptr] = 0;
563 positions[2 * num_tokens + ptr] = 0;
564 positions[3 * num_tokens + ptr] = 0;
565 }
566}