zero2robot · Phase 5 · Practitionerch5.5-fast · fast.py

Chapter 5.5

FASTTurning Torques into Tokens (DCT -> Quantize -> BPE)

By the end you can

  1. Build the pi0-FAST action tokenizer from scratch and INVERT it. Take an H x act_dim action chunk -> type-II DCT along the TIME axis (an orthonormal cosine-basis matrix, built by hand) -> quantize the coefficients to integers -> flatten dim-major -> a minbpe-style BPE trained/encoded on ACTION tokens (not text) -> a short token sequence; then BPE-decode -> dequantize -> inverse DCT and measure reconstruction error vs token count.
  2. Measure the headline compression. DCT->quantize->BPE encodes a chunk in a FRACTION of the tokens naive per-step-per-dim binning needs (~2.2-2.5x fewer on these real robot chunks; far more on a smooth synthetic chunk), at COMPARABLE reconstruction error. Understand WHY from two facts you can state precisely -- the DCT is orthonormal, so quantizing coefficients costs the same error ENERGY as quantizing raw samples (Parseval); but in the frequency domain that error concentrates, most coefficients round to exactly 0, and BPE losslessly merges the zero-runs.
  3. Scout the data honestly (the pilot's third lesson). These SCRIPTED experts switch phases (approach->grasp->carry), which injects step-discontinuities = high-frequency content, so aggressive low-frequency truncation is LOSSY here (keep 6/24 coeffs -> RMSE 0.21, unusable). Human teleop is smoother; that is why pi0-FAST truncates hard and we lean on Parseval + BPE instead. The toy's smooth synthetic chunk shows the mechanism at its best.
  4. Feel the fork with the continuous head (ch1.5). Flow decodes the WHOLE chunk in a few Euler steps, flat; FAST decodes ~35 tokens autoregressively via LM cross-entropy. Tokens buy language-model reuse + exact likelihood; flow buys a short constant decode. No policy is trained to make this point -- it is a decode-cost contrast.

See it work

live · P2

DCT → quantize → BPE, live

One smooth action chunk. Keep a handful of low-frequency DCT coefficients, quantize, and let BPE merge the zero-runs: the whole motion comes back from a fraction of the tokens per-step binning would spend. Drag the two sliders — the reconstruction snaps onto the original as the token counter drops.

trajectory · reconstruction overlaid
Reconstruction overlaid on the original chunkdim 0dim 1dim 2
DCT spectrum · energy per frequency
DCT spectrum with the coefficient cutoffkeep 6← low freqhigh freq →
6
0.05
drag either slider · coarser / fewer coeffs = fewer tokens · poster reads with JS off
Keeping 6 of 24 DCT coefficients at quantization step 0.05: 21 tokens — 3.4× fewer than the 72-token naive per-step-per-dim baseline — at reconstruction RMSE 0.040. The reconstruction lands on the original.

The DCT is orthonormal, so quantizing coefficients costs the same error energy as quantizing raw samples (Parseval) — but in the frequency domain that error concentrates: most coefficients round to exactly 0, and BPE losslessly merges the zero-runs. On this smooth synthetic chunk the win looks enormous because the energy piles into a few low frequencies. On real, phase-switching robot chunksthe motion has more high-frequency content, so FAST compresses a more honest ~2.2× over per-step binning at comparable RMSE (measured across seeds 0–2 in meta.yaml). Real precomputed grid from fast.py (seed 0, 24×3 chunk, naive baseline 72 tokens); poster reads with JS off.

Open in Colabsoon

Free-tier notebook — the button goes live when the course repository is published.

Two ways to say an action

By now you have a policy that can emit an action chunkH timesteps of act_dim numbers, the coordinated little burst of motion ch1.3 argued for. The question this chapter asks is narrow and mechanical: how do you get that chunk out of a model?

You have already built one answer. ch1.5's flow head treats the chunk as a point in a continuous space and integrates it out of noise in a few Euler steps — the whole chunk at once, no vocabulary anywhere. This chapter builds the other answer, the one pi0-FAST and OpenVLA use: turn the chunk into a short string of discrete tokens and let a language model emit them one at a time, so a robot's motion rides the exact same cross-entropy machinery as text. To do that you need a codec — something that maps a real-valued H × act_dim chunk to integers and back. That codec is the whole chapter. No policy is trained, no environment is stepped; like ch1.7, the subject is the data pipeline itself. Open fast.py. Eight regions: setup, dct, quantize, bpe, data, codec, measure, report. It is pure numpy — no torch, no model.

The naive tokenizer, and why it hurts

The obvious way to tokenize an action chunk is per-step-per-dim binning (this is what RT-2 does): chop each scalar's range into bins and emit one token per number. A chunk is then H × act_dim tokens, always — for H = 24 and a 6-DoF arm that is 144 tokens for a single burst of motion, and the language model has to generate all of them, autoregressively, every control cycle. Tokens are the currency of an LM's compute; per-step binning spends them recklessly. FAST's whole claim is that you can say the same motion in far fewer.

Step 1 — the DCT: change the basis

fast.py#dctsha256:f81cb82dfe…
# The DCT-II as a plain matrix — no scipy, no FFT trick, just the definition. Row k of D is# the k-th cosine basis vector (k=0 = DC average, higher k = faster wiggles). ORTHONORMAL# scaling makes D @ D.T = I, so the inverse is just D.T and Parseval preserves L2 energy.def dct_matrix(n: int) -> np.ndarray:    """(n, n) orthonormal DCT-II matrix. coeffs = D @ signal; signal = D.T @ coeffs."""    k = np.arange(n)[:, None]           # frequency index (rows)    t = np.arange(n)[None, :]           # time index (cols)    d = np.cos(np.pi * (2 * t + 1) * k / (2 * n)) * np.sqrt(2.0 / n)    d[0] *= 1.0 / np.sqrt(2.0)          # DC row scaled so the basis is orthonormal    return d  DCT = dct_matrix(H)  def dct(chunk: np.ndarray) -> np.ndarray:    """(H, act_dim) trajectory -> coefficients, each dim's time series independently. Row 0    is the average pose; rows toward H-1 are the fast wiggles."""    return DCT @ chunk  def idct(coeffs: np.ndarray) -> np.ndarray:    """Inverse: coefficients -> trajectory. Just the transpose (D is orthonormal) — no scaling    to undo, no schedule (contrast ch1.4's DDPM)."""    return DCT.T @ coeffs

A smooth trajectory wastes the time domain. Neighboring samples are nearly equal, so most of the H numbers per dimension are redundant. The discrete cosine transform re-expresses the same chunk in a basis of cosine waves — a slow one (the average level), a slightly faster one, and so on up to the fastest wiggle. We build it as a plain H × H matrix, one cosine per row, with no scipy and no FFT trick: coeffs = D @ chunk. The one design choice that matters is the orthonormal scaling, which makes D @ Dᵀ = I. That buys two things we lean on hard: the inverse transform is just the transpose (idct is one matmul, no schedule to undo — contrast ch1.4's DDPM reverse loop), and, by Parseval's theorem, the transform preserves L2 energy exactly. Hold onto that second fact.

Step 2 — quantize: where the zeros come from

fast.py#quantizesha256:92c0fe2503…
# Turn real coefficients into small integers. round(x / step) is the whole quantizer.# Two things make the grid mostly ZEROS (what BPE compresses): a smooth trajectory's# high-frequency coeffs are ~0 and round to 0, plus an optional low-pass keeping only the# K lowest frequencies per dim. In --break time_domain we quantize raw samples — no zeros.def quantize(coeffs: np.ndarray, keep: int, q_scale: float, q_max: int) -> np.ndarray:    q = np.clip(np.round(coeffs / q_scale), -q_max, q_max).astype(np.int64)    if keep < len(q):        q[keep:] = 0  # drop the high-frequency rows (a low-pass; only meaningful in the DCT domain)    return q  def dequantize(q: np.ndarray, q_scale: float) -> np.ndarray:    return q.astype(np.float64) * q_scale  def flatten_grid(q: np.ndarray) -> list[int]:    """(H, act_dim) grid -> 1-D token list, DIM-MAJOR: each dim's H coeffs run low->high    frequency and land contiguous, so every dim ends in a long zero-run — what BPE eats."""    return q.T.reshape(-1).tolist()

round(x / step) is the entire quantizer. Applied to the DCT coefficients, something useful happens: a smooth motion has almost no energy in the high-frequency cosines, so those coefficients are already near zero and round to exactly 0. The coefficient grid comes out mostly zeros. That is the compressible structure the next step feeds on — and it is created by changing basis, not by the rounding itself.

Here is the sharp version of the Parseval point. Because D is orthonormal, quantizing the coefficients injects the same error energy as quantizing the raw samples would. So the DCT does not buy you a better reconstruction — it buys you the same reconstruction with the error repackaged into a form you can throw a compressor at.

Step 3 — BPE, on actions instead of text

fast.py#bpesha256:3291ef966e…
# Byte-pair encoding, the minbpe move, on ACTION tokens. Repeatedly find the most frequent# adjacent PAIR across the corpus and merge it into one new token id. Runs of zeros collapse# first (0,0)->A, (A,A)->B, ... — a chunk's 20-zero tail becomes one or two tokens. BPE is# LOSSLESS entropy coding on top of the lossy DCT+quantize: it changes the token COUNT, never# the reconstruction. Ties broken by pair value -> deterministic.def merge_seq(seq: list[int], pair: tuple[int, int], new_id: int) -> list[int]:    out, i = [], 0    while i < len(seq):        if i < len(seq) - 1 and (seq[i], seq[i + 1]) == pair:            out.append(new_id)            i += 2        else:            out.append(seq[i])            i += 1    return out  def train_bpe(seqs: list[list[int]], num_merges: int) -> dict[tuple[int, int], int]:    """Learn merges. Returns an ordered dict (a,b)->new_id; order IS the apply order (encode    replays it). Base ids are already 0..V-1 (see remap below)."""    seqs = [list(s) for s in seqs]    next_id = max((max(s) for s in seqs if s), default=-1) + 1    merges: dict[tuple[int, int], int] = {}    for _ in range(num_merges):        counts: dict[tuple[int, int], int] = {}        for s in seqs:            for pair in zip(s, s[1:]):                counts[pair] = counts.get(pair, 0) + 1        if not counts:            break        best = max(counts, key=lambda p: (counts[p], p))  # most frequent; tie -> largest pair (deterministic)        if counts[best] < 2:            break  # nothing repeats: no merge would shorten the corpus        merges[best] = next_id        seqs = [merge_seq(s, best, next_id) for s in seqs]        next_id += 1    return merges  def bpe_encode(seq: list[int], merges: dict[tuple[int, int], int]) -> list[int]:    for pair, new_id in merges.items():  # replay merges in learned order        seq = merge_seq(seq, pair, new_id)    return seq  def bpe_decode(seq: list[int], merges: dict[tuple[int, int], int]) -> list[int]:    inv = {new_id: pair for pair, new_id in merges.items()}    changed = True    while changed:  # expand merged tokens back to their pairs until only base ids remain        changed, out = False, []        for t in seq:            if t in inv:                out.extend(inv[t])                changed = True            else:                out.append(t)        seq = out    return seq

This is Karpathy's minbpe move, re-derived on action tokens. Start from an alphabet of the distinct integers; repeatedly find the most frequent adjacent pair across the whole corpus and merge it into one new token id; record the merge. Runs of zeros collapse first — (0,0) → A, then (A,A) → B — so a coefficient block's long zero tail becomes one or two tokens. BPE is lossless: it is entropy coding stacked on top of the lossy DCT-and-quantize, so it changes the token count and never the reconstruction. (The artifact asserts the round-trip, because a codec you cannot invert is not a codec.)

Putting it together, and measuring it

fast.py#codecsha256:d30f7c0595…
# The whole FAST codec, one chunk at a time. encode: (DCT ->) quantize -> flatten. The# reconstruction comes straight from the quantized integers (BPE is lossless). --break# time_domain skips the DCT and spends the budget in TIME: keep every Nth action and# zero-order-HOLD it, then quantize the staircase — no basis in which energy concentrates.def downsample_hold(chunk_norm: np.ndarray, factor: int) -> np.ndarray:    return np.repeat(chunk_norm[::factor], factor, axis=0)[: len(chunk_norm)]  # ZOH back to H rows  def encode_chunk(chunk_norm: np.ndarray) -> tuple[list[int], np.ndarray]:    if USE_DCT:        q = quantize(dct(chunk_norm), KEEP, args.q_scale, args.q_max)    else:  # --break time_domain: quantize the held (downsampled) samples directly        q = quantize(downsample_hold(chunk_norm, args.downsample), H, args.q_scale, args.q_max)    return flatten_grid(q), q  def decode_chunk(q: np.ndarray) -> np.ndarray:    grid = dequantize(q, args.q_scale)    return idct(grid) if USE_DCT else grid  # break path is already a time-domain staircase  def error_jerk(recon: np.ndarray, target: np.ndarray) -> float:    """Mean squared 2nd difference of the reconstruction ERROR — a smoothness meter for what    the codec ADDED. The --break time_domain staircase inflates it ~200x (jerky residual)."""    return float((np.diff(recon - target, n=2, axis=0) ** 2).mean())  # Encode every chunk, learn ONE BPE over the pooled action-token corpus, and remap the# raw integers to a dense 0..V-1 alphabet first (so merged-token ids never collide).raw_seqs, quants = zip(*(encode_chunk(c) for c in chunks))alphabet = sorted({v for s in raw_seqs for v in s})to_id = {v: i for i, v in enumerate(alphabet)}base_seqs = [[to_id[v] for v in s] for s in raw_seqs]merges = train_bpe(base_seqs, args.num_merges)encoded = [bpe_encode(s, merges) for s in base_seqs]assert all(bpe_decode(e, merges) == b for e, b in zip(encoded, base_seqs)), "BPE must round-trip (it is lossless)" fast_tokens = sum(len(e) for e in encoded)                 # DCT->quantize->BPE: the methodnaive_tokens = n_elems                                     # per-step-per-dim binning: one token per action numbercompression_ratio = naive_tokens / fast_tokenscoeff_zero_frac = float(np.mean([(q == 0).mean() for q in quants]))  # why BPE wins: the share of quantized coeffs that are 0

The data region pulls real robot action chunks — the same PushT and ALOHA scripted demos ch1.7's dataset is built from, replayed in-process for their actions only (no rendering, so nothing here is platform-sensitive), sliced into non-overlapping H = 24 chunks. We encode every chunk, learn one BPE over the pooled corpus, and count. The measured headline, seed 0:

tokenizer tokens reconstruction RMSE
per-step-per-dim binning 1968 0.012
FAST (DCT → quantize → BPE) 888 0.014

About 2.2× fewer tokens at the same reconstruction error — and it holds on every seed (2.2 / 2.3 / 2.5×). Read the two columns together: the RMSE barely moved (Parseval promised that), and yet the token count more than halved. Where did the tokens go? Into the ~17% of coefficients that quantized to zero and the BPE merges that ate their runs. That is the entire trick, and it is honest: no policy, no rollout, no success rate — a codec, measured as a codec.

Scout the data before you trust the method

Textbook FAST goes further: it truncates, keeping only a handful of low-frequency coefficients and discarding the rest, on the theory that "the robot never jerks." That works on smooth human teleop. It does not work here, and the honest reason is worth internalizing. Our scripted experts switch phases — approach, grasp, carry — and each switch is a step-discontinuity in the action, which is exactly the high-frequency content a low-pass throws away. Truncating our chunks to 6 of 24 coefficients drives RMSE to 0.21 — a fifth of the whole action range, unusable. So on this data the compression comes from Parseval + BPE-on-zeros, not from truncation. The toy — a deliberately smooth synthetic chunk — is where truncation is cheap (keep 6 of 24 → RMSE ~0.04) and where the demo lets you watch the mechanism at its best. Same method, different data, honestly different result: check the structure your method assumes before you believe a number.

Break it: is it the DCT, or the BPE?

fast.py#measuresha256:276eb6043d…
# Reconstruction error (normalized action units) and error smoothness, per chunk. The naive# baseline quantizes raw samples at the SAME step; by Parseval the orthonormal DCT adds no# error of its own, so FAST's and naive's error energies are comparable. Compression is the win.sq_err_fast = sq_err_naive = 0.0ejerk_fast = ejerk_naive = 0.0for chunk_norm, q in zip(chunks, quants):    recon = decode_chunk(q)    q_naive = np.clip(np.round(chunk_norm / args.q_scale), -args.q_max, args.q_max)    recon_naive = q_naive * args.q_scale                   # per-element time-domain dequant    sq_err_fast += float(((recon - chunk_norm) ** 2).sum())    sq_err_naive += float(((recon_naive - chunk_norm) ** 2).sum())    ejerk_fast += error_jerk(recon, chunk_norm)            # smoothness of FAST's residual    ejerk_naive += error_jerk(recon_naive, chunk_norm)     # smoothness of the white time-domain residual fast_rmse = (sq_err_fast / n_elems) ** 0.5naive_rmse = (sq_err_naive / n_elems) ** 0.5ejerk_fast, ejerk_naive = ejerk_fast / len(chunks), ejerk_naive / len(chunks)# The FORK (ch1.5 flow vs FAST): FAST decodes AUTOREGRESSIVELY, one LM step per token# (~fast_tokens/len(chunks) sequential steps); flow decodes the whole chunk in# FLOW_DECODE_STEPS Euler steps, flat. Tokens buy LM reuse + exact likelihood; flow buys a# short constant decode.fast_ar_steps = fast_tokens / len(chunks)print(f"FAST : {fast_tokens} tokens  |  naive per-step binning: {naive_tokens} tokens  "      f"-> {compression_ratio:.2f}x fewer  ({coeff_zero_frac:.0%} of quantized coeffs are 0)  "      f"[break={args.break_mode or 'none'}]")print(f"recon RMSE (normalized): FAST {fast_rmse:.4f}  |  naive per-step {naive_rmse:.4f}  (comparable — Parseval)")print(f"error jerk (residual smoothness): FAST {ejerk_fast:.6f}  |  naive per-step {ejerk_naive:.6f}")print(f"decode fork: FAST ~{fast_ar_steps:.1f} autoregressive steps/chunk  vs  "      f"flow {FLOW_DECODE_STEPS} Euler steps (constant)")

It is tempting to think BPE is doing the real work and the transform is decoration. Settle it by generating the failure. --break time_domain spends the same kind of budget — keep fewer numbers, reconstruct — but in the time domain: keep every Nth action and zero-order-hold it, then quantize the staircase. Predict what that does to the reconstruction, then run it.

metric clean FAST --break time_domain
reconstruction RMSE 0.014 0.220 (~15×)
error jerk 0.0012 0.264 (~200×)

The motion falls apart. A trajectory's information is spread across every timestep — there is no basis in the time domain where energy concentrates — so dropping timesteps and holding is a jerky staircase with fifteen times the error. Keep the same number of coefficients and the smooth motion comes back; keep the same number of samples and it does not. The DCT basis is load-bearing. (One honest note the pilot's playbook forces: the naive comparison in the contract — plain per-step binning giving "an order of magnitude more tokens" — does not survive measurement, because BPE happily compresses a slow time-domain signal too. The claim that reproduces, and the break that actually degrades, are the ones above.)

The fork: FAST vs flow

You now hold both decoders, so name the tradeoff plainly. FAST decodes autoregressively — one LM step per token, ~35 sequential steps for one of these chunks — but every step is a plain cross-entropy over a vocabulary, so it reuses a language model's whole stack and gives you exact per-token likelihoods for free. Flow (ch1.5) decodes the entire chunk continuously in a few Euler steps flat, no vocabulary and no autoregression, but it needs a bespoke sampler and gives no discrete likelihood. Tokens buy language-model reuse; flow buys a short, constant decode. pi0 famously ships both — flow for real-time control, FAST for pretraining — and now you know why neither is simply "better."

Read the real thing

Everything here has a production form in openpi's pi0_fast.py and its FAST processor: the cosine transform, the scale-and-round quantizer, the byte-pair merges, and the autoregressive detokenize that runs your bpe_decode → dequantize → idct in reverse. You will recognize every line. What the real processor adds is scale (a real action vocabulary, real teleop) and the wiring into an LM cross-entropy head — the fork this chapter only sketched.

What's next

You turned continuous motion into a short string of integers and back, and measured — honestly, on real robot chunks — exactly what the DCT, the quantizer, and the BPE each contribute. That is the last piece of the modern VLA data stack: a policy can now speak actions the way it speaks words, and the same transformer that reads an instruction can write the motion that answers it.

Practice

practice · candidate exercisesdrafted by the exercise generator, pending human promotion. Answers reveal only after you predict — honor system.

  1. predict-then-run

    Exercise 1

    SUGGESTED exercise candidate (humans promote) — predict-then-run, ch5.5.

    Objective tested: what DCT -> quantize -> BPE actually buys you. fast.py takes real PushT + ALOHA action chunks and encodes each two ways, then reports in metrics.json:

    • fast_tokens : DCT -> quantize -> BPE (the FAST codec)
    • naive_tokens : per-step-per-dim binning (one token per action number = H*act_dim)
    • fast_recon_rmse : reconstruction error of the FAST codec (normalized action units)
    • naive_recon_rmse : reconstruction error of per-step binning at the SAME quantization step

    PREDICT before you run: how does FAST compare to naive per-step binning?

    • A) FAST uses about the SAME number of tokens at the same error — the DCT just relabels the data, so nothing is gained.

    • B) FAST uses noticeably FEWER tokens at COMPARABLE error — the orthonormal DCT costs the same error energy as quantizing raw samples (Parseval), but in the frequency domain that error concentrates, most coefficients round to 0, and BPE merges the zero-runs.

    • C) FAST uses fewer tokens but at MUCH WORSE error — you can only save tokens by throwing away reconstruction quality.

    It runs the CODEC (pure numpy, no training, well under a second). The claim to internalize is the DIRECTION (fewer tokens at comparable error), which holds on every seed — the exact ratio (~2.2-2.5x here) depends on how smooth the trajectories are. Estimated learner time: 10 minutes.

    Predict, then commit

    Pick the outcome you expect from the options above. The answer and the local run command reveal only after you commit — predicting after you know teaches nothing.

  2. code-completion

    Exercise 2

    SUGGESTED exercise candidate (humans promote) — code-completion, ch5.5.

    Objective tested: the inverse half of the transform — the thing that turns coefficients back into a trajectory. fast.py builds the forward DCT as an ORTHONORMAL matrix D (dct_matrix): a chunk's coefficients are D @ chunk. Your job is the inverse, idct, which the decoder needs to go coefficients -> trajectory.

    The whole point of using the orthonormal DCT is that the inverse is trivial: because D has orthonormal rows, D @ D.T = I, so the inverse transform is JUST the transpose — no scaling to undo, no schedule (contrast ch1.4's DDPM reverse loop). Get this one line right and the codec round-trips (BPE-decode -> dequantize -> idct) to machine precision; get the scaling wrong and every reconstruction is silently stretched.

    Implement idct below (pure numpy), then run the checks: pytest curriculum/phase5_practitioner/ch5.5_fast/exercises/suggested/checks.py -k ex2 Estimated learner time: 10 minutes.

    Run it locally:

    pytest curriculum/phase5_practitioner/ch5.5_fast/exercises/suggested/checks.py -k ex2
  3. predict-then-run

    Exercise 3

    SUGGESTED exercise candidate (humans promote) — predict-then-run + the learner-generated

    failure, ch5.5.

    Objective tested: WHERE the compression comes from — the DCT basis, or the BPE? It is tempting to think BPE is doing the work and the transform is cosmetic. This exercise makes you generate the failure that settles it.

    The FAST codec spends its token budget on a few LOW-FREQUENCY coefficients (frequency domain). The --break time_domain flag spends the SAME kind of budget in the TIME domain instead: it keeps every Nth action and zero-order-HOLDS it (a crude time-domain low-pass), then quantizes the staircase. Same idea ("keep fewer numbers, reconstruct"), different basis.

    PREDICT before you run: compared to the clean FAST codec, what does --break time_domain do to the RECONSTRUCTION (fast_recon_rmse) and its SMOOTHNESS (fast_error_jerk)?

    • A) Both stay about the same — a basis is a basis; keeping fewer numbers in time is as good as keeping fewer in frequency.

    • B) It IMPROVES both — the time domain is the "natural" domain for a trajectory, so skipping the transform can only help.

    • C) Both get much WORSE — a robot trajectory's information is spread across every timestep, so dropping timesteps and holding craters the error (RMSE ~15x) and turns the reconstruction into a jerky staircase (error-jerk ~200x). The DCT basis is load-bearing, not cosmetic.

    : it runs the codec clean AND with --break time_domain (pure numpy, well under a second each) and prints both. Then GENERATE the failure yourself and read it in rerun: run python curriculum/phase5_practitioner/ch5.5_fast/fast.py --seed 0 --break time_domain and compare the toy/reconstruction stream to the clean run. Estimated learner time: 15 minutes.

    Predict, then commit

    Pick the outcome you expect from the options above. The answer and the local run command reveal only after you commit — predicting after you know teaches nothing.

wall-clock · rendered from wallclock.csvone source · every tier
cpu-laptopexpected wall-clock on cpu-laptop: ~0.03 min (measured)measured
mpswall-clock on mps: not yet measuredpending
t4wall-clock on t4: not yet measuredpending
4090wall-clock on 4090: not yet measuredpending

Colophon · provenance

The code on this page is not pasted — each panel is included by region straight fromfast.py, and these fingerprints are its sha256, the same ones check_prose_code_drift re-checks on every PR. Edit a shown region without re-rendering and CI turns red.

#setup
000970baa0f07898e7f08dbf1d1080e462de7ffc7a99e74ddd8aff33be627081
#dct
f81cb82dfe255750705ef3420877fcc3c42726b98d2a71119841e91d4954596a
#quantize
92c0fe2503bb8ebb8ccf0ed8421f6a9ecc5af38794e11c7765d1705d88ab96b9
#bpe
3291ef966e71af92e3ffa84db3883fee23d664e3651f155daea43695d9c545d1
#data
cdfead491d203a1c107462663a49c69f62d1a332441b9bdd8293243e003f4ea0
#codec
d30f7c05958c7f4b5635fa6a35ae06f801252fa0b2a7c97fc73b4fba03af766f
#measure
276eb6043d2676a5f9cc2d53db549f234c23fdc75c12eb484d72b310cb7b3f56
#report
331f9b05c8b8ced3318efb9b815db19aa1198b642ca28e742e4b57b52d6bbfca