RYS template probe (--rys-probe) — design + implementation
Status: IMPLEMENTED (v1.1 = ΔPPL + graded task-probe → 2 templates + external
eval hook), host-only, validated on Qwen3-4B (2026-07-13, #669/#670). Not yet
captured as a patch / committed — ships on request. Companion to the shipped RYS
execution engine,
rys_layer_duplication.md (--repeat-layers, patch 0122).
0. What shipped (v1.1)
A new PROBE program mode in llamafile/rys_probe.cpp (dispatched from
llamafile/main.cpp; flag detected in llamafile/args.cpp). It:
- Parses args as the
PERPLEXITYexample, then disables-fit(the probe sizes its own smalln_ctx; the fit path builds a trial context that we don't want mutating our cparams — it aborted the flash-attn print otherwise). - Loads the model once (
common_init_from_params(params, /*model_only*/true)). - Enumerates contiguous mid-stack blocks
[i,j)in the band (default[max(3,n/8) .. n-max(3,n/8))) ×--rys-probe-widths. Default widths =auto= every width1 .. (band)(an explicitW1,W2,…list overrides). - Scores base first (empty plan = reference), then each candidate by
rebuilding only the context (
llama_init_from_modelwithcp.repeat_layers = "i,j"; the model/weights are plan-independent), computing a compact self-contained ΔPPL over a small corpus (-f/-p, else a built-in slice) + a graded built-in task-probe (24 arithmetic / word-problem / factual / sequence items, greedy decode with substring-match early-stop; the fraction correct is thetaskscore, used both as a rank signal and a guardrail — a candidate that drops below basetaskis discarded). - Prints a ranked table of the surviving wins, then exactly two ready-to-paste
templates:
- MOST EFFICIENT — best PPL-drop per added layer (largest
ΔPPL/(#dup layers)). - MAX GAIN — largest absolute PPL-drop / lowest PPL. When no wider block beats the efficient one, the two coincide and the probe says so honestly ("the same block is both").
- MOST EFFICIENT — best PPL-drop per added layer (largest
Flags: --rys-probe (enter probe mode) · --rys-probe-widths auto|W1,W2,…
(default auto) · --rys-probe-band auto|i:j · --rys-probe-topk N (default 10,
rows in the table). Corpus comes from the standard -f/-p; -ngl, -c, -fa
flow through.
The last mile — external eval (c). ΔPPL + the built-in task-probe are cheap
proxies, not a verdict. The probe's two templates feed
perf/llamafile/rys-probe-eval.sh,
which boots llama-server for BASE + each template (only --repeat-layers
changes between arms) and runs a user-supplied EVAL_CMD (e.g. a GSM8K / MMLU
slice via the omk harness) against each, printing a real task-score comparison
with deltas-vs-base. That is the analogue of dnhkng's graded benchmark, minus the
days-long search — the final pick is a downstream score, not a proxy.
Validation (Qwen3-4B-Q4_K_M, CPU):
- band [8,16), widths {2,4}, 12 candidates: base ppl 30.15; wins
9,13(ppl→24.95, Δ−5.20, 11.1% over) and10,12(ppl→27.38, Δ−2.76, 5.6% over) → MAX GAIN =9,13, MOST EFFICIENT =10,12(−1.38/layer), two distinct frontier points. - band [10,16), widths
auto(all 1..6), 14 candidates: base ppl 30.15 / task 0.917; winner11,13(ppl→28.03, Δ−2.12, 5.56% over, task 0.958) — sole survivor wide enough, so MOST EFFICIENT == MAX GAIN (honest "same block is both").
Contiguous mid-stack winners, no boundary layers, consistent with §1's structural
prior. Build gotcha: a common_params struct field addition needs a clean
host rebuild (rm -rf o + make) — incremental build:llamafile:make left
ABI-skewed common_params consumers (bug-2160/2166 class); see BUILD_CYCLE.md. A
later default-value change to an existing field (e.g. widths default
1,2,4→auto) is ABI-safe and rebuilds incrementally.
Question (user, 2026-07-13): can opencoti-llamafile grow a flag that probes a GGUF for good RYS layer-duplication templates, without reproducing dnhkng's multi-day search?
Short answer: yes, cheaply — because we can replace the two expensive halves of the reference method (a huge candidate space + a downstream-benchmark score) with (a) a pruned candidate space we already know the shape of, and (b) a perplexity scan the binary can already run. The result is a shortlist generator (minutes–~1 h), not a full Pareto search — the user still validates the top-K on the real retrieval/benchmark harness.
1. What the reference method (dnhkng RYS-II) actually does, and why it's slow
From https://dnhkng.github.io/posts/rys-ii/ (64-layer stack):
| Stage | Mechanism | Cost |
|---|---|---|
| Full scan | every contiguous (i,j) block, duplicate [i,j), measure Δ |
many single-block evals |
| Beam search | width 24, depth 3–6, greedily stack blocks | 3,024 candidates, ~14 h |
| Surrogate | XGBoost trained on 4,411 measured configs ranks ~2,000,000 candidates | top 100 fully benchmarked |
| Validate | Pareto front (better and smaller) → 4 optimal configs | large val sets |
Two things make it days-long:
- The score is a downstream benchmark —
Math120(16–120 math Qs) +EQ140(EQ-Bench scenarios). Every candidate needs generation + grading. Small "probe" sets are used for the search, larger sets for finalists, but it is still an accuracy eval per candidate. - The space is ~2 M contiguous+stacked blocks, so a surrogate (XGBoost on 4.4 k measured points) is needed just to rank it.
Key structural finding we can exploit: every Pareto-optimal template was a
single contiguous mid-stack block. The cheapest winner was (33,34)
(+1.56% params); the widest useful one (26,34) (+12.5%). Nothing near the
first/last layers ever won — which is exactly our own boundary-layer result
(rys_layer_duplication.md §10a, bug-2164: duplicating
early/late layers yields incoherent output).
2. What opencoti already has
- The execution engine:
--repeat-layers "i,j"setscparams.layer_planand re-runs[i,j)weight-shared, load-time, all archs (patch 0122). An in-process probe just calls this in a loop with different plans. - A perplexity path in the vendored
llama.cpp(used throughout the TCQ/KV campaigns) — a forward pass over a fixed corpus returning mean NLL. - KLD-vs-reference tooling (logit-equiv harness).
- The boundary-layer advisory logic that already computes the safe mid-stack
band
[max(3,n/8) .. n-max(3,n/8)).
So the only missing piece is an outer loop + a scorer + a ranked report.
3. Proposed design — a PPL-scan shortlist, not a Pareto search
3a. Score: perplexity on a small fixed corpus (NOT KLD-to-base)
- Use ΔPPL vs the base model on a small held-out corpus. RYS improves the model by changing it, so a candidate that lowers PPL below base on a quality-correlated corpus is the cheap analogue of "Math/EQ went up". One forward pass over a few-k-token corpus per candidate — seconds to a couple of minutes on a served GPU, no generation/grading.
- KLD-to-base is the wrong optimization target here and we must not use it as the score: RYS intentionally diverges from base, so minimizing KLD rewards doing nothing (this is the same trap as "logit-equiv vs base" for RYS — rys_layer_duplication.md). KLD is still useful as a guardrail: a candidate whose KLD explodes (or whose PPL blows up) is the boundary-fragility / broken-merge regime — flag and drop it, don't rank it.
- Caveat, stated honestly: PPL is a weak proxy for reasoning/EQ gains. dnhkng used task accuracy precisely because self-merge gains are small and PPL-noisy. So the probe's job is to shortlist, and the last mile stays a real eval.
3b. Space: prune to contiguous mid-stack blocks (no surrogate needed)
Because winners are always contiguous mid-stack, enumerate only:
- contiguous blocks
[i,j)fully inside the safe band[max(3,n/8) .. n-max(3,n/8)), - with width
1 .. w_max(defaultw_max ≈ ceil(n/8)— the +12.5% ceiling), - optionally strided starts to cap the count.
For a 64-layer model that is a few hundred candidates, not 2 M — so a direct scan needs no XGBoost surrogate at all. (An optional depth-2 greedy stack, à la their beam search, can be a follow-up once single-block ranking is trusted.)
3c. Surface: --rys-probe
llamafile --rys-probe \
-m model.gguf \
[--rys-probe-corpus calib.txt] # default: bundled ~4k-token slice
[--rys-probe-widths 1,2,4,8] # block widths to try
[--rys-probe-band auto|i:j] # candidate band (default = advisory band)
[--rys-probe-topk 10] # how many to print / emit
Load the model once; for each candidate plan set layer_plan, run PPL over the
corpus (base = empty plan, run first as the reference), record
(block, overhead_pct, ppl, dppl_vs_base, kld_guardrail). Emit a table sorted by
ΔPPL and a Pareto view (ΔPPL vs overhead%) — the same "better and smaller"
lens dnhkng used, so the user picks a point on the frontier. Output the winner as
a ready-to-paste --repeat-layers spec.
Reuses the 0122 engine + the existing perplexity path; host-only, no CUDA, no new kernels.
3d. Cost
N_candidates × one small-corpus PPL forward. A few hundred candidates × a
few-k-token PPL ≈ minutes to ~1 h on one GPU for a mid-size model — vs days.
The saving is entirely from (1) pruning 2 M → hundreds (structural prior) and
(2) PPL instead of graded generation.
4. Honest limitations
- Shortlist, not verdict. PPL can rank a candidate above one that would win on reasoning/EQ. Ship it as "top-K to validate", and keep the real RULER/benchmark run as the decider — exactly how dnhkng re-measured finalists.
- PPL validity on franken-merges is model-dependent. For some Gemma-4 omnimerge GGUFs raw PPL is meaningless ([project note: PPL/KLD invalid on Gemma merges]); the probe must sanity-check the base PPL is finite/reasonable and warn (or fall back to a tiny task-probe) when it isn't.
- Single-block first. Stacked (beam) templates are a phase-2 extension; the single-contiguous-block scan already covers every dnhkng Pareto winner.
5. Recommendation
Feasible and cheap. Build it as an opt-in --rys-probe subcommand that
(1) enumerates contiguous mid-stack blocks, (2) scores each by ΔPPL on a small
fixed corpus with a KLD/PPL-blowup guardrail, (3) prints a ΔPPL-vs-overhead
Pareto shortlist and the best --repeat-layers spec. It is a thin wrapper over
the already-shipped 0122 engine + the existing perplexity path — no kernels, no
CUDA. It does not reproduce dnhkng's rigor (no 2 M-candidate surrogate, no
graded Math/EQ); it deliberately trades that for a minutes-scale shortlist the
user finishes with one real eval pass.
Decision (user, 2026-07-13, #669/#670): shipped (b) ΔPPL + a graded
arithmetic/short-reasoning task-probe, output narrowed to two templates
(MOST EFFICIENT + MAX GAIN), plus a (c) external-eval hook
(perf/llamafile/rys-probe-eval.sh) that runs a real downstream eval per template
so the final pick is a task score, not a proxy. See §0.