/* SPDX-License-Identifier: AGPL-3.0-or-later */ /* Copyright (C) 2026 David (Corey) Herrera */ /** * @file test_rope_ops.c * @brief OFF-HARDWARE gate for ROPE — partial NeoX rotary position embedding. Runs without * a card, and is what has to be green before the hardware pass is worth a core. * * --------------------------------------------------------------------------- * WHAT THIS CAN PROVE, AND WHAT IT CANNOT * --------------------------------------------------------------------------- * It CAN prove: * - the host and the KERNEL agree on every wire constant, by PARSING the device header * rather than by a comment claiming they match (dev notes §4); * - the parameter block puts every field in the word the kernel reads; * - the TABLE is tilized with the same face map the activations use — checked element by * element through ft_tt_tile_face_index(), which is the only check that can see it, * because a face-permuted table is a wrong ANGLE and not a permuted answer; * - the ANGLE is right, read directly out of a rotated basis vector rather than inferred * from a correlation; * - the ARRANGEMENT OF PASSES computes the op, and clears PCC >= 0.99 with bf16 at every * circular-buffer boundary — a prediction of the card's score; * - the test DATA can see a swapped pair, asserted rather than assumed. * * It CANNOT prove the kernel implements the model. Nothing off-hardware can (dev notes §3: * "off-hardware suites cannot cover the copy path"), and here it cannot cover the FPU, the * packer, the real face order or the CB credit protocol either. * * --------------------------------------------------------------------------- * TWO KINDS OF CHECK, ON PURPOSE * --------------------------------------------------------------------------- * A rotation preserves the norm, so no norm-based check can see a wrong angle * (docs/tt_bringup_log.md §22.2 — the general form of the softmax shift-invariance trap). * And PCC is blind to a consistent permutation (§9), which is exactly the error a face-order * or partner-order mistake produces. So: * - STRUCTURE is scored by BYTES, against a synthetic table of exactly-representable * values where every intermediate is exact in bf16, so the comparison has no tolerance * to hide in; * - the ANGLE is scored SEPARATELY and explicitly, by rotating (1, 0) and reading (cos, * sin) straight out of the answer. * * Environment: * FT_TT_KERNEL_DIR where the device headers live (default ../kernels) * * @status None * @issues None * @todo None */ #include #include #include #include #include "../convert/ft_float_convert.h" #include "../ft_tt_op_dispatch.h" #include "../ft_tt_op_l1_layout.h" #include "../ft_tt_op_rope.h" #include "../ft_tt_queue.h" #include "../ft_tt_tilize.h" #include "ft_tt_rope_reference.h" #include "ft_tt_test_report.h" #define PCC_ACCEPT 0.99 /** Where the device headers live, so the wire-constant pin reads the real file. */ #define KERNEL_DIRECTORY_ENVIRONMENT "FT_TT_KERNEL_DIR" #define KERNEL_DIRECTORY_DEFAULT "../kernels" #define PATH_MAX_BYTES 512u #define TILE_DIM FT_TT_OP_TILE_DIM #define TILE_BYTES FT_TT_OP_TILE_BYTES_BF16 /** MiniCPM5-1B, read from the GGUF header on the card host. */ #define MODEL_HEAD_DIM FT_TT_OP_ROPE_MINICPM5_HEAD_DIM #define MODEL_ROPE_DIM FT_TT_OP_ROPE_MINICPM5_ROPE_DIM #define MODEL_THETA FT_TT_OP_ROPE_MINICPM5_THETA static unsigned char g_cb_buffer[FT_TT_NUM_CIRCULAR_BUFFERS * FT_TT_WORDS_PER_LOCAL_CB_CONFIG * 4]; /* ================================================================ * Reading a constant out of a device header * ================================================================ */ /** The value of `enum { = };` in `path` -- the C/C++-shared form the * device headers use since plan Phase 6.6, read out of the real header rather than * retyped here. A missing constant is a * FAILURE and never a default — a default would pass against a header that no longer * declares the field. */ static int read_device_constant(const char *path, const char *name, uint32_t *out_value) { char line[512]; FILE *file = fopen(path, "r"); int found = -1; if (!file) return -1; while (fgets(line, sizeof(line), file)) { char parsed_name[128]; char parsed_value[64]; if (sscanf(line, " enum { %127[A-Za-z0-9_] = %63[^}] };", parsed_name, parsed_value) != 2) continue; if (strcmp(parsed_name, name) != 0) continue; *out_value = (uint32_t)strtoul(parsed_value, NULL, 0); found = 0; break; } fclose(file); return found; } static void device_header_path(char *out, size_t out_bytes, const char *file) { const char *directory = getenv(KERNEL_DIRECTORY_ENVIRONMENT); if (!directory || directory[0] == '\0') directory = KERNEL_DIRECTORY_DEFAULT; snprintf(out, out_bytes, "%s/%s", directory, file); } static void check_device_constant(const char *file, const char *name, uint32_t expected) { char path[PATH_MAX_BYTES]; uint32_t value = 0xFFFFFFFFu; char label[160]; int rc; device_header_path(path, sizeof(path), file); rc = read_device_constant(path, name, &value); snprintf(label, sizeof(label), "%s::%s is the host's value", file, name); ft_tt_test_check_fmt(label, rc == 0 && value == expected, "device %u, host %u (rc=%d)", value, expected, rc); } static void test_wire_constants(void) { ft_tt_test_step("the host and the KERNEL agree on every wire word — PARSED, not claimed"); check_device_constant("resident_rope_protocol.h", "kRopeTileCount", (uint32_t)FT_TT_OP_ROPE_TILE_COUNT); check_device_constant("resident_rope_protocol.h", "kRopeHeadTiles", (uint32_t)FT_TT_OP_ROPE_HEAD_TILES); check_device_constant("resident_rope_protocol.h", "kRopeRotTiles", (uint32_t)FT_TT_OP_ROPE_ROT_TILES); check_device_constant("resident_rope_protocol.h", "kRopeHeads", (uint32_t)FT_TT_OP_ROPE_HEADS); check_device_constant("resident_rope_protocol.h", "kRopeDepth", (uint32_t)FT_TT_OP_ROPE_DEPTH); check_device_constant("resident_rope_protocol.h", "kRopeAX", (uint32_t)FT_TT_OP_ROPE_A_X); check_device_constant("resident_rope_protocol.h", "kRopeAY", (uint32_t)FT_TT_OP_ROPE_A_Y); check_device_constant("resident_rope_protocol.h", "kRopeABase", (uint32_t)FT_TT_OP_ROPE_A_BASE); check_device_constant("resident_rope_protocol.h", "kRopeTableX", (uint32_t)FT_TT_OP_ROPE_TABLE_X); check_device_constant("resident_rope_protocol.h", "kRopeTableY", (uint32_t)FT_TT_OP_ROPE_TABLE_Y); check_device_constant("resident_rope_protocol.h", "kRopeTableBase", (uint32_t)FT_TT_OP_ROPE_TABLE_BASE); check_device_constant("resident_rope_protocol.h", "kRopeCX", (uint32_t)FT_TT_OP_ROPE_C_X); check_device_constant("resident_rope_protocol.h", "kRopeCY", (uint32_t)FT_TT_OP_ROPE_C_Y); check_device_constant("resident_rope_protocol.h", "kRopeCBase", (uint32_t)FT_TT_OP_ROPE_C_BASE); check_device_constant("resident_rope_protocol.h", "kRopeWords", (uint32_t)FT_TT_OP_ROPE_WORDS); check_device_constant("resident_queue_protocol.h", "kOpRope", (uint32_t)FT_TT_QUEUE_OP_ROPE); ft_tt_test_check_fmt("the parameter block fits one queue slot", (uint32_t)FT_TT_OP_ROPE_WORDS == FT_TT_QUEUE_PARAM_WORDS, "%u vs %u", (uint32_t)FT_TT_OP_ROPE_WORDS, FT_TT_QUEUE_PARAM_WORDS); } /* ================================================================ * The parameter block * ================================================================ */ /** A request with a distinct value in every field, so a field written into the wrong word is * visible rather than plausible. */ static void fill_request(ft_tt_op_rope_request *request, uint32_t row_blocks, uint32_t heads, uint32_t head_dim, uint32_t rope_dim, uint32_t first_position) { memset(request, 0, sizeof(*request)); request->row_blocks = row_blocks; request->heads = heads; request->head_dim = head_dim; request->rope_dim = rope_dim; request->first_position = first_position; request->prefetch_tiles = 2u; request->input.x = 11u; request->input.y = 12u; request->input.address = 0x00100000u; request->input.tile_bytes = TILE_BYTES; request->table.x = 21u; request->table.y = 22u; request->table.address = 0x00200000u; request->table.tile_bytes = TILE_BYTES; request->output.x = 31u; request->output.y = 32u; request->output.address = 0x00300000u; request->output.tile_bytes = TILE_BYTES; } static void test_parameter_block(void) { ft_tt_op_rope_request request; uint32_t words[FT_TT_OP_ROPE_WORDS]; uint32_t expected_table; int rc; ft_tt_test_step("the parameter block puts every field in the word the kernel reads"); fill_request(&request, 2u, 16u, MODEL_HEAD_DIM, MODEL_ROPE_DIM, 64u); rc = ft_tt_op_rope_parameters(&request, words); ft_tt_test_check_fmt("MiniCPM5-1B Q, 64 tokens, encodes", rc == FT_TT_OK, "rc=%d", rc); ft_tt_test_check_fmt("tile_count = row_blocks * heads * head_tiles", words[FT_TT_OP_ROPE_TILE_COUNT] == 2u * 16u * 4u, "%u", words[FT_TT_OP_ROPE_TILE_COUNT]); ft_tt_test_check_fmt("head_tiles = head_dim / 32", words[FT_TT_OP_ROPE_HEAD_TILES] == MODEL_HEAD_DIM / TILE_DIM, "%u", words[FT_TT_OP_ROPE_HEAD_TILES]); ft_tt_test_check_fmt("rot_tiles = rot / 32", words[FT_TT_OP_ROPE_ROT_TILES] == MODEL_ROPE_DIM / TILE_DIM, "%u", words[FT_TT_OP_ROPE_ROT_TILES]); ft_tt_test_check_fmt("heads travels", words[FT_TT_OP_ROPE_HEADS] == 16u, "%u", words[FT_TT_OP_ROPE_HEADS]); ft_tt_test_check_fmt("the activation coordinate travels", words[FT_TT_OP_ROPE_A_X] == 11u && words[FT_TT_OP_ROPE_A_Y] == 12u && words[FT_TT_OP_ROPE_A_BASE] == 0x00100000u, "(%u,%u)+0x%X", words[FT_TT_OP_ROPE_A_X], words[FT_TT_OP_ROPE_A_Y], words[FT_TT_OP_ROPE_A_BASE]); ft_tt_test_check_fmt("the output coordinate travels", words[FT_TT_OP_ROPE_C_X] == 31u && words[FT_TT_OP_ROPE_C_Y] == 32u && words[FT_TT_OP_ROPE_C_BASE] == 0x00300000u, "(%u,%u)+0x%X", words[FT_TT_OP_ROPE_C_X], words[FT_TT_OP_ROPE_C_Y], words[FT_TT_OP_ROPE_C_BASE]); /* The driver ADDS the row-block offset so a caller cannot forget to. Position 64 is row * block 2, and a row block is 2*rot_tiles tiles. */ expected_table = 0x00200000u + 2u * (2u * (MODEL_ROPE_DIM / TILE_DIM)) * TILE_BYTES; ft_tt_test_check_fmt("the table base is advanced to the position's row block", words[FT_TT_OP_ROPE_TABLE_BASE] == expected_table, "0x%X vs 0x%X", words[FT_TT_OP_ROPE_TABLE_BASE], expected_table); ft_tt_test_check_fmt("the depth divides head_tiles AND rot_tiles", words[FT_TT_OP_ROPE_DEPTH] != 0u && (words[FT_TT_OP_ROPE_HEAD_TILES] % words[FT_TT_OP_ROPE_DEPTH]) == 0u && (words[FT_TT_OP_ROPE_ROT_TILES] % words[FT_TT_OP_ROPE_DEPTH]) == 0u, "depth %u", words[FT_TT_OP_ROPE_DEPTH]); } static void test_refusals(void) { ft_tt_op_rope_request request; uint32_t words[FT_TT_OP_ROPE_WORDS]; ft_tt_test_step("the shapes the FACE-SPACE argument cannot express are REFUSED"); /* rot = 96 -> rot_tiles 3, half = 48 features = one and a HALF tiles, so the partner * element sits in a different face COLUMN. Buildable with a face-granular gather; not * built; refused rather than approximated. */ fill_request(&request, 1u, 2u, 128u, 96u, 0u); ft_tt_test_check_fmt("an odd rot_tiles (half lands mid-tile) is refused", ft_tt_op_rope_parameters(&request, words) == FT_TT_EINVAL, "rot 96"); /* rot = 48 -> not even a whole number of tiles. */ fill_request(&request, 1u, 2u, 128u, 48u, 0u); ft_tt_test_check_fmt("a rot that is not a multiple of 32 is refused", ft_tt_op_rope_parameters(&request, words) == FT_TT_EINVAL, "rot 48"); fill_request(&request, 1u, 2u, 100u, 64u, 0u); ft_tt_test_check_fmt("a head_dim that is not a multiple of 32 is refused", ft_tt_op_rope_parameters(&request, words) == FT_TT_EINVAL, "head_dim 100"); /* THE ROW-PHASE CONTRACT: one row block may start anywhere (decode), several may not. */ fill_request(&request, 1u, 2u, 128u, 128u, 7u); ft_tt_test_check_fmt("ONE row block at an unaligned position is ACCEPTED (decode)", ft_tt_op_rope_parameters(&request, words) == FT_TT_OK, "position 7"); fill_request(&request, 2u, 2u, 128u, 128u, 7u); ft_tt_test_check_fmt("TWO row blocks at an unaligned position are refused", ft_tt_op_rope_parameters(&request, words) == FT_TT_EINVAL, "position 7, 2 blocks"); fill_request(&request, 0u, 2u, 128u, 128u, 0u); ft_tt_test_check_fmt("a zero row-block count is refused", ft_tt_op_rope_parameters(&request, words) == FT_TT_EINVAL, "row_blocks 0"); ft_tt_test_check_fmt("rope_dim 0 means the whole head", ft_tt_op_rope_rotary_width(128u, 0u) == 128u, "%u", ft_tt_op_rope_rotary_width(128u, 0u)); ft_tt_test_check_fmt("rope_dim past head_dim means the whole head", ft_tt_op_rope_rotary_width(128u, 256u) == 128u, "%u", ft_tt_op_rope_rotary_width(128u, 256u)); ft_tt_test_check_fmt("rope_dim inside the head is the partial case", ft_tt_op_rope_rotary_width(256u, 64u) == 64u, "%u", ft_tt_op_rope_rotary_width(256u, 64u)); } /* ================================================================ * Circular buffers * ================================================================ */ static void test_cb_placement(void) { ft_tt_op_rope_request request; ft_tt_launch_cb_entry entries[FT_TT_OP_ROPE_CB_COUNT]; uint64_t mask = 0; unsigned i; unsigned j; bool disjoint = true; bool inside = true; int rc; ft_tt_test_step("the seven circular buffers fit the arena and do not overlap"); fill_request(&request, 1u, 16u, MODEL_HEAD_DIM, MODEL_ROPE_DIM, 0u); request.prefetch_tiles = 4u; rc = ft_tt_op_rope_place_cbs(&request, entries, g_cb_buffer, sizeof(g_cb_buffer), &mask); ft_tt_test_check_fmt("the placement succeeds", rc == FT_TT_OK, "rc=%d", rc); if (rc != FT_TT_OK) return; for (i = 0; i < FT_TT_OP_ROPE_CB_COUNT; i++) { const uint32_t start = entries[i].address; const uint32_t end = start + entries[i].size_bytes; if (start < FT_TT_OP_CB_ARENA_BASE || end > FT_TT_OP_CB_ARENA_END) inside = false; for (j = i + 1u; j < FT_TT_OP_ROPE_CB_COUNT; j++) { const uint32_t other = entries[j].address; const uint32_t other_end = other + entries[j].size_bytes; if (start < other_end && other < end) disjoint = false; } } ft_tt_test_check("every buffer is inside the arena", inside, "arena 0x26000..0x170000"); ft_tt_test_check("no two buffers overlap", disjoint, "seven buffers"); ft_tt_test_check_fmt("the mask names seven buffers", mask == ((1ull << FT_TT_OP_ROPE_CB_IN) | (1ull << FT_TT_OP_ROPE_CB_ROT) | (1ull << FT_TT_OP_ROPE_CB_COS) | (1ull << FT_TT_OP_ROPE_CB_SIN) | (1ull << FT_TT_OP_ROPE_CB_PROD_COS) | (1ull << FT_TT_OP_ROPE_CB_PROD_SIN) | (1ull << FT_TT_OP_ROPE_CB_OUT)), "0x%llX", (unsigned long long)mask); } /* ================================================================ * The table * ================================================================ */ /** The table artifact, read the way the CARD will read it: tile `tile` of the strip, element * (row, column-within-tile), through ft_tt_tile_face_index(). */ static uint16_t table_element(const uint16_t *tiles, uint32_t row, uint32_t column) { const uint32_t tile = column / TILE_DIM; const uint32_t within = ft_tt_tile_face_index(row, column % TILE_DIM); return tiles[(size_t)tile * TILE_DIM * TILE_DIM + within]; } static void test_table_layout_and_angles(void) { const uint32_t rot = MODEL_ROPE_DIM; const uint32_t rot_tiles = rot / TILE_DIM; const uint32_t half = rot / 2u; const uint32_t blocks = 2u; const size_t bytes = ft_tt_op_rope_table_bytes(blocks * TILE_DIM, rot_tiles, TILE_BYTES); uint16_t *table = malloc(bytes); uint16_t *cos_plain = malloc((size_t)TILE_DIM * rot * sizeof(uint16_t)); uint16_t *sin_plain = malloc((size_t)TILE_DIM * rot * sizeof(uint16_t)); uint32_t block; size_t mismatches = 0; size_t sign_wrong = 0; double worst_angle = 0.0; int rc; ft_tt_test_step("the TABLE: the face map, the sign, and the angle"); if (!table || !cos_plain || !sin_plain) { ft_tt_test_check("host buffers allocate", false, "out of memory"); goto done; } rc = ft_tt_op_rope_build_table(0u, blocks * TILE_DIM, MODEL_HEAD_DIM, MODEL_ROPE_DIM, MODEL_THETA, table, bytes); ft_tt_test_check_fmt("the table builds", rc == FT_TT_OK, "rc=%d, %zu bytes", rc, bytes); if (rc != FT_TT_OK) goto done; /* THE FACE-MAP CHECK, and the only one that can see this failure. Every element of the * artifact is compared against an independently computed plain value AT THE OFFSET THE * CARD WILL READ IT FROM. A table tilized flat row-major passes no part of this, while a * PCC over the op's output would partly absorb it. */ for (block = 0; block < blocks; block++) { const uint16_t *cos_tiles = table + (size_t)block * 2u * rot_tiles * TILE_DIM * TILE_DIM; const uint16_t *sin_tiles = cos_tiles + (size_t)rot_tiles * TILE_DIM * TILE_DIM; uint32_t row; ft_tt_rope_reference_table(cos_plain, sin_plain, block * TILE_DIM, rot, MODEL_THETA); for (row = 0; row < TILE_DIM; row++) { uint32_t column; for (column = 0; column < rot; column++) { if (table_element(cos_tiles, row, column) != cos_plain[row * rot + column]) mismatches++; if (table_element(sin_tiles, row, column) != sin_plain[row * rot + column]) mismatches++; } } } ft_tt_test_check_fmt("every table element is where the FACE map puts it", mismatches == 0u, "%zu mismatches of %u", mismatches, blocks * TILE_DIM * rot * 2u); /* THE SIGN IS IN THE TABLE. Below the half it must be the NEGATIVE sine, above it the * positive one — that is what removes the branch from the kernel, so it is a property * worth a check of its own rather than one buried in an end-to-end score. */ { uint32_t row; for (row = 1u; row < TILE_DIM; row++) { uint32_t j; for (j = 1u; j < half; j++) { const float low = ft_float_from_bf16( table_element(table + (size_t)rot_tiles * TILE_DIM * TILE_DIM, row, j)); const float high = ft_float_from_bf16(table_element( table + (size_t)rot_tiles * TILE_DIM * TILE_DIM, row, j + half)); if (low != -high) sign_wrong++; } } } ft_tt_test_check_fmt("the sin table is NEGATED below the half and positive above", sign_wrong == 0u, "%zu columns disagree", sign_wrong); /* THE ANGLE ITSELF, against a second, independently written formula. A rotation * preserves the norm, so nothing downstream of here can see a wrong angle. */ { uint32_t row; for (row = 0; row < TILE_DIM; row++) { uint32_t j; for (j = 0; j < half; j += 7u) { const double angle = ft_tt_op_rope_angle(row, j, rot, MODEL_THETA); const double independent = (double)row * pow(MODEL_THETA, -2.0 * (double)j / (double)rot); const double difference = fabs(angle - independent); if (difference > worst_angle) worst_angle = difference; } } } ft_tt_test_check_fmt("the angle matches an independently written formula", worst_angle == 0.0, "worst |difference| = %.3g", worst_angle); /* Position 0 is the identity: every angle is 0, so cos is 1 and sin is 0. It is the * control that says the table is not simply constant. */ { const float cosine = ft_float_from_bf16(table_element(table, 0u, 5u)); const float sine = ft_float_from_bf16( table_element(table + (size_t)rot_tiles * TILE_DIM * TILE_DIM, 0u, 5u)); const float moved = ft_float_from_bf16(table_element(table, 3u, 5u)); ft_tt_test_check_fmt("position 0 is the identity rotation", cosine == 1.0f && sine == 0.0f, "cos %g sin %g", (double)cosine, (double)sine); ft_tt_test_check_fmt("and a later row is NOT the identity", moved != 1.0f, "cos %g", (double)moved); } ft_tt_test_check_fmt("the whole MiniCPM5-1B context table is 67.1 MB", ft_tt_op_rope_table_bytes(FT_TT_OP_ROPE_MINICPM5_CONTEXT, rot_tiles, TILE_BYTES) == 67108864u, "%zu bytes", ft_tt_op_rope_table_bytes(FT_TT_OP_ROPE_MINICPM5_CONTEXT, rot_tiles, TILE_BYTES)); ft_tt_test_check_fmt("the row-block offset indexes by position / 32", ft_tt_op_rope_table_offset(100u, rot_tiles, TILE_BYTES) == 3u * 2u * rot_tiles * TILE_BYTES, "0x%X", ft_tt_op_rope_table_offset(100u, rot_tiles, TILE_BYTES)); done: free(table); free(cos_plain); free(sin_plain); } /* ================================================================ * The op itself, through the device model * ================================================================ */ static double pearson(const float *a, const float *b, size_t count) { double mean_a = 0.0; double mean_b = 0.0; double covariance = 0.0; double variance_a = 0.0; double variance_b = 0.0; size_t index; for (index = 0; index < count; index++) { mean_a += a[index]; mean_b += b[index]; } mean_a /= (double)count; mean_b /= (double)count; for (index = 0; index < count; index++) { const double da = a[index] - mean_a; const double db = b[index] - mean_b; covariance += da * db; variance_a += da * da; variance_b += db * db; } if (variance_a == 0.0 || variance_b == 0.0) return 0.0; return covariance / sqrt(variance_a * variance_b); } static double max_abs_difference(const float *a, const float *b, size_t count) { double worst = 0.0; size_t index; for (index = 0; index < count; index++) { const double difference = fabs((double)a[index] - (double)b[index]); if (difference > worst) worst = difference; } return worst; } static uint64_t g_random_state = 0x9E3779B97F4A7C15ull; static float random_value(void) { /* Full-mantissa, in [-2, 2). Never small integers: this suite is about layout as much as * arithmetic, and a bf16-exact array cannot tell two layouts apart (dev notes §3). */ g_random_state = g_random_state * 6364136223846793005ull + 1442695040888963407ull; return ((float)(int32_t)(g_random_state >> 33) / 1073741824.0f); } /** * Run the device model over one row block and score it against the independent reference. * * `swap_partner` computes the WRONG pairing on purpose, so the suite can assert its own data * is able to see a swapped pair rather than assuming so. */ static void model_one_shape(uint32_t heads, uint32_t head_dim, uint32_t rope_dim, uint32_t position, const char *what) { const uint32_t rot = ft_tt_op_rope_rotary_width(head_dim, rope_dim); const uint32_t rows = TILE_DIM; const size_t width = (size_t)heads * head_dim; const size_t count = (size_t)rows * width; float *source = malloc(count * sizeof(float)); float *expected = malloc(count * sizeof(float)); float *actual = malloc(count * sizeof(float)); float *swapped_actual = malloc(count * sizeof(float)); uint16_t *in = malloc(count * sizeof(uint16_t)); uint16_t *out = malloc(count * sizeof(uint16_t)); uint16_t *swapped_out = malloc(count * sizeof(uint16_t)); uint16_t *cos_plain = malloc((size_t)rows * rot * sizeof(uint16_t)); uint16_t *sin_plain = malloc((size_t)rows * rot * sizeof(uint16_t)); uint16_t *swapped_sin = malloc((size_t)rows * rot * sizeof(uint16_t)); size_t index; size_t untouched_wrong = 0; char label[192]; if (!source || !expected || !actual || !swapped_actual || !in || !out || !swapped_out || !cos_plain || !sin_plain || !swapped_sin) { ft_tt_test_check("host buffers allocate", false, "out of memory"); goto done; } for (index = 0; index < count; index++) { source[index] = random_value(); in[index] = ft_bf16_from_float(source[index]); source[index] = ft_float_from_bf16(in[index]); /* what the card will actually see */ } ft_tt_rope_reference_table(cos_plain, sin_plain, position, rot, MODEL_THETA); ft_tt_rope_model_apply(in, cos_plain, sin_plain, out, rows, heads, head_dim, rot); for (index = 0; index < count; index++) actual[index] = ft_float_from_bf16(out[index]); ft_tt_rope_reference_apply(source, expected, rows, heads, head_dim, rope_dim, position, (float)MODEL_THETA); snprintf(label, sizeof(label), "%s: the model computes RoPE (PCC vs an fp32 reference)", what); ft_tt_test_check_fmt(label, pearson(expected, actual, count) >= PCC_ACCEPT, "PCC %.9f, max abs %.6g", pearson(expected, actual, count), max_abs_difference(expected, actual, count)); /* PARTIAL: everything past `rot` must come through BIT-identical, not merely close. */ if (rot < head_dim) { uint32_t row; for (row = 0; row < rows; row++) { uint32_t head; for (head = 0; head < heads; head++) { uint32_t column; for (column = rot; column < head_dim; column++) { const size_t at = (size_t)row * width + (size_t)head * head_dim + column; if (out[at] != in[at]) untouched_wrong++; } } } snprintf(label, sizeof(label), "%s: the tiles past the rotary dimension pass through " "BIT-identical", what); ft_tt_test_check_fmt(label, untouched_wrong == 0u, "%zu of %zu wrong", untouched_wrong, (size_t)rows * heads * (head_dim - rot)); } /* CAN THIS DATA SEE A SWAPPED PAIR? Asserted, not assumed. The wrong pairing is applied * by negating the table's sign convention, which is exactly what swapping the two halves * of the partner stream does to the answer. If the operands were symmetric or the data * were small integers this would come back "no difference" and the whole byte-comparison * gate would be worthless. */ for (index = 0; index < (size_t)rows * rot; index++) swapped_sin[index] = ft_bf16_from_float(-ft_float_from_bf16(sin_plain[index])); ft_tt_rope_model_apply(in, cos_plain, swapped_sin, swapped_out, rows, heads, head_dim, rot); for (index = 0; index < count; index++) swapped_actual[index] = ft_float_from_bf16(swapped_out[index]); snprintf(label, sizeof(label), "%s: the DATA can see a wrong pairing", what); ft_tt_test_check_fmt(label, max_abs_difference(actual, swapped_actual, count) > 0.01, "max abs difference %.6g", max_abs_difference(actual, swapped_actual, count)); done: free(source); free(expected); free(actual); free(swapped_actual); free(in); free(out); free(swapped_out); free(cos_plain); free(sin_plain); free(swapped_sin); } /** * THE ANGLE, READ STRAIGHT OUT OF THE ANSWER. * * Rotate the basis pair (1, 0): the output IS (cos, sin). No correlation, no norm, no * tolerance for a wrong position or a wrong theta to hide in — §22.2's rule applied to the * op whose invariance is the rotation itself. */ static void test_angle_is_read_directly(uint32_t position) { const uint32_t head_dim = MODEL_HEAD_DIM; const uint32_t rot = MODEL_ROPE_DIM; const uint32_t half = rot / 2u; const uint32_t rows = TILE_DIM; const uint32_t row = position % TILE_DIM; const uint32_t block_first = position - row; uint16_t *in = calloc((size_t)rows * head_dim, sizeof(uint16_t)); uint16_t *out = calloc((size_t)rows * head_dim, sizeof(uint16_t)); uint16_t *cos_plain = malloc((size_t)rows * rot * sizeof(uint16_t)); uint16_t *sin_plain = malloc((size_t)rows * rot * sizeof(uint16_t)); double worst_cos = 0.0; double worst_sin = 0.0; uint32_t j; char label[160]; if (!in || !out || !cos_plain || !sin_plain) { ft_tt_test_check("host buffers allocate", false, "out of memory"); goto done; } /* x[j] = 1, x[j+half] = 0, for every j — one head, one meaningful row. */ for (j = 0; j < half; j++) in[(size_t)row * head_dim + j] = ft_bf16_from_float(1.0f); ft_tt_rope_reference_table(cos_plain, sin_plain, block_first, rot, MODEL_THETA); ft_tt_rope_model_apply(in, cos_plain, sin_plain, out, rows, 1u, head_dim, rot); for (j = 0; j < half; j++) { const double frequency = pow(MODEL_THETA, -2.0 * (double)j / (double)rot); const double angle = (double)position * frequency; const double got_cos = ft_float_from_bf16(out[(size_t)row * head_dim + j]); const double got_sin = ft_float_from_bf16(out[(size_t)row * head_dim + j + half]); const double cos_error = fabs(got_cos - cos(angle)); const double sin_error = fabs(got_sin - sin(angle)); if (cos_error > worst_cos) worst_cos = cos_error; if (sin_error > worst_sin) worst_sin = sin_error; } /* 2^-8 is bf16's step at 1.0, and the value passes through two bf16 roundings, so ~0.008 * is the floor a correct implementation can reach. A wrong POSITION or a wrong theta * misses by O(1), not by O(2^-8). */ snprintf(label, sizeof(label), "position %u: rotating (1,0) returns (cos, sin)", position); ft_tt_test_check_fmt(label, worst_cos < 0.01 && worst_sin < 0.01, "worst |dcos| %.5f, |dsin| %.5f", worst_cos, worst_sin); done: free(in); free(out); free(cos_plain); free(sin_plain); } /** * A position ONE row later must produce a different answer. * * The mutation this exists for is "the table row is always row 0", which every score above * would survive at small positions because the angles are nearly equal there — and which is * the exact bug the row-phase contract invites. */ static void test_position_is_actually_used(void) { const uint32_t rot = MODEL_ROPE_DIM; const uint32_t half = rot / 2u; uint16_t *cos_plain = malloc((size_t)TILE_DIM * rot * sizeof(uint16_t)); uint16_t *sin_plain = malloc((size_t)TILE_DIM * rot * sizeof(uint16_t)); double worst = 0.0; uint32_t j; ft_tt_test_step("the POSITION reaches the answer — row by row, not just block by block"); if (!cos_plain || !sin_plain) { ft_tt_test_check("host buffers allocate", false, "out of memory"); goto done; } ft_tt_rope_reference_table(cos_plain, sin_plain, 0u, rot, MODEL_THETA); for (j = 0; j < half; j++) { const double row_0 = ft_float_from_bf16(cos_plain[j]); const double row_1 = ft_float_from_bf16(cos_plain[rot + j]); const double difference = fabs(row_0 - row_1); if (difference > worst) worst = difference; } ft_tt_test_check_fmt("consecutive table rows differ", worst > 0.1, "largest row-to-row |dcos| = %.4f", worst); done: free(cos_plain); free(sin_plain); } /* ================================================================ */ int main(void) { ft_tt_test_step("RoPE — OFF-HARDWARE gate"); test_wire_constants(); test_parameter_block(); test_refusals(); test_cb_placement(); test_table_layout_and_angles(); ft_tt_test_step("the op, through the device model"); model_one_shape(FT_TT_OP_ROPE_MINICPM5_HEADS_Q, MODEL_HEAD_DIM, MODEL_ROPE_DIM, 0u, "MiniCPM5-1B Q at position 0"); model_one_shape(FT_TT_OP_ROPE_MINICPM5_HEADS_KV, MODEL_HEAD_DIM, MODEL_ROPE_DIM, 4096u, "MiniCPM5-1B KV at position 4096"); /* The PARTIAL case MiniCPM5-1B does not exercise: 64 of 256, which is Qwen3.6's shape. */ model_one_shape(2u, 256u, 64u, 1024u, "partial, 64 of 256"); ft_tt_test_step("the ANGLE, read out of the answer rather than correlated with it"); test_angle_is_read_directly(0u); test_angle_is_read_directly(7u); test_angle_is_read_directly(100u); test_angle_is_read_directly(131040u); test_position_is_actually_used(); return ft_tt_test_finish(); }