Add native K/M/RR inference engines, HIP kernels, replay and validation harnesses, and Windows DLSS setup and runtime support. Include GPU profiling tools, build and packaging scripts, platform integration patches, and implementation documentation.
363 lines
20 KiB
Python
363 lines
20 KiB
Python
#!/usr/bin/env python3
|
|
"""Exact numpy reference of preset F's encoder stages (rr2_enc1_kernel ... rr2_enc4_kernel of nvngx_dlssd.dll
|
|
310.9), one 8x8 window at a time, from weights and real inputs only; checked against a capture (main).
|
|
|
|
A stage has C channels and works on windows of 64 tokens (origin 8 * block - shift). Arithmetic is binary16 with FP8
|
|
(e4m3) operands in the products, exactly as in presets D and E (enc1_ref.py: mm, the cubic softmax, the activation):
|
|
|
|
merge x = W1 . [the four tokens of the 2x2 group one level up, (0,0) (1,0) (0,1) (1,1)] + b1; window tokens
|
|
outside the level are the mirrored tokens
|
|
heads per head, all from q8(x): QK = Wqk . x and V = Wv . x (32 channels each, no bias). The scores are
|
|
QK . QK^T + bias: symmetric, so NVIDIA computes the blocks (queries 0-31, keys 0-31), (0-31, 32-63) and
|
|
(32-63, 32-63) and transposes the second for (32-63, 0-31). Cubic softmax over the 64 keys, P . V, and the
|
|
out-projections of all heads accumulate into one sum a (the first one's C is the bias bo)
|
|
residual r = dq(q8(x)) + a * s1 (dq: the FP8 code back as binary16; s1: binary16 at argument 94)
|
|
MLP NB branches from q8(r): C -> 32 (+ bias) -> act -> C, accumulated into m (first C: the bias bu)
|
|
residual y = r + m * s2 (s2: argument 96); the stage's tokens are q8(y)
|
|
outputs merged = Wm . [y of the 2x2 group] + bm for the next encoder (q8), and a 16-channel head h = Wh . y + bh
|
|
whose first three channels gate the stage's guide image (argument 48, three binary16 channels per token):
|
|
out = min(guide * (sigmoid(h) * s0), 65504) (s0: argument 92) at argument 64, and its 2x2 mean
|
|
((a + b) + (c + d) of the quarters) at argument 72 for the next encoder
|
|
|
|
Arena layouts: tokens are FP8 codes in planes of 32 channels ([plane][y][x][32], NVIDIA's k order within a plane);
|
|
the guide images are four binary16 per token.
|
|
|
|
usage: rr2_ref.py CAPTURE STAGE FRAME WEIGHTS.npz [--blocks X,Y ...] (verifies against the capture)
|
|
rr2_ref.py CAPTURE STAGE FRAME OUT.npz --extract DUMP.npz (weights from a phase dump, phase_dump.py)
|
|
"""
|
|
import argparse
|
|
import os
|
|
import struct
|
|
import sys
|
|
|
|
import numpy as np
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
from rrmath import e4m3, q8 # noqa: E402
|
|
from enc1_ref import inter, mm, act, add, pairs_tree, mirror, H, F # noqa: E402
|
|
from rr_capture import Capture # noqa: E402
|
|
|
|
# stage: C, channels one level up (merge input), merged channels for the next encoder, heads, MLP branches
|
|
STAGES = {'enc0': (32, 0, 0, 2, 4), 'enc1': (64, 32, 96, 2, 8), 'enc2': (96, 0, 128, 4, 12), 'enc3': (128, 0, 160, 4, 16),
|
|
'enc4': (160, 0, 160, 8, 20), 'enc5': (160, 0, 0, 8, 20),
|
|
'dec0_1': (32, 0, 0, 2, 4), 'dec4_1': (160, 0, 0, 8, 20), 'dec3_1': (128, 0, 0, 4, 16), 'dec2_1': (96, 0, 0, 4, 12), 'dec1_1': (64, 0, 0, 2, 8)}
|
|
# the decoders' first kernels (decN_0): C, channels one level down, heads, MLP branches. They expand the 4 x 4 tokens
|
|
# one level down to the window, e = We . low + be (4C outputs: sub-position (y & 1) * 2 + (x & 1), then channel), and
|
|
# start from x = dq(q8(e)) + dq(skip) * s3 (s3: binary16 at argument 98; skip: the encoder's tokens of this level,
|
|
# argument 16); then the heads and the MLP as above, the first residual on x itself (not dq(q8(x))). No merge, no guide.
|
|
EXPAND = {'dec4_0': (160, 160, 8, 20), 'dec3_0': (128, 160, 4, 16), 'dec2_0': (96, 128, 4, 12), 'dec1_0': (64, 96, 2, 8),
|
|
'dec0_0': (32, 64, 2, 4)}
|
|
# the network's sixteen kernels in the DLL's order, with their head counts
|
|
ORDER = ['enc0', 'enc1', 'enc2', 'enc3', 'enc4', 'enc5', 'dec4_0', 'dec4_1', 'dec3_0', 'dec3_1', 'dec2_0', 'dec2_1',
|
|
'dec1_0', 'dec1_1', 'dec0_0', 'dec0_1']
|
|
HEADS = [2, 2, 4, 4, 8, 8, 8, 8, 4, 4, 4, 4, 2, 2, 2, 2]
|
|
|
|
|
|
def find_tables(dll):
|
|
"""byte offsets of the sixteen kernels' relative position tables in nvngx_dlssd.dll: runs of 64 x 64 binary16
|
|
tables (one per head) with a constant diagonal and at most 225 distinct values, in ORDER"""
|
|
a = np.frombuffer(dll[:len(dll) & ~1], np.uint16)
|
|
n = len(a) - 4096
|
|
m = (a[:n] & 0x7fff) != 0
|
|
for k in range(1, 8):
|
|
m &= a[:n] == a[65 * k:65 * k + n]
|
|
m &= a[1:n + 1] == a[66:66 + n]
|
|
groups = []
|
|
for i in np.nonzero(m)[0]:
|
|
t = a[i:i + 4096].reshape(64, 64)
|
|
if not (np.all(t.diagonal() == t[0, 0]) and 50 < len(np.unique(t)) <= 225):
|
|
continue
|
|
if groups and 2 * int(i) - groups[-1][-1] == 8192:
|
|
groups[-1].append(2 * int(i))
|
|
else:
|
|
groups.append([2 * int(i)])
|
|
for start in range(len(groups) - 15):
|
|
if [len(g) for g in groups[start:start + 16]] == HEADS:
|
|
return {name: g[0] for name, g in zip(ORDER, groups[start:start + 16])}
|
|
raise SystemExit('no preset F model found in the DLL (its sixteen relative position table groups)')
|
|
|
|
|
|
def dll_weights(dll, stage):
|
|
"""an encoder stage's tensors from the DLL: plain binary16 arrays in natural [out][in] order, one after another.
|
|
Before the tables: {s0, 0, 0, 0} and (enc1) the merge of encoder 0's tokens, W1 then b1. After them, per tensor
|
|
kind all heads / branches together: Wqk, Wv (heads x 32 x C), Wo (C x heads x 32), bo, Wd (branches x 32 x C), bd,
|
|
Wu (C x branches x 32), bu, the merge
|
|
for the next encoder Wm (C_M x 4C) and bm, the guide head Wh (3 x C) and bh. A merge's input index is
|
|
channel * 4 + sub-position; the kernels' bias is (T + T^T) * sqrt(2)."""
|
|
if stage in EXPAND:
|
|
c, c_lo, nh, nb = EXPAND[stage]
|
|
c_up = c_m = 0
|
|
else:
|
|
c, c_up, c_m, nh, nb = STAGES[stage]
|
|
g = find_tables(dll)[stage]
|
|
f = lambda off, n: np.frombuffer(dll[off:off + 2 * n], H)
|
|
|
|
def B(mat, merge=0):
|
|
"""[out][in] binary16 -> the product's B operand (in x out FP8 codes, the inputs in NVIDIA's k order)"""
|
|
k = mat.shape[1]
|
|
if merge: # channel * 4 + q -> q * channels + channel
|
|
mat = mat.reshape(mat.shape[0], merge, 4).transpose(0, 2, 1).reshape(mat.shape[0], k)
|
|
return q8(mat).T[inter(k)]
|
|
w = {}
|
|
if c_up:
|
|
o = g - 8 - 2 * (3 * c_up + 4) - 2 * c - 2 * c * 4 * c_up # before encoder 0's guide head (3 x C_UP, bias, 0)
|
|
w['w1'] = B(f(o, c * 4 * c_up).reshape(c, 4 * c_up), c_up)
|
|
w['b1'] = f(o + 2 * c * 4 * c_up, c)
|
|
if stage in EXPAND: # We (C x 4 x C_LO) and be (C x 4) end where the tables start
|
|
o = g - 8 * c - 8 * c * c_lo
|
|
we = f(o, 4 * c * c_lo).reshape(c, 4, c_lo).transpose(1, 0, 2).reshape(4 * c, c_lo)
|
|
w['we'] = q8(we).T[inter(c_lo)]
|
|
w['be'] = f(g - 8 * c, 4 * c).reshape(c, 4).T.reshape(-1)
|
|
if stage == 'enc0':
|
|
# encoder 0's embedding of the 43 per-pixel features (the kernel's 64 feature slots, the rest unused): two MLP
|
|
# branches 43 -> 32 -> act -> 32, x = bu + sum: Wd (2 x 32 x 43), bd, Wu (32 x 2 x 32), bu, then 16 bytes
|
|
o = g - 16 - 64 - 4096 - 128 - 2 * 2 * 32 * 43
|
|
wd = np.zeros((2, 32, 64), H); wd[:, :, :43] = f(o, 2 * 32 * 43).reshape(2, 32, 43); o += 2 * 2 * 32 * 43
|
|
w['ewd'] = np.array([B(x) for x in wd])
|
|
w['ebd'] = f(o, 64).reshape(2, 32); o += 128
|
|
w['ewu'] = np.array([B(x) for x in f(o, 2048).reshape(32, 2, 32).transpose(1, 0, 2)]); o += 4096
|
|
w['ebu'] = f(o, 32)
|
|
o = g
|
|
t = f(o, nh * 4096).reshape(nh, 64, 64).astype(np.float64); o += nh * 8192
|
|
w['bias'] = ((t + t.transpose(0, 2, 1)) * np.sqrt(2)).astype(H)
|
|
w['wqk'] = np.array([B(x) for x in f(o, nh * 32 * c).reshape(nh, 32, c)]); o += nh * 64 * c
|
|
w['wv'] = np.array([B(x) for x in f(o, nh * 32 * c).reshape(nh, 32, c)]); o += nh * 64 * c
|
|
w['wo'] = np.array([B(x) for x in f(o, nh * 32 * c).reshape(c, nh, 32).transpose(1, 0, 2)]); o += nh * 64 * c
|
|
w['bo'] = f(o, c); o += 2 * c
|
|
w['wd'] = np.array([B(x) for x in f(o, nb * 32 * c).reshape(nb, 32, c)]); o += nb * 64 * c
|
|
w['bd'] = f(o, nb * 32).reshape(nb, 32); o += nb * 64
|
|
w['wu'] = np.array([B(x) for x in f(o, nb * 32 * c).reshape(c, nb, 32).transpose(1, 0, 2)]); o += nb * 64 * c
|
|
w['bu'] = f(o, c); o += 2 * c
|
|
if c_m:
|
|
w['wm'] = B(f(o, c_m * 4 * c).reshape(c_m, 4 * c), c); o += 2 * c_m * 4 * c
|
|
w['bm'] = f(o, c_m); o += 2 * c_m
|
|
if stage in EXPAND:
|
|
return w
|
|
if stage == 'enc0': # the merge of its tokens (enc1's W1, b1) comes before its guide head
|
|
o += 2 * 64 * 128 + 2 * 64
|
|
nhd = 4 if stage.startswith('dec') else 3 # the guide head: three gates; decoders also a blend factor
|
|
wh = np.zeros((16, c), H); wh[:nhd] = f(o, nhd * c).reshape(nhd, c); o += 2 * nhd * c
|
|
bh = np.zeros(16, H); bh[:nhd] = f(o, nhd)
|
|
w['wh'], w['bh'] = B(wh), bh
|
|
return w
|
|
|
|
|
|
def dq(x):
|
|
"""binary16 values through FP8 and back"""
|
|
return e4m3(q8(x)).astype(H)
|
|
|
|
|
|
def A(x):
|
|
"""binary16 rows -> the product's A operand: FP8 codes in NVIDIA's k order"""
|
|
return q8(x)[:, inter(x.shape[1])]
|
|
|
|
|
|
def softmax(s):
|
|
"""the cubic-exponent softmax of presets D and E over the 64 keys of a row"""
|
|
def fma16(a, b, c):
|
|
return (a.astype(np.float64) * b.astype(np.float64) + c.astype(np.float64)).astype(H)
|
|
m = np.clip((s * H(0.017181396484375)).astype(H), H(-0.55615234375), H(0.55615234375))
|
|
t = fma16(m, -m, np.full_like(m, H(0.92724609375)))
|
|
u = fma16(m, t, np.full_like(m, H(1.375)))
|
|
e = ((((u.view(np.uint16).astype(np.uint32)) << 5) & 0x7FE0).astype(np.uint16)).view(H)
|
|
nt = e.shape[1] // 8
|
|
part = {}
|
|
for tt in range(4):
|
|
for half in range(2):
|
|
ev = pairs_tree([e[:, 8 * j + 2 * tt + half] for j in range(0, nt, 2)])
|
|
od = pairs_tree([e[:, 8 * j + 2 * tt + half] for j in range(1, nt, 2)])
|
|
part[tt, half] = add(ev, od)
|
|
out = [add(add(part[0, h], part[1, h]), add(part[2, h], part[3, h])) for h in range(2)]
|
|
r = (F(1) / add(out[0], out[1]).astype(F)).astype(H)
|
|
return (e * r[:, None]).astype(H)
|
|
|
|
|
|
def sigmoid(d):
|
|
"""1 / (1 + 2^(-d log2 e)) in f32, rounded to binary16 (the GPU's exp2 and division; see rr_exact.glsl sig)"""
|
|
f = F(1) / (np.exp2(d.astype(F) * F(-1.4426950216293335)).astype(F) + F(1))
|
|
return f.astype(H)
|
|
|
|
|
|
def upsample(low, x, y):
|
|
"""the decoders' 2x bilinear fetch of the guide one level down (half-pixel centres, edge clamped), with NVIDIA's
|
|
binary16 rounding: every weight product and the three sums in tap order"""
|
|
def axis(v, n):
|
|
if v == 0:
|
|
return 0, H(0)
|
|
b = (v - 1) // 2
|
|
return b, H(0.25) if v & 1 else H(0.75)
|
|
hh, hw = low.shape[:2]
|
|
bx, wx = axis(x, hw)
|
|
by, wy = axis(y, hh)
|
|
x1, y1 = min(bx + 1, hw - 1), min(by + 1, hh - 1)
|
|
ux, uy = H(1) - wx, H(1) - wy
|
|
m = lambda v, a, b: ((v * a).astype(H) * b).astype(H)
|
|
return add(add(add(m(low[by, bx], ux, uy), m(low[by, x1], wx, uy)), m(low[y1, bx], ux, wy)), m(low[y1, x1], wx, wy))
|
|
|
|
|
|
def window(w, scal, xin, x=None):
|
|
"""xin: 64 x 4*C_UP FP8 codes (the merge input, already in k order). Returns y (64 x C binary16), the merged
|
|
tokens (16 x C_M binary16) and the head (64 x 16 binary16)."""
|
|
s0, s1, s2 = scal
|
|
base = None
|
|
if x is not None: # a decoder's first kernel: the expanded tokens plus the skip; the residual keeps it unquantised
|
|
xa, base = A(x), x
|
|
elif 'w1' in w:
|
|
x = mm(xin, w['w1'], np.tile(w['b1'], (64, 1)))
|
|
xa = A(x)
|
|
else: # the tokens themselves (already FP8)
|
|
x, xa = e4m3(xin)[:, np.argsort(inter(xin.shape[1]))].astype(H), xin
|
|
acc = np.tile(w['bo'], (64, 1))
|
|
for h in range(len(w['wqk'])):
|
|
qk = mm(xa, w['wqk'][h], np.zeros((64, 32), H))
|
|
v = mm(xa, w['wv'][h], np.zeros((64, 32), H))
|
|
qa = A(qk)
|
|
s = mm(qa, qa.T, w['bias'][h])
|
|
# NVIDIA computes three 32 x 32 blocks and transposes one; the full product is the same values
|
|
p = softmax(s)
|
|
o = mm(A(p), A(v.T).T if False else q8(v)[inter(64), :], np.zeros((64, 32), H))
|
|
acc = mm(A(o), w['wo'][h], acc)
|
|
r = add(dq(x) if base is None else base, (acc * s1).astype(H))
|
|
ra = A(r)
|
|
m = np.tile(w['bu'], (64, 1))
|
|
for j in range(len(w['wd'])):
|
|
d = mm(ra, w['wd'][j], np.tile(w['bd'][j], (64, 1)))
|
|
m = mm(A(act(d)), w['wu'][j], m)
|
|
y = add(r, (m * s2).astype(H))
|
|
ya = A(y)
|
|
# the 2x2 groups of the window: token order (0,0) (1,0) (0,1) (1,1), 16 groups row-major
|
|
g = np.zeros((16, 4 * y.shape[1]), np.uint8)
|
|
for n in range(16):
|
|
gx, gy = n % 4, n // 4
|
|
for q, (dx, dy) in enumerate(((0, 0), (1, 0), (0, 1), (1, 1))):
|
|
g[n, q * y.shape[1]:(q + 1) * y.shape[1]] = ya[(2 * gy + dy) * 8 + 2 * gx + dx]
|
|
merged = mm(g, w['wm'], np.tile(w['bm'], (16, 1))) if 'wm' in w else None
|
|
head = mm(ya, w['wh'], np.tile(w['bh'], (64, 1))) if 'wh' in w else None
|
|
return y, merged, head
|
|
|
|
|
|
def extract(z, out):
|
|
"""weights of enc1 from a phase dump (phases as phase_dump.py numbers them)"""
|
|
P = lambda p, n: z[f'p{p}_w0_{n}']
|
|
Ch = lambda p: P(p, 'C').view(H)
|
|
w = {'w1': P(1, 'B'), 'b1': Ch(1)[0], 'wqk': [], 'wv': [], 'bias': [], 'wo': [], 'bo': Ch(11)[0]}
|
|
for h in range(2):
|
|
b = 2 + 10 * h
|
|
w['wqk'].append(np.vstack([P(b, 'B'), P(b + 2, 'B')]))
|
|
w['wv'].append(np.vstack([P(b + 1, 'B'), P(b + 3, 'B')]))
|
|
tl, tr, br = Ch(b + 4), Ch(b + 5), Ch(b + 7)
|
|
w['bias'].append(np.block([[tl, tr], [tr.T, br]]))
|
|
w['wo'].append(P(b + 9, 'B'))
|
|
w['wd'] = [P(22 + 2 * j, 'B') for j in range(8)]
|
|
w['bd'] = [Ch(22 + 2 * j)[0] for j in range(8)]
|
|
w['wu'] = [P(23 + 2 * j, 'B') for j in range(8)]
|
|
w['bu'] = Ch(23)[0]
|
|
w['wm'], w['bm'], w['wh'], w['bh'] = P(38, 'B'), Ch(38)[0], P(39, 'B'), Ch(39)[0]
|
|
np.savez_compressed(out, **{k: np.array(v) for k, v in w.items()})
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument('capture')
|
|
ap.add_argument('stage')
|
|
ap.add_argument('frame', type=int)
|
|
ap.add_argument('weights')
|
|
ap.add_argument('--extract')
|
|
ap.add_argument('--blocks', nargs='*', default=['5,5'])
|
|
o = ap.parse_args()
|
|
if o.extract:
|
|
return extract(np.load(o.extract), o.weights)
|
|
expand = o.stage in EXPAND
|
|
if expand:
|
|
c, c_lo, heads, nb = EXPAND[o.stage]
|
|
c_up = c_m = 0
|
|
else:
|
|
c, c_up, c_m, heads, nb = STAGES[o.stage]
|
|
w = dll_weights(open(o.weights, 'rb').read(), o.stage) if o.weights.lower().endswith('.dll') else dict(np.load(o.weights))
|
|
cap = Capture(os.path.expanduser(o.capture))
|
|
kernel = f'rr2_{o.stage}_kernel' if o.stage.startswith('enc') else f'rr2_{o.stage[:4]}_kernel_{o.stage[5]}'
|
|
l = [l for l in cap.launches if l.kernel == kernel][o.frame - 1]
|
|
pre = np.frombuffer(l.pre(l.arena_alloc), np.uint8)
|
|
post = np.frombuffer(l.post_buffer(cap, l.arena_alloc), np.uint8)
|
|
ptr = {p.offset: p.delta for p in l.pointers if p.alloc == l.arena_alloc}
|
|
wd, ht = struct.unpack_from('<2I', l.args, 0)
|
|
sx, sy = struct.unpack_from('<2H', l.args, 80)
|
|
scal = np.frombuffer(l.args[92:98], H)
|
|
s3 = np.frombuffer(l.args[98:100], H)[0]
|
|
planes = lambda buf, off, w_, h_, ch: buf[off:off + w_ * h_ * ch].reshape(ch // 32, h_, w_, 32)
|
|
up = (planes(pre, ptr[8], wd // 2, ht // 2, c_lo) if expand else planes(pre, ptr[8], 2 * wd, 2 * ht, c_up) if c_up else
|
|
planes(pre, ptr[8], wd, ht, c))
|
|
skip = planes(pre, ptr[16], wd, ht, c) if expand else None
|
|
guided = 48 in ptr and o.stage.startswith('enc')
|
|
dec_guide = o.stage.startswith('dec') and 56 in ptr
|
|
tok_ref = planes(post, ptr[32], wd, ht, c)
|
|
mer_ref = planes(post, ptr[40], wd // 2, ht // 2, c_m) if c_m else None
|
|
img = lambda buf, off, w_, h_: np.frombuffer(bytes(buf[off:off + w_ * h_ * 8]) + bytes(8), H)[:w_ * h_ * 4].reshape(h_, w_, 4)
|
|
guide, out_ref = (img(pre, ptr[48], wd, ht), img(post, ptr[64], wd, ht)) if guided else (None, None)
|
|
half_ref = img(post, ptr[72], wd // 2, ht // 2) if c_m else None
|
|
if dec_guide: # the guide one level down (argument 48), the encoder's of this level (56), the output (64)
|
|
g_low, g_enc, out_ref = img(pre, ptr[48], wd // 2, ht // 2), img(pre, ptr[56], wd, ht), img(post, ptr[64], wd, ht)
|
|
ok = True
|
|
for blk in o.blocks:
|
|
bx, by = (int(v) for v in blk.split(','))
|
|
x0, y0 = 8 * bx - sx, 8 * by - sy
|
|
inside = np.array([0 <= x0 + t % 8 < wd and 0 <= y0 + t // 8 < ht for t in range(64)])
|
|
xin = np.zeros((64, 4 * c_up if c_up else c), np.uint8)
|
|
xe = None
|
|
if expand:
|
|
lows = np.array([np.concatenate([up[p, mirror(y0 // 2 + n // 4, ht // 2), mirror(x0 // 2 + n % 4, wd // 2)]
|
|
for p in range(c_lo // 32)]) for n in range(16)])
|
|
e = mm(lows, w['we'], np.tile(w['be'], (16, 1)))
|
|
un = np.argsort(inter(c))
|
|
xe = np.zeros((64, c), H)
|
|
for t in range(64):
|
|
tx, ty = t % 8, t // 8
|
|
sub = (ty % 2) * 2 + tx % 2
|
|
sk = np.concatenate([skip[p, mirror(y0 + ty, ht), mirror(x0 + tx, wd)] for p in range(c // 32)])
|
|
xe[t] = add(dq(e[(ty // 2) * 4 + tx // 2, sub * c:(sub + 1) * c]), (e4m3(sk)[un].astype(H) * s3).astype(H))
|
|
for t in range(64 if not expand else 0): # tokens outside the level are the mirrored ones (no edge repeat), as in presets D and E
|
|
X, Y = mirror(x0 + t % 8, wd), mirror(y0 + t // 8, ht)
|
|
if not c_up:
|
|
xin[t] = np.concatenate([up[p, Y, X] for p in range(c // 32)])
|
|
continue
|
|
for q, (dx, dy) in enumerate(((0, 0), (1, 0), (0, 1), (1, 1))):
|
|
xin[t, q * c_up:(q + 1) * c_up] = np.concatenate([up[p, 2 * Y + dy, 2 * X + dx] for p in range(c_up // 32)])
|
|
y, merged, head = window(w, scal, xin, xe)
|
|
ya, ma = A(y), (A(merged) if c_m else None)
|
|
bad_t = bad_m = bad_o = bad_h = 0
|
|
out = np.zeros((64, 4), H)
|
|
for t in np.nonzero(inside)[0]:
|
|
X, Y = x0 + t % 8, y0 + t // 8
|
|
bad_t += int(np.sum(np.concatenate([tok_ref[p, Y, X] for p in range(c // 32)]) != ya[t]))
|
|
if dec_guide:
|
|
# out = min(enc * (1 - a) + (upsampled * sigmoid(h[0:3]) * s0) * a, 65504), a = sigmoid(h[3])
|
|
sg = sigmoid(head[t, :4])
|
|
gate = np.array([(sg[0] * scal[0]).astype(H), (sg[1] * scal[0]).astype(H), (sg[2] * scal[0]).astype(H), H(0)])
|
|
gt = (upsample(g_low, X, Y) * gate).astype(H)
|
|
gt[3] = H(0)
|
|
mix = add((g_enc[Y, X] * (H(1) - sg[3])).astype(H), (gt * sg[3]).astype(H))
|
|
out[t] = np.minimum(mix, H(65504))
|
|
bad_o += int(np.sum(out[t].view(np.uint16) != out_ref[Y, X].view(np.uint16)))
|
|
if not guided:
|
|
continue
|
|
gate = (sigmoid(head[t, :3]) * scal[0]).astype(H)
|
|
out[t, :3] = np.minimum((guide[Y, X, :3] * gate).astype(H), H(65504))
|
|
bad_o += int(np.sum(out[t].view(np.uint16) != out_ref[Y, X].view(np.uint16)))
|
|
for n in range(16):
|
|
X, Y = x0 // 2 + n % 4, y0 // 2 + n // 4
|
|
t0 = (2 * (n // 4)) * 8 + 2 * (n % 4)
|
|
if not c_m or not (inside[t0] and x0 % 2 == 0 and y0 % 2 == 0):
|
|
continue
|
|
bad_m += int(np.sum(np.concatenate([mer_ref[p, Y, X] for p in range(c_m // 32)]) != ma[n]))
|
|
qv = [(out[t0 + d] * H(0.25)).astype(H) for d in (0, 1, 8, 9)]
|
|
bad_h += int(np.sum(add(add(qv[0], qv[1]), add(qv[2], qv[3])).view(np.uint16) != half_ref[Y, X].view(np.uint16)))
|
|
print(f'block {blk}: {int(inside.sum())} tokens inside; differing tokens bytes {bad_t}, merged bytes {bad_m}, '
|
|
f'guide values {bad_o}, half guide values {bad_h}')
|
|
ok &= bad_t + bad_m + bad_o + bad_h == 0
|
|
print('IDENTICAL' if ok else 'DIFFERENT')
|
|
sys.exit(0 if ok else 1)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|