Forward-declare the internal function (defined in sam3.cpp) We'll test it by replicating the computation here.
| 121 | // Forward-declare the internal function (defined in sam3.cpp) |
| 122 | // We'll test it by replicating the computation here. |
| 123 | static bool test_rope(const std::string & ref_dir) { |
| 124 | fprintf(stderr, "\n=== Test: RoPE ===\n"); |
| 125 | |
| 126 | // Test window RoPE (24x24) |
| 127 | auto ref_window = load_ref(ref_dir + "/rope_window_real"); |
| 128 | if (!ref_window.data.empty()) { |
| 129 | // ref is [576, 32, 2] — (cos, sin) pairs |
| 130 | // Our compute_axial_cis produces [N, head_dim] with interleaved (cos, sin) |
| 131 | // But the Python ref stores them as [N, half_head, 2] where [:,:,0]=cos, [:,:,1]=sin |
| 132 | const int N = 576; |
| 133 | const int half_head = 32; |
| 134 | const int dim = 64; |
| 135 | const float theta = 10000.0f; |
| 136 | const float scale_pos = 1.0f; |
| 137 | |
| 138 | std::vector<float> our_rope(N * dim); |
| 139 | |
| 140 | const int half_dim = dim / 4; // 16 |
| 141 | std::vector<float> freqs(half_dim); |
| 142 | for (int i = 0; i < half_dim; ++i) { |
| 143 | freqs[i] = 1.0f / powf(theta, (float)(i * 4) / dim); |
| 144 | } |
| 145 | |
| 146 | for (int idx = 0; idx < N; ++idx) { |
| 147 | float t_x = (float)(idx % 24) * scale_pos; |
| 148 | float t_y = (float)(idx / 24) * scale_pos; |
| 149 | |
| 150 | for (int i = 0; i < half_dim; ++i) { |
| 151 | float angle_x = t_x * freqs[i]; |
| 152 | our_rope[idx * dim + i * 2 + 0] = cosf(angle_x); |
| 153 | our_rope[idx * dim + i * 2 + 1] = sinf(angle_x); |
| 154 | } |
| 155 | for (int i = 0; i < half_dim; ++i) { |
| 156 | float angle_y = t_y * freqs[i]; |
| 157 | our_rope[idx * dim + half_dim * 2 + i * 2 + 0] = cosf(angle_y); |
| 158 | our_rope[idx * dim + half_dim * 2 + i * 2 + 1] = sinf(angle_y); |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | // Compare: ref is [N, 32, 2] = [N, half_head, (cos,sin)] |
| 163 | // Our layout is [N, dim] = [N, 64] with pairs (cos_x, sin_x, cos_x, sin_x, ..., cos_y, sin_y, ...) |
| 164 | // Need to rearrange comparison: |
| 165 | // ref[idx, j, 0] = cos of freq j for position idx |
| 166 | // ref[idx, j, 1] = sin of freq j for position idx |
| 167 | // Python: freqs_cis = cat([freqs_cis_x, freqs_cis_y], dim=-1) → [N, 32] |
| 168 | // freqs_cis_x has 16 complex values, freqs_cis_y has 16 complex values → total 32 |
| 169 | // view_as_real → [N, 32, 2] where [:,i,0]=cos, [:,i,1]=sin |
| 170 | // So ref[idx, j, 0] for j<16 = cos(t_x * freqs[j]) |
| 171 | // ref[idx, j, 1] for j<16 = sin(t_x * freqs[j]) |
| 172 | // ref[idx, j, 0] for j>=16 = cos(t_y * freqs[j-16]) |
| 173 | // ref[idx, j, 1] for j>=16 = sin(t_y * freqs[j-16]) |
| 174 | |
| 175 | // Our layout: [idx * 64 + j*2+0] = cos, [idx * 64 + j*2+1] = sin |
| 176 | // For x: j = 0..15 → our[idx*64 + j*2], ref[idx*64 + j*2] |
| 177 | // For y: j = 16..31 → our[idx*64 + 32 + (j-16)*2], ref[idx*64 + (j)*2] |
| 178 | |
| 179 | // Actually both are the same layout! ref is stored flat as [N*32*2] = [N*64] |
| 180 | // and our_rope is [N*64] with the same interleaving. |
no test coverage detected