==> tests/scalar.c <==1/* gpt2.rs's constants_are_their_values and scalar_functions_are_close (references in double, computed
2 * here without libm), then a golden digest from the Rust over 300 000 inputs of each function. */
3#include "mesh/ref/gpt2_scalar.h"
4
5#include "mesh/ftz.h"
6
7#include <stdio.h>
8
9static int failures;
10
11static double dabs(double x) { return x < 0 ? -x : x; }
12
13/* exp by range reduction to |r| <= ln2/2 and a long Taylor series. */
14static double dexp(double x) {
15 const double ln2 = 0.6931471805599453;
16 int k = (int)(x / ln2 + (x < 0 ? -0.5 : 0.5));
17 double r = x - k * ln2, term = 1, sum = 1;
18 for (int n = 1; n < 30; n++) term *= r / n, sum += term;
19 while (k > 0) sum *= 2, k--;
20 while (k < 0) sum /= 2, k++;
21 return sum;
22}
23
24static double dtanh(double x) { return 1 - 2 / (dexp(2 * x) + 1); }
25
26static double dsqrt(double x) {
27 double y = x > 1 ? x : 1;
28 for (int i = 0; i < 200; i++) y = 0.5 * (y + x / y);
29 return y;
30}
31
32static uint64_t fh(uint64_t h, uint64_t w) { return (h ^ w) * 0x100000001b3ull; }
33
34static uint32_t mix(uint32_t x) {
35 x ^= x >> 16;
36 x *= 0x7feb352du;
37 x ^= x >> 15;
38 x *= 0x846ca68bu;
39 return x ^ (x >> 16);
40}
41
42static uint32_t gen(uint32_t m, uint32_t i) {
43 uint32_t h = mix((i * 0x9e3779b9u) ^ (m * 0x85ebca6bu));
44 uint32_t h2 = mix(h ^ 0x27d4eb2fu);
45 if (m == 0) return h;
46 if (m == 1) return (h & 0x80000000u) | ((h2 % 41) << 23) | (h & 0x007fffffu);
47 return (h & 0x80000000u) | ((50 + h2 % 31) << 23) | (h & 0x007fffffu);
48}
49
50int main(void) {
51 /* constants_are_their_values */
52 if (mesh_ftz_from_bits(MESH_GPT2_LN_EPS_BITS) != 1e-5f || mesh_ftz_from_bits(MESH_GPT2_GELU_C_BITS) != 0.7978846f ||
53 mesh_ftz_from_bits(MESH_GPT2_GELU_A_BITS) != 0.044715f || mesh_ftz_from_bits(MESH_GPT2_LOG2E_BITS) != 1.442695f ||
54 mesh_ftz_from_bits(MESH_GPT2_LN2_HI_BITS) != 0.69314575f ||
55 mesh_ftz_from_bits(MESH_GPT2_LN2_LO_BITS) != 1.4286068e-6f || mesh_ftz_from_bits(MESH_GPT2_ATTN_SCALE_BITS) != 0.125f)
56 failures++;
57 const float want[11] = {1.0f / 3628800.0f, 1.0f / 362880.0f, 1.0f / 40320.0f, 1.0f / 5040.0f, 1.0f / 720.0f,
58 1.0f / 120.0f, 1.0f / 24.0f, 1.0f / 6.0f, 0.5f, 1.0f, 1.0f};
59 for (int i = 0; i < 11; i++)
60 if (mesh_ftz_from_bits(mesh_gpt2_exp_poly_coeffs[i]) != want[i]) failures++;
61
62 /* scalar_functions_are_close */
63 for (int i = -400; i <= 400; i++) {
64 float x = (float)i / 8.0f;
65 if (x > -87.0f && x < 60.0f) {
66 double e = mesh_gpt2_exp_poly(x);
67 if (dabs(e / dexp(x) - 1.0) >= 4e-7) fprintf(stderr, "exp %g: %g\n", x, e), failures++;
68 }
69 double t = mesh_gpt2_tanh_poly(x / 8.0f);
70 if (dabs(t - dtanh((double)x / 8.0)) >= 3e-7) fprintf(stderr, "tanh %g\n", x), failures++;
71 }
72 const float as[7] = {1e-5f, 0.01f, 0.5f, 1.0f, 2.0f, 1234.5f, 3e7f};
73 for (int i = 0; i < 7; i++) {
74 double s = mesh_gpt2_sqrt_newton(as[i]);
75 if (dabs(s / dsqrt(as[i]) - 1.0) >= 2e-7) fprintf(stderr, "sqrt %g\n", as[i]), failures++;
76 }
77 if (mesh_gpt2_exp_poly(0.0f) != 1.0f || mesh_gpt2_exp_poly(-100.0f) != 0.0f) failures++;
78
79 /* golden: gold gpt2 "scalars" */
80 uint64_t h = 0xcbf29ce484222325ull;
81 for (uint32_t i = 0; i < 300000; i++) {
82 float x = mesh_ftz_from_bits(gen(i % 3, i));
83 float y = ((float)i - 150000.0f) / 1000.0f;
84 float v[7] = {mesh_gpt2_exp_poly(x), mesh_gpt2_exp_poly(y), mesh_gpt2_tanh_poly(x), mesh_gpt2_tanh_poly(y),
85 mesh_gpt2_gelu_tanh(x), mesh_gpt2_gelu_tanh(y),
86 mesh_gpt2_sqrt_newton(mesh_ftz_from_bits(gen(i % 3, i) & 0x7fffffffu))};
87 for (int j = 0; j < 7; j++) h = fh(h, mesh_ftz_bits(v[j]));
88 }
89 if (h != 0xcd832a1d2b3bc7b0ull) fprintf(stderr, "scalars %#018llx\n", (unsigned long long)h), failures++;
90 return failures != 0;
91}