ticket #000036: add KAT-regen tooling + close
"One more iteration then close" (fox): added committed KAT-regeneration
scripts for both the T3 calculator and φ_PRG — the regen step was a
throwaway temp script before; now it's reproducible and the phi_prg
test's skipif reason ("run scripts/generate_phi_prg_kat.py") points at
a file that exists. Then closed #000036.
New scripts:
- scripts/generate_t3_bound_kat.py — regenerates
bench/fixtures/t3-bound/known-answer-tests.jsonl from a fixed 12-config
list (the §7 worked examples under max_envelope + non-default-C_B*
+ g=0 edge + explicit-b1_model pins for the other three models).
- scripts/generate_phi_prg_kat.py — regenerates
bench/fixtures/phi-prg/known-answer-tests.jsonl from a fixed 10-entry
list (placeholder/random seeds, one-bit-flip variants, block-boundary
dim_h=16/17, 4096 counter-rollover stress).
- Both verified to reproduce the committed fixture data lines byte-
for-byte (only the header comments changed, to reference the script).
Each docstring states: run after any algorithm change, then bump the
module version (CALCULATOR_VERSION / PHI_PRG_VERSION) so the fixture's
version field changes too.
Doc/test:
- test_t3_bound_calculator.py skipif reason now references the regen
script (matches the phi_prg test pattern).
- #000035 §3.3 + t3-bound.md §10.1 reference the regen scripts.
Closure (#000036):
- Status → closed · 2026-05-11 in the ticket file + TICKETS.md row.
Phase 1 + dav1d Tier-1/Tier-2 (Option B in v1) + KAT-regen tooling
all landed; all §5 acceptance criteria met; both dav1d closure
blockers cleared. Continuation: empirical C_B1/C_B2/C_B3 tightening
under #000043 (parks on v7 deployment data); landing the bound's
framing into a v7 plastic-training spec parks on that spec gaining
a deployment target; R2's architectural integrations (Merkle audit-
event commitment, SQD canonicalization, CTI clause-lattice, 5F
trigger, ForkScore security-risk) are separate tickets if wanted.
- t3-bound.md header flipped to "closed 2026-05-11".
Full suite: 2312 passed, 28 skipped.
This commit is contained in:
parent
101b101281
commit
8599ce3b2c
9 changed files with 298 additions and 29 deletions
115
scripts/generate_phi_prg_kat.py
Normal file
115
scripts/generate_phi_prg_kat.py
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Regenerate the φ_PRG anchor-map known-answer-test fixture.
|
||||
|
||||
Writes ``bench/fixtures/phi-prg/known-answer-tests.jsonl`` from the
|
||||
fixed (seed, hard_hash, dim_h) list below. Each entry records the
|
||||
SHA-256 of the raw HMAC-SHA-512 expansion output (before float
|
||||
conversion) — the durable contract; the float layout can change
|
||||
without invalidating the fixture (the byte stream doesn't).
|
||||
|
||||
Run this whenever the φ_PRG algorithm changes — and then bump
|
||||
``PHI_PRG_VERSION`` in ``arborist/substrate/anchor_prg.py`` so the
|
||||
fixture's ``version`` field changes too. The KAT regression test
|
||||
(``tests/test_anchor_prg.py::test_phi_prg_known_answer_tests``)
|
||||
pins these values and asserts the version matches the module.
|
||||
|
||||
Usage::
|
||||
|
||||
python -m scripts.generate_phi_prg_kat # from repo root
|
||||
# or: python scripts/generate_phi_prg_kat.py
|
||||
|
||||
Config list (10 entries): the placeholder seed against zero / all-
|
||||
ones hashes at several dim_h; a random (seed, hash) pair with two
|
||||
one-bit-flip variants (avalanche cross-check); block-boundary cases
|
||||
(dim_h=16 = exactly one HMAC block; dim_h=17 = two blocks with
|
||||
truncation); a 4096-element counter-rollover stress sample. Note:
|
||||
at counter=0 the bytes are identical regardless of endianness, so
|
||||
the single-block (dim_h ≤ 16) entries are endianness-invariant; the
|
||||
multi-block entries pin the little-endian counter encoding.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))
|
||||
|
||||
from arborist.substrate.anchor_prg import _expand, PHI_PRG_VERSION # noqa: E402
|
||||
|
||||
_FIXTURE = (
|
||||
pathlib.Path(__file__).resolve().parents[1]
|
||||
/ "bench" / "fixtures" / "phi-prg" / "known-answer-tests.jsonl"
|
||||
)
|
||||
|
||||
# (label, seed_hex, hard_hash_hex, dim_h)
|
||||
_ENTRIES: list[tuple[str, str, str, int]] = [
|
||||
("placeholder-seed/zero-hash/dim_h=1",
|
||||
"ef532720a49159beb6816d98e13a162bac63c531b631bd1adb0fcca96b467ff3",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", 1),
|
||||
("placeholder-seed/zero-hash/dim_h=8",
|
||||
"ef532720a49159beb6816d98e13a162bac63c531b631bd1adb0fcca96b467ff3",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", 8),
|
||||
("placeholder-seed/zero-hash/dim_h=32",
|
||||
"ef532720a49159beb6816d98e13a162bac63c531b631bd1adb0fcca96b467ff3",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", 32),
|
||||
("placeholder-seed/all-ones-hash/dim_h=16",
|
||||
"ef532720a49159beb6816d98e13a162bac63c531b631bd1adb0fcca96b467ff3",
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16),
|
||||
("seed=A/hash=B/dim_h=64",
|
||||
"14ab2dab0d3cddeaa58ec70632d3ff4f5de2c514004a92144e260dfe384d912e",
|
||||
"a23cb10b94660f062b467f313a9dc9d84f2dc2748c3627c661082dda7f55e3bb", 64),
|
||||
("seed=A/hash=B'/dim_h=64 (one-bit-flip from prior)",
|
||||
"14ab2dab0d3cddeaa58ec70632d3ff4f5de2c514004a92144e260dfe384d912e",
|
||||
"223cb10b94660f062b467f313a9dc9d84f2dc2748c3627c661082dda7f55e3bb", 64),
|
||||
("seed=A'/hash=B/dim_h=64 (one-bit-flip seed)",
|
||||
"94ab2dab0d3cddeaa58ec70632d3ff4f5de2c514004a92144e260dfe384d912e",
|
||||
"a23cb10b94660f062b467f313a9dc9d84f2dc2748c3627c661082dda7f55e3bb", 64),
|
||||
("block-boundary/dim_h=16",
|
||||
"cfd60c2bda64ebcefbb23a5b28d98269c9c4f8b8ac77f6f9ca7a0f4865b10f58",
|
||||
"724cd966a7bfe78ba802877510ffb90c67f385a1d3135e4e1b8a1b38f744c6da", 16),
|
||||
("block-boundary/dim_h=17",
|
||||
"cfd60c2bda64ebcefbb23a5b28d98269c9c4f8b8ac77f6f9ca7a0f4865b10f58",
|
||||
"724cd966a7bfe78ba802877510ffb90c67f385a1d3135e4e1b8a1b38f744c6da", 17),
|
||||
("stress/dim_h=4096",
|
||||
"0ddd62c311f88ebe2d4f6cd5d9d1374474dfd645e012043648dd966a71785c95",
|
||||
"e605ede3d9d0d13c6d7d32c5c424b998677eef0689a0d9f0fa4ebd1bb4307cb9", 4096),
|
||||
]
|
||||
|
||||
|
||||
def build_lines() -> list[str]:
|
||||
lines = [
|
||||
f"# arborist v7 phi_prg known-answer tests — version {PHI_PRG_VERSION}",
|
||||
"# Regenerated by scripts/generate_phi_prg_kat.py. Records SHA-256 of",
|
||||
"# the raw HMAC-SHA-512 (little-endian counter-mode) expansion output,",
|
||||
"# before float conversion. Algorithm change MUST bump PHI_PRG_VERSION",
|
||||
"# and re-run this script; old runs replay against the old fixture.",
|
||||
]
|
||||
for label, seed_hex, hh_hex, dim_h in _ENTRIES:
|
||||
seed = bytes.fromhex(seed_hex)
|
||||
hh = bytes.fromhex(hh_hex)
|
||||
raw = _expand(seed, hh, dim_h * 4)
|
||||
lines.append(json.dumps({
|
||||
"label": label,
|
||||
"version": PHI_PRG_VERSION,
|
||||
"seed_hex": seed_hex,
|
||||
"hard_hash_hex": hh_hex,
|
||||
"dim_h": dim_h,
|
||||
"output_sha256": hashlib.sha256(raw).hexdigest(),
|
||||
"output_bytes": dim_h * 4,
|
||||
}, ensure_ascii=False))
|
||||
return lines
|
||||
|
||||
|
||||
def main() -> int:
|
||||
lines = build_lines()
|
||||
_FIXTURE.parent.mkdir(parents=True, exist_ok=True)
|
||||
_FIXTURE.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
print(f"wrote {len(_ENTRIES)} KAT entries to {_FIXTURE} (version {PHI_PRG_VERSION})")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
145
scripts/generate_t3_bound_kat.py
Normal file
145
scripts/generate_t3_bound_kat.py
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Regenerate the T3-bound calculator known-answer-test fixture.
|
||||
|
||||
Writes ``bench/fixtures/t3-bound/known-answer-tests.jsonl`` from the
|
||||
fixed config list below by running ``t3_bound_bits`` on each and
|
||||
recording the full output. Run this whenever the calculator's
|
||||
algorithm changes — and then bump ``CALCULATOR_VERSION`` in
|
||||
``bench/scripts/t3_bound_calculator.py`` so the fixture's
|
||||
``calculator_version`` field changes too. The KAT regression test
|
||||
(``tests/test_t3_bound_calculator.py::test_t3_bound_known_answer_tests``)
|
||||
pins these values.
|
||||
|
||||
Usage::
|
||||
|
||||
python -m scripts.generate_t3_bound_kat # from repo root
|
||||
# or: python scripts/generate_t3_bound_kat.py
|
||||
|
||||
Config list rationale (12 entries):
|
||||
|
||||
- ``small-deployment-§7.1`` / ``medium-deployment-§7.2`` /
|
||||
``hardened-deployment-§7.3`` — the worked examples in
|
||||
``docs/soft-hash-channel-t3-bound.md`` §7, under the default
|
||||
``b1_model=max_envelope``.
|
||||
- ``extreme-low-g`` / ``tight-window-W=100`` — corner regimes.
|
||||
- ``tightened-c-b1`` / ``all-constants-tight`` — non-default
|
||||
C_B* overrides (replay-completeness for the constants path).
|
||||
- ``b3-floor-regime`` — exercises the B3 floor-at-zero branch.
|
||||
- ``gradient-fraction-zero`` — the g=0 component-isolation case
|
||||
(B1=0; B2+B3 still fire).
|
||||
- ``b1-effective-control-v1-mode`` / ``b1-fraction-channels-mode``
|
||||
/ ``b1-aggregate-bias-mode`` — explicit ``b1_model`` pins so the
|
||||
non-default models are regression-guarded.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
# Allow ``python scripts/generate_t3_bound_kat.py`` from repo root.
|
||||
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))
|
||||
|
||||
from bench.scripts.t3_bound_calculator import ( # noqa: E402
|
||||
CALCULATOR_VERSION,
|
||||
t3_bound_bits,
|
||||
)
|
||||
|
||||
_FIXTURE = (
|
||||
pathlib.Path(__file__).resolve().parents[1]
|
||||
/ "bench" / "fixtures" / "t3-bound" / "known-answer-tests.jsonl"
|
||||
)
|
||||
|
||||
# (label, inputs-dict). inputs may include c_b1/c_b2/c_b3/b1_model.
|
||||
_CONFIGS: list[tuple[str, dict]] = [
|
||||
("small-deployment-§7.1", dict(
|
||||
gradient_fraction=0.05, gradient_norm_max=1.0, gradient_noise_stddev=0.1,
|
||||
lr_decision_interval=100, lr_grid_size=8, window_length=10000,
|
||||
batches_per_epoch=1024, steps_per_epoch=1024)),
|
||||
("medium-deployment-§7.2", dict(
|
||||
gradient_fraction=0.05, gradient_norm_max=1.0, gradient_noise_stddev=0.1,
|
||||
lr_decision_interval=1000, lr_grid_size=16, window_length=100000,
|
||||
batches_per_epoch=8192, steps_per_epoch=8192)),
|
||||
("hardened-deployment-§7.3", dict(
|
||||
gradient_fraction=0.01, gradient_norm_max=1.0, gradient_noise_stddev=0.1,
|
||||
lr_decision_interval=100, lr_grid_size=4, window_length=10000,
|
||||
batches_per_epoch=1024, steps_per_epoch=1024)),
|
||||
("extreme-low-g", dict(
|
||||
gradient_fraction=0.001, gradient_norm_max=1.0, gradient_noise_stddev=0.5,
|
||||
lr_decision_interval=500, lr_grid_size=2, window_length=5000,
|
||||
batches_per_epoch=256, steps_per_epoch=256)),
|
||||
("tight-window-W=100", dict(
|
||||
gradient_fraction=0.05, gradient_norm_max=2.0, gradient_noise_stddev=0.1,
|
||||
lr_decision_interval=50, lr_grid_size=4, window_length=100,
|
||||
batches_per_epoch=64, steps_per_epoch=64)),
|
||||
("tightened-c-b1", dict(
|
||||
gradient_fraction=0.05, gradient_norm_max=1.0, gradient_noise_stddev=0.1,
|
||||
lr_decision_interval=100, lr_grid_size=8, window_length=10000,
|
||||
batches_per_epoch=1024, steps_per_epoch=1024, c_b1=0.5)),
|
||||
("all-constants-tight", dict(
|
||||
gradient_fraction=0.05, gradient_norm_max=1.0, gradient_noise_stddev=0.1,
|
||||
lr_decision_interval=100, lr_grid_size=8, window_length=10000,
|
||||
batches_per_epoch=1024, steps_per_epoch=1024, c_b1=0.1, c_b2=0.1, c_b3=0.1)),
|
||||
("b3-floor-regime", dict(
|
||||
gradient_fraction=0.05, gradient_norm_max=100.0, gradient_noise_stddev=0.001,
|
||||
lr_decision_interval=100, lr_grid_size=8, window_length=10000,
|
||||
batches_per_epoch=4, steps_per_epoch=4)),
|
||||
("gradient-fraction-zero", dict(
|
||||
gradient_fraction=0.0, gradient_norm_max=1.0, gradient_noise_stddev=0.1,
|
||||
lr_decision_interval=100, lr_grid_size=8, window_length=10000,
|
||||
batches_per_epoch=1024, steps_per_epoch=1024)),
|
||||
("b1-effective-control-v1-mode", dict(
|
||||
gradient_fraction=0.05, gradient_norm_max=1.0, gradient_noise_stddev=0.1,
|
||||
lr_decision_interval=100, lr_grid_size=8, window_length=10000,
|
||||
batches_per_epoch=1024, steps_per_epoch=1024, b1_model="effective_control_v1")),
|
||||
("b1-fraction-channels-mode", dict(
|
||||
gradient_fraction=0.05, gradient_norm_max=1.0, gradient_noise_stddev=0.1,
|
||||
lr_decision_interval=100, lr_grid_size=8, window_length=10000,
|
||||
batches_per_epoch=1024, steps_per_epoch=1024, b1_model="fraction_channels")),
|
||||
("b1-aggregate-bias-mode", dict(
|
||||
gradient_fraction=0.05, gradient_norm_max=1.0, gradient_noise_stddev=0.1,
|
||||
lr_decision_interval=100, lr_grid_size=8, window_length=10000,
|
||||
batches_per_epoch=1024, steps_per_epoch=1024, b1_model="aggregate_bias")),
|
||||
]
|
||||
|
||||
|
||||
def build_lines() -> list[str]:
|
||||
lines = [
|
||||
f"# T3 bound calculator known-answer tests — version {CALCULATOR_VERSION}",
|
||||
"# Regenerated by scripts/generate_t3_bound_kat.py. Algorithm change",
|
||||
"# MUST bump CALCULATOR_VERSION and re-run this script; old runs",
|
||||
"# replay against the old fixture (do not delete history).",
|
||||
]
|
||||
for label, inp in _CONFIGS:
|
||||
r = t3_bound_bits(**inp)
|
||||
lines.append(json.dumps({
|
||||
"label": label,
|
||||
"calculator_version": r["calculator_version"],
|
||||
"b1_model": r["b1_model"],
|
||||
"inputs": r["inputs"],
|
||||
"expected_total": r["I_window_bits_upper_bound"],
|
||||
"expected_b1": r["B1_contribution"],
|
||||
"expected_b1_selected": r["b1_selected"],
|
||||
"expected_b1_fraction_channels": r["B1_fraction_channels"],
|
||||
"expected_b1_aggregate_bias": r["B1_aggregate_bias"],
|
||||
"expected_b1_effective_control_v1": r["B1_effective_control_v1"],
|
||||
"expected_b2": r["B2_contribution"],
|
||||
"expected_b3": r["B3_contribution"],
|
||||
"expected_snr_grad": r["snr_grad"],
|
||||
"expected_snr_per_channel": r["snr_per_channel"],
|
||||
"expected_certification_status": r["certification_status"],
|
||||
}, ensure_ascii=False))
|
||||
return lines
|
||||
|
||||
|
||||
def main() -> int:
|
||||
lines = build_lines()
|
||||
_FIXTURE.parent.mkdir(parents=True, exist_ok=True)
|
||||
_FIXTURE.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
print(f"wrote {len(_CONFIGS)} KAT entries to {_FIXTURE} (version {CALCULATOR_VERSION})")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Loading…
Add table
Add a link
Reference in a new issue