| 181 | // ═══════════════════════════════════════════════════════════════════════════════ |
| 182 | |
| 183 | static void test_sinusoidal_pe(const std::string & ref_dir, int & n_pass, int & n_fail) { |
| 184 | fprintf(stderr, "\n╔══════════════════════════════════════════╗\n"); |
| 185 | fprintf(stderr, "║ Test: Sinusoidal PE ║\n"); |
| 186 | fprintf(stderr, "╚══════════════════════════════════════════╝\n"); |
| 187 | |
| 188 | struct { int H; int W; const char * name; } cases[] = { |
| 189 | {288, 288, "pe_288"}, |
| 190 | {144, 144, "pe_144"}, |
| 191 | { 72, 72, "pe_72"}, |
| 192 | { 36, 36, "pe_36"}, |
| 193 | }; |
| 194 | |
| 195 | for (auto & tc : cases) { |
| 196 | auto ref = load_ref(ref_dir + "/" + tc.name); |
| 197 | if (ref.data.empty()) continue; |
| 198 | |
| 199 | const int H = tc.H, W = tc.W, d_model = 256; |
| 200 | const int half = d_model / 2; // 128 |
| 201 | const float scale = 2.0f * (float)M_PI; |
| 202 | const float temperature = 10000.0f; |
| 203 | |
| 204 | // Match Python PositionEmbeddingSine.forward() exactly: |
| 205 | // y_embed = arange(1, H+1) / (H + eps) * scale |
| 206 | // dim_t = temperature ** (2 * (arange(half) // 2) / half) |
| 207 | // pos_y = y_embed / dim_t |
| 208 | // stack(sin(even), cos(odd)).flatten → interleaved sin/cos |
| 209 | // Output: [1, 256, H, W] (NCHW) |
| 210 | |
| 211 | std::vector<float> our_pe(d_model * H * W); |
| 212 | const float eps = 1e-6f; |
| 213 | |
| 214 | for (int y = 0; y < H; ++y) { |
| 215 | for (int x = 0; x < W; ++x) { |
| 216 | float pos_y = ((float)(y + 1) / ((float)H + eps)) * scale; |
| 217 | float pos_x = ((float)(x + 1) / ((float)W + eps)) * scale; |
| 218 | |
| 219 | for (int i = 0; i < half; ++i) { |
| 220 | int paired = (i / 2) * 2; // 0,0,2,2,4,4,... |
| 221 | float dim_t = powf(temperature, (float)paired / (float)half); |
| 222 | |
| 223 | float val_x, val_y; |
| 224 | if (i % 2 == 0) { |
| 225 | val_x = sinf(pos_x / dim_t); |
| 226 | val_y = sinf(pos_y / dim_t); |
| 227 | } else { |
| 228 | val_x = cosf(pos_x / dim_t); |
| 229 | val_y = cosf(pos_y / dim_t); |
| 230 | } |
| 231 | |
| 232 | // PyTorch output is [1, 256, H, W] |
| 233 | // Channel layout: first 128 = pos_y, next 128 = pos_x |
| 234 | our_pe[i * H * W + y * W + x] = val_y; |
| 235 | our_pe[(i + half) * H * W + y * W + x] = val_x; |
| 236 | } |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | check(tc.name, our_pe.data(), ref, 1e-5f, n_pass, n_fail); |