File size: 11,131 Bytes
28c70af
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#!/usr/bin/env python3
# SPDX-License-Identifier: MIT OR Apache-2.0
"""P3's whole-model oracle, from the SHIPPED ggml (llama.cpp b11192): every layer of the Qwen3 graph, output_norm and
the logits, built through ctypes with the ops llama.cpp's graph uses and computed by the release's own CPU backend
(libggml-cpu-haswell.so, 3 threads), for a chat prompt of fewer than 64 tokens (so attention takes ggml's reference
flash-attention path, as testing/forward_oracle.py's layer-0 checks do).

The graph is computed one layer at a time, each in its own context, carrying the residual stream between them as
f32 bytes, so memory stays near one layer's weights; the arithmetic is the same as one graph's.

Runs Q1_0 (1-bit) and Q2_0_g64 (ternary) models; since 0.3.4 also tied embeddings (no output.weight: the logits
from token_embd, as llama.cpp duplicates it), F16 weights and the Llama graph (no Q/K norms, RoPE in NORM mode, no
YaRN: ext_factor 0, attn_factor 1, as llama-context.cpp derives them without rope scaling). Writes .models/oracle-forward/model-<gguf stem>.tsv: a header line (the tokens), then per token and layer the sha256 of l_out's
f32 row, per token the sha256 of result_norm and of the logits row, the argmax, and the top five ids with their
logits (for eyes).
usage: python3 testing/model_oracle.py GGUF LIBDIR [OUT]"""
import ctypes as C, hashlib, mmap, os, struct, sys
from pathlib import Path

gguf, libdir = sys.argv[1], sys.argv[2]
out = Path(sys.argv[3] if len(sys.argv) > 3 else Path(__file__).resolve().parents[1] / ".models" / "oracle-forward")
out.mkdir(parents=True, exist_ok=True)
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from gguf_guard import parse  # noqa: E402

base = C.CDLL(os.path.join(libdir, "libggml-base.so"), mode=C.RTLD_GLOBAL)
cpu = C.CDLL(os.path.join(libdir, "libggml-cpu-haswell.so"), mode=C.RTLD_GLOBAL)
cpu.ggml_cpu_init()


class InitParams(C.Structure):
    _fields_ = [("mem_size", C.c_size_t), ("mem_buffer", C.c_void_p), ("no_alloc", C.c_bool)]


P = C.c_void_p
base.ggml_init.restype = P
base.ggml_init.argtypes = [InitParams]
for f, args in (("ggml_mul_mat", [P, P, P]), ("ggml_new_tensor_1d", [P, C.c_int, C.c_int64]),
                ("ggml_new_tensor_2d", [P, C.c_int, C.c_int64, C.c_int64]), ("ggml_new_tensor_3d", [P, C.c_int, C.c_int64, C.c_int64, C.c_int64]),
                ("ggml_get_rows", [P, P, P]), ("ggml_rms_norm", [P, P, C.c_float]), ("ggml_mul", [P, P, P]), ("ggml_add", [P, P, P]),
                ("ggml_new_graph", [P]), ("ggml_get_data", [P]), ("ggml_reshape_2d", [P, P, C.c_int64, C.c_int64]),
                ("ggml_reshape_3d", [P, P, C.c_int64, C.c_int64, C.c_int64]), ("ggml_permute", [P, P, C.c_int, C.c_int, C.c_int, C.c_int]),
                ("ggml_cpy", [P, P, P]), ("ggml_swiglu_split", [P, P, P]),
                ("ggml_rope_ext", [P, P, P, P, C.c_int, C.c_int, C.c_int, C.c_float, C.c_float, C.c_float, C.c_float, C.c_float, C.c_float]),
                ("ggml_flash_attn_ext", [P, P, P, P, P, C.c_float, C.c_float, C.c_float])):
    getattr(base, f).restype = P
    getattr(base, f).argtypes = args
base.ggml_build_forward_expand.argtypes = [P, P]
base.ggml_free.argtypes = [P]
base.ggml_prec_set_acc.argtypes = [P, C.c_int]
cpu.ggml_graph_compute_with_ctx.argtypes = [P, P, C.c_int]
F32, F16, I32, Q1_0, Q2_0 = 0, 1, 26, 41, 42
QK = {Q1_0: 128, Q2_0: 64, F16: 1}
BLOCK = {Q1_0: 18, Q2_0: 18, F16: 2}

fh = open(gguf, "rb")
mm = mmap.mmap(fh.fileno(), 0, access=mmap.ACCESS_READ)
ver, kv, tensors, align, data_start = parse(mm[:64 << 20])
T = {t["name"]: t for t in tensors}
arch = kv["general.architecture"]
assert arch in ("qwen3", "llama"), arch
A = lambda k, d=None: kv.get(f"{arch}.{k}", d)  # noqa: E731
eps = float(A("attention.layer_norm_rms_epsilon"))
n_layer = int(A("block_count"))
n_embd = int(A("embedding_length"))
n_ff = int(A("feed_forward_length"))
n_head, n_head_kv = int(A("attention.head_count")), int(A("attention.head_count_kv"))
head = int(A("attention.key_length", n_embd // n_head))
n_vocab = T["token_embd.weight"]["dims"][1]
OUTPUT = "output.weight" if "output.weight" in T else "token_embd.weight"  # tied embeddings: llama.cpp duplicates token_embd
ROPE_MODE = 2 if arch == "qwen3" else 0  # NEOX for Qwen3, NORM for Llama (llama_model_rope_type)

libm = C.CDLL("libm.so.6")
libm.logf.restype = C.c_float
libm.logf.argtypes = [C.c_float]
f32 = lambda x: struct.unpack("<f", struct.pack("<f", x))[0]  # noqa: E731
yarn = A("rope.scaling.type") == "yarn"
factor = f32(float(A("rope.scaling.factor", 1.0))) if yarn else 1.0
freq_scale = f32(1.0 / factor)
logfac = libm.logf(f32(1.0 / freq_scale))
mscale = f32(f32(f32(0.1) * logfac) + 1.0) if factor > 1.0 else 1.0
attn_factor = f32(mscale * f32(1.0 / f32(1.0 + f32(0.1 * logfac)))) if yarn else 1.0
ext_factor = 1.0 if yarn else 0.0
n_ctx_orig = int(A("rope.scaling.original_context_length", A("context_length")))
freq_base = float(A("rope.freq_base", 10000.0))
kq_scale = f32(1.0 / f32(head ** 0.5))

if n_vocab > 150000:  # the Qwen3 vocabulary: "You are Savante." / "What does a bonsai need?" in the Bonsai template
    prompt = [151644, 8948, 198, 2610, 525, 20739, 5409, 13, 151645, 198, 151644, 872, 198, 3838, 1558, 264, 22725, 12118, 30, 151645, 198,
              151644, 77091, 198, 151667, 271, 151668, 271]
else:  # the SmolLM2 vocabulary, the same conversation in ChatML (llama-server b11192's /apply-template + /tokenize)
    prompt = [1, 9690, 198, 2683, 359, 12404, 14648, 30, 2, 198, 1, 4093, 198, 1780, 1072, 253, 48219, 737, 47, 2, 198, 1, 520, 9531, 198]
n = len(prompt)
assert n < 64, "the reference attention path needs fewer than 64 query rows"


def q1_bytes(t):  # a matrix's bytes: 18-byte blocks of 128 (Q1_0) or 64 (Q2_0_g64) weights, or 2 bytes per F16 weight
    return t["dims"][1] * (t["dims"][0] // QK[t["type"]]) * BLOCK[t["type"]]


def load(ctx, name):
    t = T[name]
    if t["type"] == F32:
        x = base.ggml_new_tensor_1d(ctx, F32, t["dims"][0])
        nb = 4 * t["dims"][0]
    else:
        assert t["type"] in QK, (name, t["type"])
        x = base.ggml_new_tensor_2d(ctx, t["type"], t["dims"][0], t["dims"][1])
        nb = q1_bytes(t)
    C.memmove(base.ggml_get_data(x), C.c_char_p(mm[data_start + t["offset"]: data_start + t["offset"] + nb]), nb)
    return x


def rows(t, width):
    raw = C.string_at(base.ggml_get_data(t), width * 4 * n)
    return [raw[p * width * 4:(p + 1) * width * 4] for p in range(n)]


def new_ctx(weight_bytes):
    return base.ggml_init(InitParams(weight_bytes + (192 << 20), None, False))


def compute(ctx, t):
    gf = base.ggml_new_graph(ctx)
    base.ggml_build_forward_expand(gf, t)
    cpu.ggml_graph_compute_with_ctx(ctx, gf, 3)


# the embedding
ctx = new_ctx(q1_bytes(T["token_embd.weight"]))
tok = base.ggml_new_tensor_1d(ctx, I32, n)
C.memmove(base.ggml_get_data(tok), (C.c_int32 * n)(*prompt), 4 * n)
x = base.ggml_get_rows(ctx, load(ctx, "token_embd.weight"), tok)
compute(ctx, x)
stream = C.string_at(base.ggml_get_data(x), n_embd * 4 * n)
base.ggml_free(ctx)

lines = []
mask_vals = (C.c_uint16 * (n * n))(*[0 if j <= i else 0xFC00 for i in range(n) for j in range(n)])
for il in range(n_layer):
    names = [f"blk.{il}.{w}.weight" for w in ("attn_norm", "attn_q", "attn_k", "attn_v", "attn_q_norm", "attn_k_norm", "attn_output",
                                              "ffn_norm", "ffn_gate", "ffn_up", "ffn_down") if f"blk.{il}.{w}.weight" in T]
    ctx = new_ctx(sum(q1_bytes(T[m]) if T[m]["type"] in QK else 4 * T[m]["dims"][0] for m in names))
    W = {m.split(".")[2]: load(ctx, m) for m in names}
    inp = base.ggml_new_tensor_2d(ctx, F32, n_embd, n)
    C.memmove(base.ggml_get_data(inp), C.c_char_p(stream), len(stream))
    pos = base.ggml_new_tensor_1d(ctx, I32, n)
    C.memmove(base.ggml_get_data(pos), (C.c_int32 * n)(*range(n)), 4 * n)
    mask = base.ggml_new_tensor_2d(ctx, F16, n, n)
    C.memmove(base.ggml_get_data(mask), mask_vals, 2 * n * n)
    xn = base.ggml_mul(ctx, base.ggml_rms_norm(ctx, inp, C.c_float(eps)), W["attn_norm"])

    def qk(wname, nname, nh):
        r = base.ggml_reshape_3d(ctx, base.ggml_mul_mat(ctx, W[wname], xn), head, nh, n)
        if nname in W:  # Qwen3's per-head norms; Llama has none
            r = base.ggml_mul(ctx, base.ggml_rms_norm(ctx, r, C.c_float(eps)), W[nname])
        return base.ggml_rope_ext(ctx, r, pos, None, head, ROPE_MODE, n_ctx_orig, C.c_float(freq_base), C.c_float(freq_scale),
                                  C.c_float(ext_factor), C.c_float(attn_factor), C.c_float(32.0), C.c_float(1.0))
    q = qk("attn_q", "attn_q_norm", n_head)
    k = qk("attn_k", "attn_k_norm", n_head_kv)
    v = base.ggml_reshape_3d(ctx, base.ggml_mul_mat(ctx, W["attn_v"], xn), head, n_head_kv, n)
    k16 = base.ggml_cpy(ctx, k, base.ggml_new_tensor_3d(ctx, F16, head, n_head_kv, n))
    v16 = base.ggml_cpy(ctx, v, base.ggml_new_tensor_3d(ctx, F16, head, n_head_kv, n))
    fa = base.ggml_flash_attn_ext(ctx, base.ggml_permute(ctx, q, 0, 2, 1, 3), base.ggml_permute(ctx, k16, 0, 2, 1, 3),
                                  base.ggml_permute(ctx, v16, 0, 2, 1, 3), mask, C.c_float(kq_scale), C.c_float(0.0), C.c_float(0.0))
    base.ggml_prec_set_acc(fa, 10)
    ffn_inp = base.ggml_add(ctx, base.ggml_mul_mat(ctx, W["attn_output"], base.ggml_reshape_2d(ctx, fa, n_head * head, n)), inp)
    fn = base.ggml_mul(ctx, base.ggml_rms_norm(ctx, ffn_inp, C.c_float(eps)), W["ffn_norm"])
    sw = base.ggml_swiglu_split(ctx, base.ggml_mul_mat(ctx, W["ffn_gate"], fn), base.ggml_mul_mat(ctx, W["ffn_up"], fn))
    l_out = base.ggml_add(ctx, base.ggml_mul_mat(ctx, W["ffn_down"], sw), ffn_inp)
    compute(ctx, l_out)
    for p, r in enumerate(rows(l_out, n_embd)):
        lines.append(f"l_out\t{il}\t{p}\t{hashlib.sha256(r).hexdigest()}\t{' '.join(f'{v:.6g}' for v in struct.unpack('<3f', r[:12]))}")
    stream = C.string_at(base.ggml_get_data(l_out), n_embd * 4 * n)
    base.ggml_free(ctx)
    print(f"layer {il + 1}/{n_layer}", end="\r", flush=True)

# output_norm and the logits
ctx = new_ctx(q1_bytes(T[OUTPUT]) + 4 * n_embd + 4 * n_vocab * n)
inp = base.ggml_new_tensor_2d(ctx, F32, n_embd, n)
C.memmove(base.ggml_get_data(inp), C.c_char_p(stream), len(stream))
rn = base.ggml_mul(ctx, base.ggml_rms_norm(ctx, inp, C.c_float(eps)), load(ctx, "output_norm.weight"))
logits = base.ggml_mul_mat(ctx, load(ctx, OUTPUT), rn)
compute(ctx, logits)
for p, r in enumerate(rows(rn, n_embd)):
    lines.append(f"result_norm\t-\t{p}\t{hashlib.sha256(r).hexdigest()}\t{' '.join(f'{v:.6g}' for v in struct.unpack('<3f', r[:12]))}")
for p, r in enumerate(rows(logits, n_vocab)):
    vals = struct.unpack(f"<{n_vocab}f", r)
    top = sorted(range(n_vocab), key=lambda i: -vals[i])[:5]
    lines.append(f"logits\t{top[0]}\t{p}\t{hashlib.sha256(r).hexdigest()}\t{' '.join(f'{i}:{vals[i]:.6g}' for i in top)}")
base.ggml_free(ctx)
dest = out / f"model-{Path(gguf).stem}.tsv"
with open(dest, "w") as f:
    f.write(f"# {n_layer} layers · tokens {' '.join(map(str, prompt))}\n")
    f.write("\n".join(lines) + "\n")
print(f"\n{n_layer} layers, result_norm and logits for {n} tokens → {dest}")