Uh oh!
There was an error while loading. Please reload this page.
feat(oracle): PyTorch-as-oracle per-op parity harness -- exchange format + NGC runner (ADR 091, T1.3) - #131
Merged
Merged
Conversation
…oss-precision reuse Generify the opNode wrappers and constructors over tensor.Float and add NewRegistryNode[T], the single source of truth for registry constructor arguments, so the PyTorch-oracle harness (T1.3) can run the exact same op definitions at float32 without duplicating them. Registry() now routes its Make closures through the factory. No behavior change at float64. Refs #128, zerfoo ADR 091.
…fixture (T1.3) testing/oracle implements the ztensor side of the PyTorch-as-oracle harness: - bundle.go: format_version 1 case bundles -- manifest.json (op, torch expression, shapes, dtypes f32/f64, seed, per-op tolerances) plus raw little-endian row-major tensor files (inputs, params, upstream gradient, ztensor forward output, input/parameter gradients). Writer + reader. - torchmap.go: op -> torch expression table covering 25/26 registry ops; HadamardTransform skipped with reason. Per-op tolerance overrides. - generate.go + cmd/oracle-gen: runs every gradcheck registry op forward+backward on the float32 CPU engine with seeded inputs/upstream and dumps one bundle per op (GPU-engine variant reuses the same format). - diff.go: the exact pass/fail logic mirrored by scripts/oracle/run_oracle.py (|got-ref| <= atol + rtol*|ref|, NaN always fails). - redproof_test.go: CI red proof for the ztensor#125 fast-math tanh class -- a bundle recorded with an unsaturated tanh (growth past |x|>9) MUST fail the diff vs math.Tanh ground truth, and the saturating twin passes. - bundle_test.go / generate_test.go: endianness pin, f32/f64 round-trips, registry<->mapping lockstep, full-generation validation, determinism. Refs #128, zerfoo ADR 091.
… (T1.3) scripts/oracle/run_oracle.py (stdlib+numpy+torch only, offline) replays the case bundles in PyTorch, backprops the recorded upstream, and writes a report.json with per-op max abs/rel diffs for forward and every gradient, pass/fail vs the manifest tolerances; exit 1 on any failure. oracle-pod.yaml runs it in nvcr.io/nvidia/pytorch:26.02-py3 on the GB10 with nvidia.com/gpu: 1 and a memory limit via the Spark HTTP API; README.md documents generate -> rsync -> submit -> report. Verified locally end-to-end against torch 2.11 CPU: 25/25 bundles pass; a corrupted forward fails with exit 1. Refs #128, zerfoo ADR 091.
…h oracle) Refs #128, zerfoo ADR 091.
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements T1.3 of the GPU training-stack hardening plan (zerfoo
docs/plan-gpu-training-hardening.md, decision rationale zerfoo ADR 091): the PyTorch-as-oracle per-op parity harness. The same op, same inputs, through ztensor and through torch (innvcr.io/nvidia/pytorch:26.02-py3on the DGX GB10), diffing forward AND backward within per-op tolerances. Catches numerics-convention divergence (fast-math, reduction ordering, eps placement) that ztensor's CPU and GPU engines could share and gradcheck cannot see. Test infrastructure only — the production stack stays pure Go.Refs #128. Builds on the merged gradcheck registry (#129) and ADR 006.
Bundle format (format_version 1)
One directory per op case (authoritative spec:
testing/oracle/bundle.go):Tensor files are raw little-endian IEEE-754, row-major, no header; the manifest records shape and dtype (
float32/float64, numpy names). Pass criterion per element, identical intesting/oracle/diff.goandscripts/oracle/run_oracle.py:|ztensor − torch| ≤ atol + rtol·|torch|; any NaN fails. First cut records the f32 CPU engine; the GPU-engine variant regenerates bundles through the same format on the DGX.To run the registry at f32 without duplicating op definitions, the gradcheck op wrappers are now generic over
tensor.Float, andgradcheck.NewRegistryNode[T]is the single source of truth for constructor arguments (slopes, axes, reshape targets, layernorm width/eps).Registry()routes through it; no behavior change at float64. A CI test keeps the mapping table and the registry in lockstep.Op → torch mapping (25 mapped, 1 skipped)
x0 + x1/x0 - x1/x0 * x1/x0 / x1torch.pow(x0, x1)torch.tanh(x0)/torch.sigmoid(x0)/torch.relu(x0)torch.nn.functional.leaky_relu(x0, 0.1)torch.exp(x0)etc.x0 + 0.7/x0 * -1.3x0 @ x1x0.transpose(0, 1)x0.reshape(3, 2)torch.softmax(x0, 1)x0.sum(dim=1, keepdim=True)/x0.mean(dim=1, keepdim=True)x0.amax(dim=1, keepdim=True)(no-ties sampling is load-bearing)torch.nn.functional.layer_norm(x0, (4,), weight=gamma.reshape(4), bias=beta.reshape(4), eps=1e-05)SKIPPED:
HadamardTransform— torch has no built-in normalized Walsh-Hadamard transform; hand-building the H matrix in the runner would test our own reimplementation rather than torch.Red proof (CI-side, no torch dependency)
testing/oracle/redproof_test.goencodes the ztensor#125 fast-math tanh class: a Tanh bundle whose recorded "ztensor output" comes from an unsaturated tanh approximation (grows past|x| > 9instead of clamping to ±1, like the raw cubic-GELU tanh-arg overflow). The Go reference checker — the same diff logic as the Python runner, withmath.Tanhstanding in fortorch.tanh— must flag it red in both forward and backward (TestRedProofFastMathTanhFails), and the saturating twin must pass green (TestGreenProofTrueTanhPasses). NaN policy pinned byTestDiffNaNFails.Verified locally (beyond CI)
End-to-end against real torch 2.11 on CPU:
oracle-gen→run_oracle.pygives 25/25 PASS (max fwd diff 4.1e-6 on Exp, everything else ≤2.4e-7); corrupting one forward element makes the runner report FAIL and exit 1. Round-trip, endianness pin (f321.0=00 00 80 3F), determinism (two generations byte-identical), and full-suitego test -race ./...green.DGX run procedure (the lead runs this serially — NOT run in this PR)
Full procedure + GPU-engine next cut:
scripts/oracle/README.md. This harness is the gate for the kernel-numerics work (plan T3.1–T3.4).