The uniform equivalence contract#
The governing correctness principle for the uniform T3 layer, and the most important of the uniform design notes: what
UniformTuckerTensorTrainis (a faster representation of ragged T3s, nothing more), and what “correct” means for every uniform operation. (How the contract drives the test strategy and the masking schedule is contributor material:contributor/testing_strategy.md,contributor/uniform_internals.md.)
The contract#
A
UniformTuckerTensorTrainis a faster representation of (a stack of) ragged Tucker tensor trains. For every operation with a ragged counterpart,to_uniform → op_uniform → to_ragged == op_raggedon the masked (“real”) parts only. The padded (“garbage”) parts are explicitly don’t-care.
That is the whole thing. The uniform layer exists for GPU efficiency and jit (no loop unrolling); it
introduces no new semantics. Anything a uniform op produces in the real blocks must match what the
ragged op would have produced; anything in the padding is free.
It holds element-wise in the stacked/variety case too (docs/uniform_ranks_and_varieties.md): a
varying-rank stack has no single ragged T3, but to_uniform(tree of ragged T3s) → op → to_ragged still
equals the tree of per-element ragged results. So the contract and the determinantal-variety view are
fully compatible — “faster ragged” is exactly “a batch of points moved the same way the ragged op would
move each one.”
The contract holds on non-minimal-rank input too: a uniform op does whatever the ragged op does there (see the contributor notes for how that case is pinned down and tested).
The vector I/O boundary — packedness mirror#
The sampling ops (probe / apply / entries and their derivatives) take mode-vectors across the
ragged/uniform seam, and probe returns them too. These can be carried two ways, and the uniform ops
infer the input’s packedness and mirror it in the output (the same “infer, don’t flag” rule as
numpy/jax and ragged/uniform dispatch):
ragged in → ragged out — a
len=dsequence of real-width vectors. Here the contract holds as written:op_uniform(ragged) == op_ragged.packed in → packed out — one supercore-shaped
(d,)+…+(N,)array (each mode zero-padded to the common widthN). Here the contract holds after unpacking:unpack(op_uniform(packed)) == op_ragged, equivalentlyop_uniform(packed) == pack(op_ragged).
So the contract is not weakened — it attaches to whichever form crossed the boundary. The packed form is
the inner-loop path (the optimizer keeps probe residuals packed end-to-end — no per-matvec
unpack/repack, and d stays a single scan-able axis rather than a Python list); the ragged form is the
drop-in “faster ragged” for one-off/interactive use. The user-facing ops (frontend methods included, by
delegation) mirror; the fitting split-seam *_from_sweep hooks are packed-only (the loop’s natural
mode). A backend user gets full parity via the mirroring ops plus the exposed pack_vectors /
unpack_vectors / pack_if_ragged helpers.
The padding convention is a prefix, and it is not obvious (internal rank edge-vectors scatter; only
the mode/shape padding is a prefix): real data sits in [0:Ni], zeros in [Ni:N], keyed on the shape
ints — no mask needed. pack/unpack encode exactly this, so packed reductions (e.g. sumsq_over_probes
over a packed array) are correct because the padding is inert zeros.
The fill must be finite. “Garbage don’t-care” implicitly assumes finite garbage: masking works by
multiplication, and 0 × NaN = NaN — padding filled with NaN/inf (“to be safe”) poisons masked
reductions and breaks correctness. Zeros are the robust fill, and packed vectors always travel with
their shape information (the fill is never used to infer shape).
Honest scope and limits#
Only user-facing ops with ragged twins are bound by the contract. The masking/padding plumbing (
ut3_make_masks,ut3_apply_masks, pack/unpack) is uniform-only — it is the machinery that makes the contract hold, not something the contract constrains.The uniform layer mirrors only the shape-stable subset of ragged ops.
rtol/atoltruncation has no uniform counterpart (data-dependent shapes would break uniformity/jit); uniform truncates via max-rank masks. So the SVD form of the contract isut3svd(max-rank mask) == t3svd(max_ranks), not thertol/atolpath. This is a deliberate narrowing, not a gap (docs/uniform_ranks_and_varieties.md). Relatedly, rank-changing ops (e.g.ut3svd) move to the minimal structural ranks (computable from shape + rank structure → static, jit-safe; they only ever shrink), never the numerical rank (value-dependent, would breakjit— it is the forbiddenrtol=0).The contract says nothing about the garbage — only the masked real parts are meaningful.
Orthogonalization at reduced numerical rank completes the prefix with arbitrary orthonormal vectors. When a slot’s structural rank exceeds its numerical rank, the extra prefix columns are legitimate-but-arbitrary orthonormal directions (deterministic per run, not meaningful data) — visible in outputs, and expected.
See also docs/uniform_ranks_and_varieties.md, docs/uniform_supercore_layout.md,
docs/uniform_masks_vs_ranks.md, docs/contributor/uniform_pytree_composition.md.