| 116 | // ═══════════════════════════════════════════════════════════════════════════════ |
| 117 | |
| 118 | static void test_rope(const std::string & ref_dir, int & n_pass, int & n_fail) { |
| 119 | fprintf(stderr, "\n╔══════════════════════════════════════════╗\n"); |
| 120 | fprintf(stderr, "║ Test: RoPE Frequencies ║\n"); |
| 121 | fprintf(stderr, "╚══════════════════════════════════════════╝\n"); |
| 122 | |
| 123 | struct { int end; float scale; const char * name; } cases[] = { |
| 124 | {24, 1.0f, "rope_window_real"}, |
| 125 | {72, 24.0f/72.0f, "rope_global_real"}, |
| 126 | }; |
| 127 | |
| 128 | const int dim = 64; |
| 129 | const float theta = 10000.0f; |
| 130 | const int half_dim = dim / 4; // 16 |
| 131 | |
| 132 | std::vector<float> freqs(half_dim); |
| 133 | for (int i = 0; i < half_dim; ++i) { |
| 134 | freqs[i] = 1.0f / powf(theta, (float)(i * 4) / (float)dim); |
| 135 | } |
| 136 | |
| 137 | for (auto & tc : cases) { |
| 138 | auto ref = load_ref(ref_dir + "/" + tc.name); |
| 139 | if (ref.data.empty()) continue; |
| 140 | |
| 141 | const int N = tc.end * tc.end; |
| 142 | std::vector<float> our_rope(N * dim); |
| 143 | |
| 144 | for (int idx = 0; idx < N; ++idx) { |
| 145 | float t_x = (float)(idx % tc.end) * tc.scale; |
| 146 | float t_y = static_cast<float>(idx / tc.end) * tc.scale; // NOLINT(bugprone-integer-division) |
| 147 | |
| 148 | for (int i = 0; i < half_dim; ++i) { |
| 149 | float angle_x = t_x * freqs[i]; |
| 150 | our_rope[idx * dim + i * 2 + 0] = cosf(angle_x); |
| 151 | our_rope[idx * dim + i * 2 + 1] = sinf(angle_x); |
| 152 | } |
| 153 | for (int i = 0; i < half_dim; ++i) { |
| 154 | float angle_y = t_y * freqs[i]; |
| 155 | our_rope[idx * dim + half_dim * 2 + i * 2 + 0] = cosf(angle_y); |
| 156 | our_rope[idx * dim + half_dim * 2 + i * 2 + 1] = sinf(angle_y); |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | check(tc.name, our_rope.data(), ref, 1e-5f, n_pass, n_fail); |
| 161 | } |
| 162 | |
| 163 | // Also compare against checkpoint freqs_cis to verify conversion is lossless |
| 164 | for (int bi : {0, 7}) { |
| 165 | std::string name = "ckpt_freqs_cis_block" + std::to_string(bi); |
| 166 | auto ref_ckpt = load_ref(ref_dir + "/" + name); |
| 167 | auto ref_computed = load_ref(ref_dir + "/" + (bi == 0 ? "rope_window_real" : "rope_global_real")); |
| 168 | if (!ref_ckpt.data.empty() && !ref_computed.data.empty()) { |
| 169 | auto r = compare_tensors(ref_ckpt.data.data(), ref_computed.data.data(), |
| 170 | std::min(ref_ckpt.numel(), ref_computed.numel()), 1e-6f); |
| 171 | bool ok = r.max_diff <= 1e-6f; |
| 172 | fprintf(stderr, " %s ckpt vs computed block %d: max=%.6e\n", |
| 173 | ok ? "[PASS]" : "[FAIL]", bi, r.max_diff); |
| 174 | if (ok) n_pass++; else n_fail++; |
| 175 | } |
no test coverage detected