Helper: build one perceiver layer on latents, given features x. latents: [D, N_lat, batch] (D=64, N_lat = n latent tokens) x: [D, N_x, batch] (D=64, N_x = n feature tokens) pos: [D, N_x, batch] or nullptr (added to K and V after projection) Returns updated latents [D, N_lat, batch].
| 5435 | // pos: [D, N_x, batch] or nullptr (added to K and V after projection) |
| 5436 | // Returns updated latents [D, N_lat, batch]. |
| 5437 | static struct ggml_tensor* edgetam_perceiver_layer_forward( |
| 5438 | struct ggml_context* ctx, |
| 5439 | struct ggml_tensor* latents, |
| 5440 | struct ggml_tensor* x, |
| 5441 | struct ggml_tensor* pos, |
| 5442 | const edgetam_perceiver_layer& layer) { |
| 5443 | const int64_t D = latents->ne[0]; // 64 |
| 5444 | const int64_t N_lat = latents->ne[1]; // 256 (1D) or 1 (2D per window) |
| 5445 | const int64_t batch = latents->ne[2]; // 1 (1D) or 256 (2D) |
| 5446 | const int64_t N_x = x->ne[1]; // H*W (1D) or 16 (2D per window) |
| 5447 | const float scale = 1.0f / sqrtf((float)D); // 0.125 for D=64 |
| 5448 | |
| 5449 | // ── Cross-attention: latents attend to features ───────────────────── |
| 5450 | { |
| 5451 | auto* lat_n = sam3_layer_norm(ctx, latents, layer.ca_norm_latents_w, |
| 5452 | layer.ca_norm_latents_b); |
| 5453 | auto* x_n = sam3_layer_norm(ctx, x, layer.ca_norm_x_w, layer.ca_norm_x_b); |
| 5454 | |
| 5455 | // Q from latents: [D, N_lat, batch] → [D, N_lat, batch] |
| 5456 | auto* q = ggml_mul_mat(ctx, layer.ca_q_w, lat_n); // [D, N_lat, batch] |
| 5457 | |
| 5458 | // KV from features: [2*D, N_x, batch] |
| 5459 | auto* kv = ggml_mul_mat(ctx, layer.ca_kv_w, x_n); // [2*D, N_x, batch] |
| 5460 | |
| 5461 | // Split into K and V via views — kv is contiguous [2*D, N_x, batch] |
| 5462 | auto* k_raw = ggml_view_3d(ctx, kv, D, N_x, batch, |
| 5463 | kv->nb[1], kv->nb[2], 0); // [D, N_x, batch] |
| 5464 | auto* v_raw = ggml_view_3d(ctx, kv, D, N_x, batch, |
| 5465 | kv->nb[1], kv->nb[2], D * sizeof(float)); // [D, N_x, batch] |
| 5466 | |
| 5467 | // Add positional encoding to K and V |
| 5468 | struct ggml_tensor* k; |
| 5469 | struct ggml_tensor* v; |
| 5470 | if (pos) { |
| 5471 | k = ggml_add(ctx, k_raw, pos); |
| 5472 | v = ggml_add(ctx, v_raw, pos); |
| 5473 | } else { |
| 5474 | k = ggml_cont(ctx, k_raw); |
| 5475 | v = ggml_cont(ctx, v_raw); |
| 5476 | } |
| 5477 | |
| 5478 | // Reshape for flash_attn_ext (1 head): |
| 5479 | // Q: [D, N_lat, 1, batch] K: [D, N_x, 1, batch] V: [D, N_x, 1, batch] |
| 5480 | q = ggml_reshape_4d(ctx, q, D, N_lat, 1, batch); |
| 5481 | k = ggml_reshape_4d(ctx, k, D, N_x, 1, batch); |
| 5482 | v = ggml_reshape_4d(ctx, v, D, N_x, 1, batch); |
| 5483 | |
| 5484 | auto* attn = ggml_flash_attn_ext(ctx, q, k, v, nullptr, scale, 0.0f, 0.0f); |
| 5485 | // Output: [D, 1, N_lat, batch] (permuted) → reshape to [D, N_lat, batch] |
| 5486 | attn = ggml_reshape_3d(ctx, attn, D, N_lat, batch); |
| 5487 | |
| 5488 | // Output projection |
| 5489 | auto* ca_out = ggml_mul_mat(ctx, layer.ca_out_w, attn); // [D, N_lat, batch] |
| 5490 | |
| 5491 | // Residual |
| 5492 | latents = ggml_add(ctx, latents, ca_out); |
| 5493 | } |
| 5494 |
no test coverage detected