Feature Requirement · TODO · plan only — no code changed

Rank-1 Direction Weight Projection for the Delayed Anchor Circuit

A growing-window rank-1 OLS trajectory extrapolator that de-stales the anchor's evaluation point — additive to signed_ema, zero extra communication, self-gating on incoherent data, and byte-identical when disabled.

substrate · shamanez/verl @ autonomous-harness-v1 model · Qwen2.5-1.5B-Instruct loss · vanilla GRPO paper · RELEX (arXiv 2605.21468)

Produced by a 5-agent team (paper/method · code-reading · algorithm-design · systems-cost · experiment-design) with a lead synthesizer. Semantics below were confirmed against the real code on autonomous-harness-v1.

01 Problem statement

The comm-efficient anchor circuit ships a full weight snapshot across the pipeline boundary only sparsely (every cadence steps) and forwards from a stale snapshot (delay_K steps old). The operating merger signed_ema reads only sign(M_anchor) per coordinate, where M_anchor is an EMA of gradients evaluated at the stale weight point θ[t−K]. As K grows, the gradient's sign pattern rotates away from the live weights and the correction degrades — the k-collapse, empirically appearing ~step 61 at cadence/delay_K = 20/20.

Goal

Insert a growing-window rank-1 OLS trajectory extrapolator that de-stales the anchor's evaluation point from θ[t−K] to an estimate θ̂[t], so the sign fed to signed_ema is computed at (near-)current weights — with no added inter-stage communication, without leading the fast circuit, cross-rank-identical, and byte-identical when disabled. The method is strictly additive to signed_ema (never a replacement) and must gate itself off on incoherent trajectories (Big-Math), where the fork's own evidence shows projection is harmful and do-nothing is optimal.

02 Current anchor-circuit behavior

Confirmed against the code (paths under /home/user/verl/). This answers the two questions directly: cadence = how often weights are exchanged (sparsity); delay_K = the lag. At fast tick 20 with delay_K=10, the anchor operates on the tick-10 snapshot.

ConceptSemanticsFile:line
Fire predicate(step % cadence)==0; non-fire steps early-return. Snapshot push happens every tick; only the fwd/bwd + M update is gated.anchor.py:124-134; transformer_impl.py:1485-1486, :1433-1483
cadence (sparsity)anchor fires only on steps cadence, 2·cadence, …. Default 20; ≥1.comm_eff.py:112-114,188; val :901-902
delay_K (lag)staleness of the forwarded snapshot in steps; 0=current, 1=prior. Default 20; ≥0.comm_eff.py:115-117,189; val :903-904
cadence vs delay_K — confirmedat step=20, delay_K=10, get_stale computes target_step = step − k = 10 → returns the tick-10 snapshot.anchor.py:257-272
Never-lead invariantreplay realizes staleness alternating K / K+1, asserted ≥ delay_K (never fresher).transformer_impl.py:1513-1529
Stale bufferAnchorStalenessQueue, ring size delay_K+1, keyed by step; warmup falls back to oldest.anchor.py:228-279
Replay ringAnchorReplayRing, fire-aware retention _keep_residue=(−delay_K)%cadence, _maxlen=delay_K//cadence+1.anchor.py:418-563
Isolated cloneanchor backward runs on a deep-copied, hook-free module; fail-closed full-load assert; live optimizer grads untouched.transformer_impl.py:1759-1817,1973-1976; anchor.py:689-848
M_anchor EMAnew = beta_anc·anc + (1−beta_anc)·ga, fp32; fed the DP-MEAN-reduced G_anchor. beta_anc default 0.95, op=0.50.spectral_filter.py:310-331; anchor.py:1135-1162; reduce :2052-2061
Merger (sign-only)g_corr = alpha·gm + (1−alpha)·gm.abs()·sign(anc). Uses only sign(M). Cold-M guard: ‖M‖≤eps → return g_mask. alpha default 0.0, op=0.25.spectral_filter.py:403-441
Writeback orderzero_grad → anchor_refresh → fwd/bwd (fast G_comp) → grad_correction (rewrites .grad) → optimizer_step.engine/base.py:212-253

Config vs operating point — not a contradiction

Shipped defaults are alpha=0.0, beta_anc=0.95 (inert); the locked launcher operating point is alpha=0.25, beta_anc=0.50.

03 Why the paper setting (RELEX) differs from ours

RELEX assumptionOur anchor realityTransfers?Consequence
Dense per-step sampling (stride 1)Sparse, every cadence ticksPartially — OLS fits on real tick numbersFewer points → higher fit variance; re-measure EVR/R²/cos at sparse stride
Fresh (current) weightsStale by delay_KStructurally — window ends at newest visible tickThe horizon we extrapolate is the lag; target unseen at fit time → offline validation only
Whole-run cumulative trendLocal, small K-gapFavorably — small h/span is the EXP-47 helps-regimeUse the anchor-pinned form, not base-delta form
Forward extrapolation past last ckptHARD: anchor must NEVER leadOnly as internal reference, never a weight writePredict at t_tgt ≤ t_fast; feeds sign/velocity into signed_ema, never overwrites fast weights
Output = predicted weightsMerger needs a gradient-sign referenceKey mismatch — see §7Weight ≠ gradient; transfer sign+confidence only, magnitude from a real backward
Coherent, near-linear (R²>0.98)Dataset-dependent: GSM8K cos≈0.86, Big-Math cos≈0.15Only when measured coherent — gate mandatoryOn Big-Math no projector beats do-nothing (EXP-60); gate must fall back to signed_ema

Two anchor-specific hazards RELEX never faces

(1) Cross-rank sign ambiguityeigh eigenvector sign is arbitrary and could differ per rank; must be pinned deterministically before entering the merger. (2) Variable stalenesst_a and the gap vary run-to-run, compounding horizon variance.

04 Proposed rank-1 projection method (growing-window)

Per target matrix, flatten to θ∈R^d. Store θ_base=θ_0 and consecutive-tick deltas d_k = θ_{t_k}−θ_{t_{k−1}}. Maintain the consecutive-delta Gram D[k,l]=⟨d_k,d_l⟩; prefix-sum → base-delta Gram A; re-base bilinearly. The Gram is additive over shards and group members (block/global pooling for free) and is d-independent in storage — the fork's offline TrajGram discipline ported online (rank1_traj.py:110-139).

# fire hook: fold in new anchor-visible snapshot θ_new @ tick t_new
d_new = θ_new − θ_prev                      # one fp32 diff (bf16-safe, no cancellation)
for k in retained: D[new,k]=D[k,new]=dot(d_k,d_new)
D[new,new]=dot(d_new,d_new); store d_new; θ_prev←θ_new; W←W+1
if W > W_cap: drop_oldest_and_rebase()       # fixed base θ0 + sliding recent fit window

# fit on demand (Gram only — no d-vector touched)
G  = symmetrize(B[window, window])           # W×W
λ,U = eigh(G)  (descending)
σ1 = sqrt(max(λ1,0));  c = σ1·u1             # coefficient trajectory c_i=⟨Δ_i,v1⟩
EVR1 = λ1/trace(G)                           # coherence signal #1

v1 = (1/σ1)·Σ_i u1[i]·Δ_i is materialized only to build θ̂ (one streamed O(d) pass), never for scoring.

4-checkpoint bootstrap

N0 = 4. A line needs ≥2 points; 4 gives a usable plus slack. Below fire 4 the projector is a strict no-op → warmup falls back to the raw stale anchor (unchanged from today). At cadence/delay_K=20/20, fires {20,40,60,80} record true-ticks {0,20,40,60} → first projection at step 80.

Does "keep growing ⇒ keeps improving" hold?

Only conditionally. Model c_i = a·t_i + b + ½q·(t_i−t̄)² + ε_i, Var(ε)=τ²:

TermFormulaDirection
Variance (pro-growth)Var(ĉ) ≈ 4τ²/W; slope SE ∝ W^(−3/2) (4→20 pts = 11.5× tighter)↓ monotone with W
Bias (anti-growth)Bias(ĉ) ≈ −q·s²·W²/12 (∝ W²)↑ with W if trajectory curves
OptimumW* = (144τ²/(q²s⁴))^(1/5)modest window (single-to-low-tens)

Verdict

Growth improves only while variance dominates (W<W*) and the trajectory is stationary + coherent. Past W*, the bias inverts it. W* depends on curvature (non-stationarity) and isn't knowable a priori, so the claim is restated as "monotone within a stationary, coherent phase; cap/forget at breakpoints."

Mandatory guards: grow only while all three hold, else cap/slide/forget — EVR1 ≳ 0.7, fit R² ≳ 0.9, fire-to-fire consec-delta cos ≳ 0.8. Default policy: fixed base θ_0, sliding recent fit window W_cap=8; optional forgetting w_i=ρ^((t*−t_i)/s), ρ=0.85. Horizon clamp h_safe≈30 (EXP-47).

05 Ordinary least-squares coefficient prediction

Closed form on real tick indices (handles sparse + variable staleness exactly; no uniform-spacing assumption):

t̄=mean(t_i); c̄=mean(c_i)
a = Σ(t_i−t̄)(c_i−c̄) / Σ(t_i−t̄)²   =  Cov(t,c)/Var(t)
b = c̄ − a·t̄
R² = 1 − SS_res/SS_tot

De-stale to "now", never lead:

h_eff = min(t_fast − t_W, h_safe)     # t_W = newest visible tick = t_fast − K
t_tgt = t_W + h_eff                    # ≤ t_fast  ⇒  ANCHOR NEVER LEADS
ĉ     = a·t_tgt + b

If h_safe < delay_K we deliberately land short of now (still lagging — conservative), never past it.

Weight reconstruction as an explicit linear combination of raw snapshots (the falsifiable "stays inside the snapshot span" contract, self-tested to 1e-9 / audited to 1e-6):

θ̂(t_tgt) = θ_base + ĉ·v1 = θ_base + Σ_i γ_i·Δ_i,   γ_i = (ĉ/σ1)·u1[i]

Naming note

This OLS lives in rank1_traj._linfit. predictors.py:Order1 is a different object (a two-point Lagrange baseline) — do not conflate.

06 Alternative projection variants

Granularity — recommend per-tensor fit + block-pooled coherence gate

LevelUseVerdict
Per-tensor (196 proj matrices)fit v1, ĉ; matches signed_ema per-matrix sign + Gram additivityfit here (heterogeneous scales); gate noisy (only W samples)
Block / per-layer (Gram sums exactly)pooled EVR1/R²/cos; shrink noisy per-tensor gategate here (robust, ~free)
Globalone scalar for logging / kill-switchlog-only; single v1 across shapes is meaningless

Ingredient comparison

MechanismWhat it buysCostFails when
Direction v1denoised shared axis; relocates eval point → fresher sign(M); off-v1 noise discardedone eigh(W×W) + O(d) reconstruction; +1 clean backward (already the anchor's job)not rank-1 / v1 rotates (Big-Math) → coherence-gate
Coefficients a,b (OLS)de-stale scalar ĉ(t), low variance if linear; sets extrapolation distance; = trusttrivial (closed-form on W scalars)curvature/regime change → W² bias; over-horizon → clamp
Sign (sign(M), signed_ema core)scale-invariant, currency-safe; the proven op lever~free (in path)unreliable where |M| tiny → magnitude-gate (§7)
Magnitude (|M|, ‖M‖)per-coord + per-tensor confidence on the sign; graceful skip~free (percentile+clip)wrong if used as gradient magnitude (Adam+LR unknown) — confidence only

07 Interaction with sign correction (additive)

Operating merger unchanged: G_corr = α·G_noisy + (1−α)·|G_noisy|·sign(M_anchor) (α=0.25, β_anc=0.50).

The currency problem — why direction can't touch raw weights

The projection predicts weights θ̂; the merger consumes a gradient reference dL/dθ. They differ by (i) Adam's per-coord preconditioner + momentum, (ii) unknown LR scale, (iii) sign (descent negates). So a weight-trajectory direction is a valid gradient reference only up to sign and a per-coordinate preconditioner — its gradient-unit magnitude is unrecoverable. Therefore v1 must not be added to raw weights as if it were a gradient; it becomes a gradient reference only by (A) evaluating a backward at θ̂, or (B) collapsing to a sign.

Two resolutions — ship both, distinct roles

  • Resolution A · PRIMARY  (= user option #3 mechanism). Load θ̂[t] into the isolated anchor clone, run the one clean unmasked backward on the paired batch → M̂ = G_anchor(θ̂), feed RAW into the M_anchor EMA via the existing update_anchor. No currency mismatch — the gradient is genuinely evaluated at de-staled weights. A drop-in upgrade of the existing LookaheadProjector.project() seam.
  • Resolution B · SECONDARY  (sign/gate only). M̂_dir = −a·v1 (weight velocity a·v1 ≈ −η·ḡ). Free (no extra backward), but no magnitude, no off-v1 structure. Use only to cross-check sign(M_anchor) and feed the coherence gate.

Additive decomposition (reduces exactly to signed_ema at full confidence)

w_i   = clip(|M_i| / (κ·quantile_p(|M|)), 0, 1)          # option #1: per-coord confidence
τ     = shrink_to_block(g(R², EVR1, fire_cos, sign_agree))  # option #3, ∈[0,1]
α_eff = α + (1−α)·(1−τ)
signed_i = w_i·|G_noisy_i|·sign(M_i) + (1−w_i)·G_noisy_i  # keep fast sign where M unsure
G_corr_i = α_eff·G_noisy_i + (1−α_eff)·signed_i
  • DIRECTION → refreshes the sign input by moving the eval point (option #3). Applied to the anchor reference, never raw weights.
  • COEFFICIENTS → set the de-stale distance + supply the coherence/trust τ.
  • MAGNITUDE → per-coord confidence w_i (option #1) + per-tensor trust ‖M‖; output magnitude always stays |G_noisy|.

Invariants

τ=1, w≡1 → byte-identical signed_ema;   τ=0 → do-nothing (G_noisy, the Big-Math-optimal behavior).

08 Cost analysis

Constants: 196 proj matrices (1.310 B params, 85%) + embed etc → 1.544 B total; bf16 snapshot = 3.09 GB; MLP (gate/up/down) = 88% of each layer.

Communication — per fire: weights up 3.09 GB + sign(M) down 0.19 GB ≈ 3.28 GB round-trip

CadenceFires / 500 stepsTotal linkvs K=1
K=1 (infeasible)5001.64 TB
K=1050164 GB1/10
K=20 (default)2582 GB1/20

Zero extra communication — argued

The snapshots at ticks 0,K,2K,… are already shipped for the anchor's core backward. SVD/OLS/extrapolation are local anchor-node compute; the return payload (sign(M)) has identical shape whether M came from stale or de-staled weights. Incremental link bytes = 0. (A good fit can only reduce comm by widening K.)

Memory (Gram trick)

Storage@ T=20, whole model
Resident T×d SVD stack (fp32)O(T·d)123.5 GB
Gram, all 338 tensors fp64O(n·T²)1.08 MB
Gram, 196 proj onlyO(n·T²)0.63 MB

~10⁵× smaller. θ̂ is materialized on demand as a streamed O(d) linear combo of snapshots the anchor already retains (one transient 3 GB vector).

Compute per fire

Gram row update ~15 ms (HBM-resident) / eigh(T×T)×338 tensors < a few ms / OLS closed-form µs. Full-batch anchor backward ≈ ~5 s. Projection share < 1% at the full-batch op point. Negligible.

09 Failure modes

#FailureDetection signalGuard
aSparse ckpts → high-variance v1/slope (SE 11.5× at T=4 vs 20)coef_r2, SE(a), EVR1require T≥6 to trust slope; -floor; damp slope λ*=0.3 toward hold-stale
bNoisy RL → direction wander / low EVR (Big-Math cos≈0.15 vs GSM8K 0.86)EVR1=λ1/tr(G); consec_delta_cosmandatory coherence gate: EVR1≥0.6 ∧ cos≥0.3–0.5 else hold-stale (ratio≡1.0)
cDelayed weights → must cover gap, never leadrequested horizon vs delay_K; realized K/K+1clamp t_tgt≤t_fast; anchor-pinned form: h→0 ⇒ θ̂→θ_anchor; clamp to h_safe
dSlope blow-up (degenerate Var(t) / outlier snapshot)‖a·H·σ‖ ≫ observed step; OOS residual sign (ρ=−0.75)clamp |a·H·σ| ≤ κ·last-step, κ∈[1,2]; damp; EVR floor; hold-stale on trip
eNon-stationary phase change (grow-forever inverts here)rolling drop; slope-sign flip; cos trend downrolling/forgetting window (W_cap, ρ) or re-base θ0 at breakpoint (EXP-49/61 favor bounded K=3–5)
fCross-rank divergence (eigvec sign / reduce order)per-fire all-gather checksum of {slope, intercept, θ̂}prediction is sign-invariant (ĉ flips with u); DP-mean inputs; compute once on anchor node, broadcast; checksum-assert → hold-stale

10 Implementation plan (no interface change, byte-identical OFF)

Extend the existing look-ahead seam (a look-ahead projector already exists but holds only 2–3 newest snapshots with fixed/learned linear extrapolation — the growing rank-1 SVD is additive to it).

ConcernFile:linesChange
Growing snapshot ringlookahead.py:336-471new GrowingWeightTrajRing (true-tick keyed, grows to W_cap)
Projectorlookahead.py:474-600Rank1TrajProjector: Gram update → eigh → OLS → θ̂
Ring/projector build + fire hooktransformer_impl.py:1590-1683build under new flag; push K-stale snapshot (_src_tick available :1589,1604)
Feed projected into EMAtransformer_impl.py:2052-2061after backward at θ̂, update_anchor unchanged
New merger mode projected_anchorspectral_filter.py:403-441; dispatch :1173-1200keep sign() step; source sign+τ+w_i
Merger-mode enum (3 sites)comm_eff.py:1017; spectral_filter.py:146; state.py:577-623add mode string
Config fields + validationcomm_eff.py:187-198 / 201-435 / 901-1011new anchor.* + spectral.* flags (§12)
Offline engine to portresearch/scripts/weight_proj/rank1_traj.pyreuse fit_rank_r, rank_anchored_pred, _linfit verbatim

Byte-identical-OFF invariants any new code must preserve

(state.py:1015-1028, base.py:124-190) Reachable only via a flag that leaves maybe_build_comm_eff_state → None (or the sub-feature disabled) when off; no RNG draw / buffer alloc / collective on the OFF path; runs only on the isolated clone (never live optimizer params); stays off the train path-tag (falsifier counters anchor_* stay 0); correction_mode defaults to a mode returning G_comp bitwise unchanged. Master gate lookahead_enabled already requires lookahead_anchor=true AND a non-disabled mode.

11 Experiment plan (Big-Math)

Step 1 — GPU-FREE OFFLINE GATE (mandatory; commit no GPU until it clears)

Extend research/scripts/rank1_scorecard.py (~150 LOC) using shared R2 traces: GSM8K EXP-43/57, Big-Math EXP-58 (50 snapshots @ cadence-20, fp32). Subsample to the cadence-20 grid; simulate the lagging anchor with existing leakage guards (build_tick_plan h≥1, fit_score_split assert max(fit_idx)<score_idx); grow W=4…W_max, refit each fire; score weight_proj_ratio = ‖θ̂−θ_now‖/‖θ_stale−θ_now‖ (do-nothing≡1.0). Report ratio(W) trend + dir_cos, coef_r2, EVR1, consec_delta_cos, v1 drift.

Gate decision — commit GPU iff BOTH

  • Big-Math (gate-correctness): ungated ratio ≥1.0 (projection harmful → gate necessary) AND gated ratio ≥0.999 (falls back to do-nothing, no worse). Fraction gated-ON ≈ 0.
  • GSM8K (margin): ungated ratio ≤0.95 at h=K (EXP-47 got 0.940) AND ratio(W) slope ≤0 over W=4…W_max AND EVR1≥0.6, coef_r2≥0.7.
  • Fail either → STOP / redesign.

Step 2 — online arms

Big-Math surface: gshasiri/Big-Math-RL-Verified-filtered, MAX_RESPONSE_LENGTH=4096, locked comm-eff accel base, cadence/delay_K=20/20, PowerSGD r=77, α=0.25 / β_anc=0.50, 200 steps (7 projected fires, W:4→10); GSM8K confirmation arm at resp 1024.

ArmToggles on the locked base
A controlnone (the k-collapse state)
B + de-stalerlookahead_anchor=true, lookahead_mode=growing_rank1_ols, lookahead_min_snapshots=4, lookahead_rank=1, lookahead_window_max=-1
C + coherence gateB + lookahead_coherence_gate=true, ..._floor=0.5, lookahead_evr_floor=0.6, lookahead_horizon_clamp=30
D + confidence signing (opt)C + signed_ema_conf_gate=true, signed_ema_conf_floor=0.5

Metrics & thresholds

  • val/score (primary); grad_norm stability (fail on NaN or >10× arm-A); comm bytes_ratio must equal arm A; coefficient-prediction error (retrospective, no peek); EVR/coherence; fraction gated-ON; off-path bitwise parity.
  • Big-Math (safety, primary): arm C/D Δ ≥ −max(0.010, 2σ) (no regression); gated-ON ≈ 0.
  • GSM8K (margin): arm C Δ ≥ max(0.010, 2σ); low coeff error; high EVR/coherence.
  • Seeds: GSM8K 3, Big-Math 2 (safety-weighted); B/D on seed 0 as ablations.

12 Config toggles and safety guards (defaults OFF)

CommEffAnchorConfig (comm_eff.py:98-198):

FlagDefaultMeaning
lookahead_mode += "growing_rank1_ols"disabledselect method
lookahead_rank1SVD rank
lookahead_window_max-1 (cap via gate)W_cap
lookahead_min_snapshots (exists)4bootstrap N0
lookahead_coherence_gateFalseoption #3 on
lookahead_coherence_metricconsec_delta_cosgate signal (alt coef_r2)
lookahead_coherence_floor0.0coherence gate
lookahead_evr_floor0.0EVR floor
lookahead_horizon_clamp-1horizon clamp (ticks, op=30)

CommEffSpectralConfig (comm_eff.py:201-435): signed_ema_conf_gate:bool=False, signed_ema_conf_floor:float=0.0 (option #1).

Guards (defense-in-depth, engaged in arms C/D)

(1) coherence gate per-tensor/per-fire → else raw θ[t−K]; (2) EVR floor (skip if not rank-1); (3) horizon clamp h_safe≈30; (4) window forgetting on drift; (5) lagging-anchor invariant (reads only ckpts ≤t−K → never leads, cross-rank-identical); (6) cold-M fallback retained; (7) zero-extra-comm asserted via bytes_ratio.

13 Recommended next steps

  1. Run the GPU-free offline gate (§11 step 1) on EXP-43/57 (GSM8K) + EXP-58 (Big-Math). Reproduce EXP-60 degeneracy, measure ratio(W) monotonicity, and validate the gate collapses to do-nothing on Big-Math. No GPU until this clears both thresholds.
  2. Implement the config surface (§12) + a byte-identical-OFF parity test (CPU parity + 1–2-step GPU weight-hash probe).
  3. Implement GrowingWeightTrajRing + Rank1TrajProjector (Resolution A) porting rank1_traj math; wire the fire hook + update_anchor feed. Pin the eigenvector sign; compute-once-and-broadcast on the anchor node.
  4. Implement the projected_anchor merger mode + option-#1 confidence gate additively over signed_ema (verify τ=1, w≡1 reduces byte-identical).
  5. Run arm A vs C on Big-Math (safety, primary) at 20/20, 200 steps; require no-regression + gated-ON≈0 + comm parity.
  6. Run the GSM8K confirmation arm for the coherent margin; add arms B/D ablations on seed 0.
  7. Gate promotion on: Big-Math parity-or-better AND GSM8K Δ≥0.01 AND zero extra comm AND byte-identical OFF.

Unresolved contradictions

None material. All five agent reports agree on semantics, formulas, file:lines, and the additive-gated design. Minor reconciliations: (a) alpha/beta_anc shipped defaults (0.0/0.95) vs locked op point (0.25/0.50) — both correct, different layers; (b) rank1_direction.py lives at research/scripts/, not weight_proj/; (c) offline engine spans 338 matrices, the online anchor targets 196 proj matrices — Grams are additive, so consistent.