The endorsed design: the I-frame is the LEARNED integer image codec int_hyper (wai.neural.int_hyper), decoded byte-exact, and the P-frames are integer block-MC residuals on top — so the whole video decode is integer and byte-identical on every machine. A learned keyframe you can put in a receipt, plus integer inter-frames. - int_video::decode_pframes(i_frame, h, w, bs): the P-frame loop seeded with ANY decoded I-frame (integer MC from the previous reconstructed frame + quantized residual over rANS). Decouples the inter-frame codec from the keyframe source. - The NEURAL-KEYFRAME path = int_hyper_synth::decode_conditional (frame 0) ∘ decode_pframes (the rest). Both pure i64; the composition lives in the test/caller. - tools/wai_int_video_keyframe_encode.py: uses the committed tiny int_hyper vector as frame 0 (so the conformance is self-contained, never-skips), translates it for the P-frames, codes the residual. Emits only the P-frame bitstream + expected hashes (frame 0's hash IS int_hyper's, 7cbe882c). - Committed vector (tests/vectors/int_video/video_nk*) + NEVER-skip test (gated on neural_int+video_int): decodes the I-frame via int_hyper then the P-frames, every frame byte-for-byte + clip hash a5f53d477999d159; frame 0 == the int_hyper image. - Wired wai.video.int_hyper: NeuralIntegerExact (embeds the learned integer codec), Lossy tier. `wai capabilities` → 58, 3 integer-exact (int_synth, int_hyper, video). SPEC table + capability comment. Co-Authored-By: Claude Opus 4.8 <[email protected]>
134 lines
6.7 KiB
Python
134 lines
6.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate a TINY, self-contained `wai.neural.int_hyper` conformance vector.
|
|
|
|
The SOTA int_hyper vector needs a ~23 MB PTQ model, too large to commit — so the
|
|
Rust conformance test SKIPS without it, and CI never exercises the decode. This
|
|
emits a SMALL synthetic conditional codec (deterministic random integer weights)
|
|
that drives the SAME decode path end to end — factorized z -> integer hyper-
|
|
decoder h_s -> integer-sigma entropy bucketing -> integer rANS -> integer
|
|
synthesis g_s -> RGB — with a committed expected FNV-1a. A few KB, so it commits
|
|
and the int_hyper decode is CI-verifiable with no external artifacts.
|
|
|
|
python tools/wai_int_hyper_tiny_vector.py <out-dir>
|
|
|
|
The numbers are not a "real" codec (random weights); the point is a deterministic
|
|
exercise of every integer op (convT / conv / igdn / leaky / factorized-z /
|
|
conditional-y / rANS), byte-reproducible on every machine.
|
|
"""
|
|
import sys, os, json
|
|
import numpy as np
|
|
from compressai._CXX import pmf_to_quantized_cdf
|
|
from compressai.ans import RansEncoder
|
|
|
|
# Canonical integer-net runner + helpers (the same the SOTA export uses).
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
from wai_sota_int_synth_export import run_int_net, det_build_index_vec, fnv1a, model_to_bin
|
|
|
|
AFRAC = 12; GF = 16; SHIFT = 24
|
|
SCALE = 1 << SHIFT
|
|
|
|
|
|
def quant_cdf(pmf):
|
|
"""CompressAI quantized CDF for a pmf over n symbols (+ 1 tail) -> len n+2."""
|
|
tail = max(1e-6, 1.0 - sum(pmf))
|
|
return list(pmf_to_quantized_cdf([float(p) for p in pmf] + [tail], 16))
|
|
|
|
|
|
def gauss_pmf(sigma, n):
|
|
c = n / 2.0
|
|
p = np.exp(-((np.arange(n) - c) ** 2) / (2.0 * sigma * sigma))
|
|
return (p / p.sum()).tolist()
|
|
|
|
|
|
def convT(rng, cin, cout, k, stride, pad, opad):
|
|
w = rng.integers(-160, 160, (cin, cout, k, k), dtype=np.int64)
|
|
mult = rng.integers(SCALE >> 7, SCALE >> 6, cout, dtype=np.int64) # scale ~1/128..1/64
|
|
bias = rng.integers(-30, 30, cout, dtype=np.int64)
|
|
return {'type': 'convT', 'in_ch': cin, 'out_ch': cout, 'k': k, 'stride': stride, 'pad': pad,
|
|
'opad': opad, 'weight': w.reshape(-1).tolist(), 'mult': mult.tolist(), 'bias': bias.tolist()}
|
|
|
|
|
|
def conv(rng, cin, cout, k, stride, pad):
|
|
w = rng.integers(-160, 160, (cout, cin, k, k), dtype=np.int64)
|
|
mult = rng.integers(SCALE >> 7, SCALE >> 6, cout, dtype=np.int64)
|
|
bias = rng.integers(-30, 30, cout, dtype=np.int64)
|
|
return {'type': 'conv', 'in_ch': cin, 'out_ch': cout, 'k': k, 'stride': stride, 'pad': pad,
|
|
'weight': w.reshape(-1).tolist(), 'mult': mult.tolist(), 'bias': bias.tolist()}
|
|
|
|
|
|
def igdn(rng, c):
|
|
gamma = rng.integers(0, max(2, (1 << GF) // (4 * c)), (c, c), dtype=np.int64)
|
|
beta = rng.integers(1 << (2 * AFRAC - 1), 1 << (2 * AFRAC), c, dtype=np.int64) # positive
|
|
return {'type': 'igdn', 'c': c, 'gamma': gamma.reshape(-1).tolist(), 'beta': beta.tolist()}
|
|
|
|
|
|
def main(outdir):
|
|
os.makedirs(outdir, exist_ok=True)
|
|
rng = np.random.default_rng(20260625) # fixed seed -> deterministic vector
|
|
M, N = 4, 2 # tiny y / z channel counts
|
|
zh = zw = 2 # z spatial; y spatial = 4; image = 16
|
|
# tiny synthesis g_s: convT -> igdn -> convT (-> 3 RGB)
|
|
g_s = {'afrac': AFRAC, 'gf': GF, 'shift': SHIFT, 'layers': [
|
|
convT(rng, M, M, 3, 2, 1, 1), igdn(rng, M), convT(rng, M, 3, 3, 2, 1, 1)]}
|
|
# tiny hyper-decoder h_s: convT -> leaky -> conv (-> 2M = sigma||mu)
|
|
h_s = {'afrac': AFRAC, 'gf': GF, 'shift': SHIFT, 'layers': [
|
|
convT(rng, N, M, 3, 2, 1, 1), {'type': 'leaky', 'slope_q': 655, 'slope_frac': 16},
|
|
conv(rng, M, 2 * M, 3, 1, 1)]}
|
|
|
|
# entropy tables — y Gaussian-conditional buckets + z factorized (one CDF/channel)
|
|
scale_table = [1.0, 2.0, 4.0, 8.0]
|
|
edges = np.floor(np.array(scale_table) * (1 << AFRAC)).astype(np.int64)
|
|
NY = 16
|
|
cdfs = [quant_cdf(gauss_pmf(s, NY)) for s in scale_table]
|
|
cdf_lengths = [len(c) for c in cdfs]
|
|
offsets = [-NY // 2] * len(scale_table)
|
|
NZ = 16
|
|
z_cdf = quant_cdf(gauss_pmf(2.0, NZ))
|
|
z_cdfs = [z_cdf for _ in range(N)]
|
|
z_cdf_lengths = [len(z_cdf)] * N
|
|
z_offsets = [-NZ // 2] * N
|
|
z_median_q = [0] * N
|
|
|
|
# bitstream symbols (deterministic), kept in range of the tiny CDFs
|
|
z_sym = rng.integers(-3, 4, (N, zh, zw), dtype=np.int64) # factorized z symbols
|
|
z_q = (z_sym << AFRAC) # + median_q (0)
|
|
sm = run_int_net(h_s, z_q) # [2M, 4, 4]
|
|
sigma_int, mu_int = sm[:M], sm[M:]
|
|
sh, sw = sigma_int.shape[1], sigma_int.shape[2]
|
|
resid = rng.integers(-3, 4, (M, sh, sw), dtype=np.int64) # y residuals
|
|
indexes = det_build_index_vec(sigma_int, edges)
|
|
y_hat = (resid << AFRAC) + mu_int
|
|
rgb_chw = run_int_net(g_s, y_hat) # [3, 16, 16] @ 2^afrac
|
|
one = 1 << AFRAC
|
|
out_c = np.clip(rgb_chw, 0, one)
|
|
pix = (((out_c * 255 + (1 << (AFRAC - 1))) >> AFRAC)).clip(0, 255).astype(np.uint8)
|
|
rgb = np.ascontiguousarray(pix.transpose(1, 2, 0)) # HWC u8
|
|
rgb_hash = format(fnv1a(rgb.tobytes()), '016x')
|
|
|
|
enc = RansEncoder()
|
|
zc = np.repeat(np.arange(N), zh * zw)
|
|
z_bits = enc.encode_with_indexes(z_sym.reshape(-1).tolist(), zc.tolist(), z_cdfs, z_cdf_lengths, z_offsets)
|
|
enc2 = RansEncoder()
|
|
y_bits = enc2.encode_with_indexes(resid.reshape(-1).tolist(), indexes.reshape(-1).tolist(),
|
|
cdfs, cdf_lengths, offsets)
|
|
|
|
json.dump(g_s, open(f'{outdir}/tiny_g_s.json', 'w'))
|
|
json.dump(h_s, open(f'{outdir}/tiny_h_s.json', 'w'))
|
|
open(f'{outdir}/tiny_g_s.bin', 'wb').write(model_to_bin(g_s))
|
|
open(f'{outdir}/tiny_h_s.bin', 'wb').write(model_to_bin(h_s))
|
|
json.dump({'afrac': AFRAC, 'frac': AFRAC, 'scale_edges': edges.tolist(),
|
|
'cdfs': cdfs, 'cdf_lengths': cdf_lengths, 'offsets': offsets,
|
|
'z_cdfs': z_cdfs, 'z_cdf_lengths': z_cdf_lengths, 'z_offsets': z_offsets,
|
|
'z_median_q': z_median_q}, open(f'{outdir}/tiny_entropy.json', 'w'))
|
|
json.dump({'m': M, 'sh': int(sh), 'sw': int(sw), 'n_z': N, 'zh': zh, 'zw': zw,
|
|
'z_rans': list(z_bits), 'rans': list(y_bits)}, open(f'{outdir}/tiny_bitstream.json', 'w'))
|
|
json.dump({'rgb_hash': rgb_hash, 'out_c': 3, 'out_h': int(pix.shape[1]), 'out_w': int(pix.shape[2])},
|
|
open(f'{outdir}/tiny.json', 'w'))
|
|
sz = sum(os.path.getsize(f'{outdir}/{f}') for f in os.listdir(outdir))
|
|
print(f'tiny int_hyper vector: image {pix.shape[1]}x{pix.shape[2]}, '
|
|
f'{len(z_bits)} z + {len(y_bits)} y rANS bytes, rgb_hash {rgb_hash}, total {sz} B')
|
|
return rgb, int(pix.shape[1]), int(pix.shape[2]) # the decoded I-frame, for the video keyframe encoder
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main(sys.argv[1])
|