File size: 5,732 Bytes
bbb6388 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | // src/artifact/dequant.cpp - the `strata-dequant` CLI (dequantize a GGUF tensor and dump F32).
// The dequantizers themselves are header-only in include/strata/artifact/dequant.hpp.
#include "strata/artifact/dequant.hpp"
#ifdef STRATA_DEQUANT_SELFTEST
int main(int argc, char** argv) {
if (argc < 2) {
std::printf("usage: dequant <file.gguf>\n");
return 2;
}
try {
strata::GgufFile g(argv[1]);
int checked = 0, bad = 0;
for (const auto& t : g.tensors()) {
if (t.type != 42) continue; // Q2_0 only for now
if (checked >= 8) break;
const uint8_t* base = g.tensor_data(t);
const uint64_t nblocks = t.elements() / 64;
for (uint64_t b = 0; b < nblocks && b < 64; ++b) {
const uint8_t* blk = base + b * 18;
float out[64];
strata::dequantize_q2_0(blk, out);
const float d = strata::fp16_to_fp32(strata::read_u16(blk));
// STRUCTURAL INVARIANT: every value must be exactly one of {-d, 0, d, 2d}.
for (int j = 0; j < 64; ++j) {
const float v = out[j];
const bool ok = (v == -d) || (v == 0.0f) || (v == d) || (v == 2.0f * d) || (d == 0.0f);
if (!ok) {
++bad;
if (bad < 4)
std::printf(" VIOLATION %s block %llu elem %d: v=%g d=%g\n", t.name.c_str(),
(unsigned long long)b, j, v, d);
}
}
}
if (checked == 0) {
// A folded checksum over the FIRST 64 blocks, for cross-checking against the Python
// implementation on identical bytes. A single implementation being self-consistent
// proves little - two disagreeing is what finds bugs (round 67).
double sum = 0, sumabs = 0;
const uint8_t* base = g.tensor_data(t);
for (uint64_t b = 0; b < nblocks && b < 64; ++b) {
float out[64];
strata::dequantize_q2_0(base + b * 18, out);
for (int j = 0; j < 64; ++j) {
sum += out[j];
sumabs += std::fabs(out[j]);
}
}
std::printf(" CHECKSUM %s sum=%.10g sumabs=%.10g\n", t.name.c_str(), sum, sumabs);
}
++checked;
}
// Q5_0 cross-check on the same 64-block window gguf-py is given, using the first Q5_0 tensor.
for (const auto& t : g.tensors()) {
if (t.type != 6 && t.type != 20 && t.type != 14 && t.type != 12 && t.type != 13 && t.type != 11 &&
t.type != 23)
continue;
const uint8_t* base = g.tensor_data(t);
const int bsz =
(t.type == 20)
? 18
: (t.type == 14
? 210
: (t.type == 12
? 144
: (t.type == 13 ? 176 : (t.type == 11 ? 110 : (t.type == 23 ? 136 : 22)))));
const int nel =
(t.type == 14 || t.type == 12 || t.type == 13 || t.type == 11 || t.type == 23) ? 256 : 32;
const uint64_t nb = t.elements() / (uint64_t)nel;
double sum = 0, sumabs = 0;
for (uint64_t b = 0; b < nb && b < 64; ++b) {
float out[256];
if (t.type == 20)
strata::dequantize_iq4_nl(base + b * bsz, out);
else if (t.type == 14)
strata::dequantize_q6_K(base + b * bsz, out);
else if (t.type == 12)
strata::dequantize_q4_K(base + b * bsz, out);
else if (t.type == 13)
strata::dequantize_q5_K(base + b * bsz, out);
else if (t.type == 11)
strata::dequantize_q3_K(base + b * bsz, out);
else if (t.type == 23)
strata::dequantize_iq4_xs(base + b * bsz, out);
else
strata::dequantize_q5_0(base + b * bsz, out);
for (int j = 0; j < nel; ++j) {
sum += out[j];
sumabs += std::fabs(out[j]);
}
}
std::printf(" CHECKSUM %s %s sum=%.10g sumabs=%.10g\n", t.type_name(), t.name.c_str(), sum,
sumabs);
float f8[256];
if (t.type == 20)
strata::dequantize_iq4_nl(base, f8);
else if (t.type == 14)
strata::dequantize_q6_K(base, f8);
else if (t.type == 12)
strata::dequantize_q4_K(base, f8);
else if (t.type == 13)
strata::dequantize_q5_K(base, f8);
else if (t.type == 11)
strata::dequantize_q3_K(base, f8);
else if (t.type == 23)
strata::dequantize_iq4_xs(base, f8);
else
strata::dequantize_q5_0(base, f8);
std::printf(" REF first8:");
for (int j = 0; j < 8; ++j) std::printf(" %.6g", f8[j]);
std::printf("\n");
if (t.type == 23) break; // emit Q5_0 first, then IQ4_NL
}
std::printf("%s\n Q2_0 tensors spot-checked: %d\n symbolic invariant violations: %d -> %s\n",
argv[1], checked, bad, bad ? "FAIL" : "PASS");
return bad ? 1 : 0;
} catch (const std::exception& e) {
std::printf("ERROR: %s\n", e.what());
return 1;
}
}
#endif
|