132 lines
5.2 KiB
C
132 lines
5.2 KiB
C
/* SPDX-License-Identifier: AGPL-3.0-or-later */
|
|
/* Copyright (C) 2026 David (Corey) Herrera */
|
|
/**
|
|
* @file ft_tt_rope_reference.c
|
|
* @brief Implementation of the RoPE reference and the RoPE device model. See
|
|
* ft_tt_rope_reference.h for why they are two functions and not one.
|
|
*
|
|
* Nothing here includes ft_tt_op_rope.h. That is the point: the reference is written from
|
|
* the CUDA kernel and the CPU oracle, so a shared mistake between the driver and its own
|
|
* check is impossible rather than unlikely.
|
|
*
|
|
* @status None
|
|
* @issues None
|
|
* @todo None
|
|
*/
|
|
|
|
#include "ft_tt_rope_reference.h"
|
|
|
|
#include <math.h>
|
|
|
|
#include "../convert/ft_float_convert.h"
|
|
|
|
void ft_tt_rope_reference_row(float *row, uint32_t heads, uint32_t head_dim,
|
|
uint32_t rope_dim, uint32_t position, float theta_base)
|
|
{
|
|
const uint32_t rot = (rope_dim > 0u && rope_dim <= head_dim) ? rope_dim : head_dim;
|
|
const uint32_t half = rot / 2u;
|
|
uint32_t head;
|
|
|
|
for (head = 0; head < heads; head++) {
|
|
float *values = row + (size_t)head * head_dim;
|
|
uint32_t j;
|
|
|
|
for (j = 0; j < half; j++) {
|
|
/* fp32 powf/cosf/sinf, per j, exactly as rope.cu does it — so the reference has
|
|
* the SAME numerical shape as the K80 path this op has to match, rather than a
|
|
* better one that would make a real divergence look like a rounding. */
|
|
const float frequency =
|
|
1.0f / powf(theta_base, (float)(2u * j) / (float)rot);
|
|
const float angle = (float)position * frequency;
|
|
const float cosine = cosf(angle);
|
|
const float sine = sinf(angle);
|
|
const float low = values[j];
|
|
const float high = values[j + half];
|
|
|
|
values[j] = low * cosine - high * sine;
|
|
values[j + half] = low * sine + high * cosine;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ft_tt_rope_reference_apply(const float *in, float *out, uint32_t rows, uint32_t heads,
|
|
uint32_t head_dim, uint32_t rope_dim,
|
|
uint32_t block_first_position, float theta_base)
|
|
{
|
|
const size_t width = (size_t)heads * head_dim;
|
|
uint32_t row;
|
|
|
|
for (row = 0; row < rows; row++) {
|
|
size_t index;
|
|
|
|
for (index = 0; index < width; index++)
|
|
out[(size_t)row * width + index] = in[(size_t)row * width + index];
|
|
ft_tt_rope_reference_row(out + (size_t)row * width, heads, head_dim, rope_dim,
|
|
block_first_position + row, theta_base);
|
|
}
|
|
}
|
|
|
|
void ft_tt_rope_model_apply(const uint16_t *in, const uint16_t *cos_table,
|
|
const uint16_t *sin_table, uint16_t *out, uint32_t rows,
|
|
uint32_t heads, uint32_t head_dim, uint32_t rot)
|
|
{
|
|
const size_t width = (size_t)heads * head_dim;
|
|
const uint32_t half = rot / 2u;
|
|
uint32_t row;
|
|
|
|
for (row = 0; row < rows; row++) {
|
|
uint32_t head;
|
|
|
|
for (head = 0; head < heads; head++) {
|
|
const size_t base = (size_t)row * width + (size_t)head * head_dim;
|
|
uint32_t column;
|
|
|
|
for (column = 0; column < head_dim; column++) {
|
|
if (column < rot) {
|
|
const uint32_t partner =
|
|
(column < half) ? (column + half) : (column - half);
|
|
const float value = ft_float_from_bf16(in[base + column]);
|
|
const float other = ft_float_from_bf16(in[base + partner]);
|
|
const float cosine =
|
|
ft_float_from_bf16(cos_table[(size_t)row * rot + column]);
|
|
const float sine =
|
|
ft_float_from_bf16(sin_table[(size_t)row * rot + column]);
|
|
/* Each product is packed to bf16 in its own staging circular buffer
|
|
* before the add, so the model rounds twice on the way in and once on
|
|
* the way out — exactly where resident_rope_compute.c packs. */
|
|
const float first = ft_float_from_bf16(ft_bf16_from_float(value * cosine));
|
|
const float second = ft_float_from_bf16(ft_bf16_from_float(other * sine));
|
|
|
|
out[base + column] = ft_bf16_from_float(first + second);
|
|
} else {
|
|
out[base + column] = in[base + column];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ft_tt_rope_reference_table(uint16_t *cos_table, uint16_t *sin_table,
|
|
uint32_t block_first_position, uint32_t rot,
|
|
double theta_base)
|
|
{
|
|
const uint32_t half = rot / 2u;
|
|
uint32_t row;
|
|
|
|
for (row = 0; row < 32u; row++) {
|
|
const uint32_t position = block_first_position + row;
|
|
uint32_t column;
|
|
|
|
for (column = 0; column < rot; column++) {
|
|
const uint32_t j = (half == 0u) ? 0u : (column % half);
|
|
const double frequency = pow(theta_base, -2.0 * (double)j / (double)rot);
|
|
const double angle = (double)position * frequency;
|
|
const double sine = sin(angle);
|
|
|
|
cos_table[(size_t)row * rot + column] = ft_bf16_from_float((float)cos(angle));
|
|
sin_table[(size_t)row * rot + column] =
|
|
ft_bf16_from_float((float)((column < half) ? -sine : sine));
|
|
}
|
|
}
|
|
}
|