GitHub ↗

Sparse parity: an accuracy–energy benchmark

Three graded tiers of the sutro-problems sparse-parity task — what the curves mean, the algorithms behind them, and the honest list of pitfalls. August 26, 2026.

The problem

Each instance hides a secret set of k of n bit positions. A row's label is the XOR (parity) of the bits at those positions. A submission is a straight-line program in the v3 instruction set of the simplified Dally model (8-bit values, no loops, no branches, no data-dependent addressing). In one invocation it receives mtrain labeled rows and mtest unlabeled rows, and must output all mtest predicted labels.

Energy is the program's static read cost: every operand read of address a costs ⌈√a⌉ (a Manhattan-distance proxy — far memory is expensive). Scoring is joint: one program, one energy number covering training-decode and test-prediction together, so there is no phase interface to game. Accuracy is aggregated over the whole suite (never thresholded per instance) and reported as the normalized advantage η = 2·accuracy − 1 (0 = coin-flipping, 1 = perfect), which is meaningful because test rows come in bitwise-complement pairs: with odd k a row and its complement carry opposite labels, so any constant guess scores exactly 50%.

Training sets are rejection-sampled until exactly one candidate secret explains all the labels ("unique identifiability"). That keeps 100% accuracy always attainable — the benchmark measures approximation quality, not dataset ambiguity — and it gives verified decoders a powerful guarantee used throughout: any weight-k solution consistent with the training rows is the secret.

nk mtrainmtest candidates C(n,k)instruction cap
Tier 1 — approximate123 832220 100,000
Tier 2 — scaled joint (superseded)325 18256201,376 250,000
Tier 3 — mask recovery (current)325 18—201,376 2,000,000

How to read the curves: the Pareto frontier

Every submitted circuit is one point: its measured accuracy and its energy. An algorithm family with a knob (how many candidates to check, how many restarts to run, how many outputs to compute) traces a curve of such points. The Pareto frontier is the lower envelope over everything submitted:

E*(A) = min { energy(P) :  P is a submitted program with accuracy(P) ≥ A }

Read it as a cost function: pick a target accuracy on the x-axis, go up to the gray step line — that is the least energy any known program needs to hit that target. It only steps up as the target rises. A point above the frontier is dominated (something cheaper achieves the same accuracy); a new algorithm "wins" by pushing some stretch of the frontier down or to the right. The benchmark's object of study is the shape of this envelope — in particular where it bends when brute force stops being affordable.

Tier 1 (n=12): the approximate benchmark

Energy required vs accuracy for the n=12 tier: mask decoder frontier from 52.5% at ~50k reads to 100% at 444k reads; try-each-candidate 5x more expensive; ISD restarts close but bending up; GE + Gray scan a flat line at ~480k reads.
n=12, k=3, 8 train / 32 test. Deterministic public suite: all 220 secrets × 8 repetitions. The enumeration baselines' accuracy here is exact — identical under every suite key. (The output-truncation curve that used to appear here was dropped: uniformly dominated, and inconsistent with the mask-recovery direction of Tier 3.)

Algorithm: try-each-candidate (blue)

Decode: for each candidate triple T among the first q (of 220, in fixed order), compare the parity of columns T against all 8 training labels, producing an indicator ind_T. By unique identifiability, exactly one candidate matches — so decoding succeeds iff the secret is among the q examined. Predict: each test row is labeled OR over T of (ind_T AND parity_T(x)), so prediction re-scans all q candidates per output — the source of this family's 5× energy handicap. Advantage is exactly η = q/220.

Algorithm: mask decoder (green — owns the frontier at this size)

Same decode phase, but the q one-hot indicators are compressed once into 12 mask bits, mask_c = OR of ind_T over candidates T containing column c — the mask is the recovered secret. Each prediction is then XOR_c (mask_c AND x_c): 12 ANDs and 11 XORs per output, independent of q. Same accuracy formula, ~5× less energy (exact recovery at 444,389 reads instead of 2,209,461).

The scalable families at this size (orange, yellow)

The same two polynomial algorithms that own the n=32 tiers are plotted here for comparison. ISD restarts (orange) track the mask decoder closely up to ~75% and then bend away (diminishing returns as rotated information sets start overlapping — only 12 distinct subsets exist at n=12). The GE + null-space Gray scan (yellow) is a nearly horizontal line: it pays a fixed ~477k-read Gaussian-elimination-plus-basis cost, after which sweeping all 2⁴ = 16 null-space solutions is almost free — 65% → 99% accuracy for ~1% more energy. At n=12 both sit above the enumeration frontier: with only 220 candidates, brute force is still the best buy. The n=32 graphs below show exactly this ordering inverting — the benchmark's central phenomenon.

Energy (reads)Accuracy ηOpsScore time Cheapest strategy
13750.0%0.00 321 msconstant guess
122,52362.5%0.25 3,0679 msmask decoder, q=55
222,25575.0%0.50 5,37719 msmask decoder, q=110
397,83895.0%0.90 9,07326 msmask decoder, q=198
444,389100.0%1.00 9,99728 msmask decoder, q=220
2,209,461100.0%1.00 36,709101 mstry-each-candidate, q=220
482,04599.3%0.99 13,92838 msGE + Gray scan, s=15

Score time = one circuit against the whole 1,760-instance dev suite (220 secrets × 8 reps × 32 rows = 56,320 labels), after the 0.09 s suite build. See Run times.

Tier 2 (n=32, joint): where enumeration dies

Status: superseded by Tier 3 below (which drops the test set and raises the cap); kept because its measurements motivate both changes.

At n=32, k=5 the candidate count jumps to C(32,5) = 201,376 and the linear system's null space has 2n−m = 214 solutions. Under the tier's 250,000-instruction cap both brute-force families are priced out:

What remains is the polynomial family — this instance of the problem is syndrome decoding of a random linear code, so the natural scalable algorithm is information-set decoding.

Algorithm: ISD restarts (Prange) — the reference circuit

One restart: pick an "information set" of m=18 of the 32 columns; copy those training columns plus the labels into an 18×19 augmented matrix; run branchless GF(2) Gaussian elimination (pivoting is done with cmp/select chains since the ISA has no branches); read out the zero-free-variable solution; accept it only if it has weight exactly k and reproduces every training label. Unique identifiability turns that acceptance test into a proof: an accepted solution is the secret. T restarts use rotating column subsets and OR their accepted solutions into a 32-bit mask; prediction is the O(n) mask predictor. Each restart succeeds when the secret happens to lie inside its information set — probability ∏i<5(18−i)/(32−i) ≈ 4.25%, confirmed at 4.22% pooled over fresh suites (single deterministic suites scatter 2.2–5.6%; see pitfall 7 and the spatial-model analysis). The circuit is all-or-nothing by construction: on every suite instance its output is either exactly correct or exactly all-zeros — never garbage.

Energy required vs accuracy for the n=32 tier: ISD restart frontier from 50.9% at 2.6M reads to 57.4% at 10.2M reads; capped enumeration pinned near 50% at 14-40M reads; faint replicas show suite noise.
n=32, k=5, 18 train / 256 test, 250k-instruction cap. Dev suite: 128 sampled secrets × 8 repetitions. Faint blue replicas: the same ISD sweep under two other suite keys — the sampling noise you buy by giving up determinism (±2–4pp here; the final suite doubles the secret sample).
T restartsOps Energy (reads)Accuracyη Score time
152,9262,641,072 50.9%0.0180.14 s
3125,9025,657,464 53.2%0.0640.32 s
6235,36610,181,932 57.4%0.1470.57 s

Score time = one circuit against the 1,024-instance dev suite (128 secrets × 8 reps × 256 rows = 262,144 labels), after the 1.8 s suite build. The whole tier tops out at η = 0.147: under the 250k cap nothing reaches the upper 85% of the curve, which is why Tier 3 raises it.

Everything above η ≈ 0.15 is currently open: candidates include packed GE (byte-packing historically bought 5–15× in this repo, which buys more restarts under the cap), partial null-space scans grafted onto a restart, and Stern-style collision decoding re-expressed without hashing. The mtest = 256 choice came from the measured energy split of this reference circuit — prediction costs ≈4,400 reads per output vs ≈1.5M per decode restart, so the test side carries 11–43% of total energy across the frontier: both knobs stay relevant, neither dominates.

Tier 3 (n=32, mask recovery): the current tier

Two changes relative to Tier 2, both motivated by its measurements. The test set is dropped. Sparse parity makes it provably redundant: a circuit that has not identified the secret predicts test rows at exactly 50% (any strict-subset parity is uncorrelated with the true one), so joint test accuracy was always (1 + recovery)/2 — and every measured Tier-2 circuit was empirically all-or-nothing. The submission now outputs the secret itself: 32 mask cells, scored by exact match (well-defined because training sets are uniquely identifiable). The accuracy axis becomes the secret recovery rate; the chance floor drops from 50% to 1/C(n,k) ≈ 5·10⁻⁶; complement pairing is no longer needed (so even k would be legal); and there is nowhere to offload work — a fixed standard evaluator can label rows from a recovered mask at ~4,400 reads/row, a constant that cannot change rankings. The cap rises to 2,000,000 so that a known family reaches 100% recovery.

Algorithm: GE + null-space Gray scan — reaches 100%

Full-width branchless GF(2) elimination, then extraction of the null-space basis (dimension n − m = 14 w.h.p.), then a Gray-code walk through the solution space w = s₀ ⊕ Σ aⱼ·bⱼ, capturing any weight-k visitor — every visited w solves the training system, so identifiability makes a weight-k visitor the secret. The step knob s sweeps the curve: s=0 is min-support GE (6.1% recovery), s=2¹⁴−1 visits the whole space (measured 100.0% at 1.77M instructions / 43.3M reads). The curve is concave because the secret's Gray coefficients are low-weight (≈2.2 of 14 ones on average), so the walk tends to visit it early — s=4,095 already recovers 74%.

Energy vs secret recovery rate for the mask tier: ISD restarts cheapest below ~25% recovery, the Gray-scan family owns the frontier up to 100% at 43.3M reads, capped enumeration recovers at most 8% at dominated energy.
n=32, k=5, 18 train, no test set, 2M-instruction cap. Dev suite: 128 sampled secrets × 8 reps; faint replicas: two other suite keys (spread ≤5.5pp). ISD restarts own the frontier below ~25% recovery; the scan owns the rest; enumeration (≤15,000 of 201,376 candidates fit the cap) is dominated everywhere.
Energy (reads)Recovery OpsScore timeCheapest strategy
1,505,8625.8%36,542 0.09 sISD, T=1
12,042,48022.5%291,958 0.70 sISD, T=8
23,676,53974.2%593,160 1.46 sscan, s=4,095
43,325,468100.0%1,772,808 4.38 sscan, s=16,383 ★

Score time = one circuit against the 1,024-instance dev suite, after the 1.7 s suite build. A full adjudication run of the most expensive circuit — fresh hidden 2,048-instance suite included — measures 8.2 s.

Cap margins at 2M: the full scan uses 89% of the cap; packed candidate enumeration needs ≈4.2M ops (2.1× over — the thin margin to watch), naive ≈26M (13×). Under the cap, enumeration tops out at ≤8% recovery for 35M reads: dominated everywhere, even with the raised budget. Headroom for submissions: byte-packing the scan state (~65 ops/step instead of 96) and packing GE both shift the frontier left several-fold.

Why do these circuits need 10⁵–10⁶ instructions?

Because the instruction set is straight-line — the count is not algorithmic work, it is algorithmic work × unrolling × branchless overhead:

Measured across the reference circuits: straight-line instruction count ≈ (RAM-model bit-operation count) × 1.2–18, where the factor tracks how much data-dependent control and addressing the algorithm needs — 1.2× for fully oblivious enumeration, 2.7× for the Gray-scan step, 5.6× for select-chain pivoting, 17.8× for dynamic matrix reads (the O(live-cells)-per-access multiplexer bound, hit exactly). Full derivations and literature anchors in the spatial-model analysis.

Pitfalls in the scoring and benchmark design

  1. The public suite is minable — never adjudicate on it. An adversarial review demonstrated a working exploit on the n=12 tier: a submission that decodes only 50 candidates and falls back to per-output input bits mined from the published suite passed the η≥0.25 scorer at lower cost than the honest entry, while its true advantage was 0.223. Deterministic public suites are for development; rankings near a threshold must be confirmed on a held-out suite key or fresh hidden randomness (evaluate_scaled(ir, suite_key=None) draws from SystemRandom).
  2. Sampled secrets must be fresh or hidden. The scaled tier samples 128–256 of the 201,376 secrets. The dev suite's sample is derivable from its public key, and a ~33,000-instruction circuit (13% of the cap; the 256-output prediction phase alone has a ≥8,192-instruction floor) that enumerates just those 128 secrets scores a perfect η = 1.0 on the dev suite — Pareto-dominating the honest frontier — while measuring exactly 0.0 on a fresh key. This is the sharpest cheating vector introduced by scaling; it is why adjudication uses fresh hidden secrets.
  3. Output truncation must be priced, not banned — via the decode/predict energy balance. "Compute f of the outputs, guess the rest" is a legitimate strategy; complement pairing makes its accuracy exactly linear in f. The danger is imbalance: if prediction dominates energy the whole leaderboard becomes output-truncation variants (an early n=8 target suffered exactly this); if decode dominates, the output knob is irrelevant. Set mtest ≈ D/P of the reference decoder and re-derive it per tier.
  4. Candidate enumeration is not the only brute force. The solution space of the linear system has 2n−mtrain elements, and identifiability makes any weight-k element the secret. At an earlier prototype size (n=24, m=17, gap 7) a min-weight scan over all 2⁷ solutions reached η≈0.91 almost for free. mtrain=18 (not 20) was chosen at n=32 specifically so the full 214 scan exceeds the cap. Any future tier must check both exponentials against its cap.
  5. The instruction cap is part of the contract. Energy alone cannot kill brute force — enumeration would still populate the curve, just expensively. The cap defines the compute class, and its margins should be stated per family: the full null-space scan needs ≈1.1M instructions (a real in-ISA Gray-code scan measures ~65 per step — about 4× the cap) and candidate enumeration ≈26M (about 100× the cap), while the T=6 reference circuit uses 94% of the cap. The scan-side margin is the thin one — a cap of ~1M would silently re-admit it. Raising the cap is a benchmark-version change, not a tuning knob. (The evaluator also bounds working memory, closing a multi-GB allocation vector.)
  6. Accepted noise, quantified. With sampled secrets and fresh randomness, measured accuracy on the scaled dev suite varies by up to ±4pp across suite keys (deterministic information sets interact with which secrets get sampled). Energy is static and exact — only the accuracy coordinate is noisy. Near-threshold comparisons should average ≥3 fresh runs; the final suite's 2× secret sample cuts the spread by ~√2.
  7. Single-suite measurements can masquerade as effects. We previously attributed a "measured 1.8%/restart vs textbook 4.25%" ISD gap to rank-deficient information sets; deeper measurement (pooled over fresh suites) shows 4.22% — matching the textbook Prange ratio — with 2.2–5.6% suite-to-suite scatter because success is secret-dependent under fixed rotating subsets. The genuine rank correction is real but only visible at n=12 (19.2% vs ideal 25.45%). Publish pooled curves with spread, not single deterministic points. The companion spatial-model analysis carries the full sanity-check table; the same lesson cut the other way at n=12, where "linear algebra caps at ~53%" was wrong — min-support Gaussian elimination reaches 64% (η≈0.28).
  8. Complement pairing needs odd k. The exact-50% guarantee for constant guesses relies on labels flipping under bitwise complement, which holds only for odd k (both tiers use k=3, k=5). An even-k tier needs a different balancing scheme (e.g. full-cube partitions).
  9. No data-dependent addressing cuts both ways. Submitted circuits cannot hash or index, so meet-in-the-middle attacks cannot be expressed — which is why the cap arithmetic above holds. But the generator is ordinary Python: identifiability checking uses packed signatures over all 201,376 candidates in milliseconds. Never assume suite-construction tricks are available to submissions, or vice versa.

Run times

Measured on an Apple-silicon laptop, each command in a fresh process (imports included). Regenerating everything — all three curves plus the full test suite — takes about 65 seconds.

These timings date from this report (2026-08-26), when all three tiers lived in the tree. Tiers 1 and 2 have since been retired: the repository now ships only the Tier 3 mask task as a single self-contained mask_sparse_parity.py, with the curve script at doc/generate_mask_graph.py. The retired tiers' modules, scripts and submissions remain in git history; the numbers below are kept as the measurement record.

commandtimewhat dominates
generate_graph.py (Tier 1 curve)2.0 s 32 circuit evaluations; suite build is 0.09 s
generate_scaled_graph.py (Tier 2 curve)15.7 s 3 suite builds (5.3 s) + 25 evaluations
doc/generate_mask_graph.py (Tier 3 curve)44.4 s 13 evaluations of large circuits (~25 s) + 3 suite builds
mask_sparse_parity.py (Tier 3 references)14.3 s 5 circuits including the 1.77 M-op full scan
scaled_sparse_parity.py (Tier 2 references)2.9 s ISD T=1/3/6 + suite build
pytest (all 40 tests)3.0 s 1.2 / 1.3 / 0.8 s for Tiers 1 / 2 / 3

Where the time goes

Suite generation happens once per distinct key and is then cached in-process. Tier 1 is nearly free (0.09 s for 1,760 instances) because it enumerates all 220 secrets against a cheap identifiability check; Tiers 2 and 3 cost 1.7 s for a 1,024-instance dev suite and 3.4 s for a 2,048-instance final suite, since rejection sampling must verify uniqueness against all 201,376 candidates per draw.

Circuit evaluation is dominated by per-instruction dispatch into the batched numpy engine, not by suite size: measured per-op cost is 0.9 µs at batch 64, 1.14 µs at batch 1,024, and only 4.3 µs at batch 16,384. The working rule is

score seconds ≈ instructions × 1–2.5 µs      (nearly independent of suite size)
compile seconds ≈ instructions × 1.1 µs

So doubling the suite from 1,024 to 2,048 instances is close to free, while doubling circuit size doubles the time. One adjudication point — a single circuit on a fresh hidden final suite, generation included — measures 4.1 s (Tier 2, ISD T=6) to 8.2 s (Tier 3, full scan). Extrapolated to the 10 M-instruction cap discussed in the companion analysis: ~11 s compile plus ~15–25 s score, i.e. roughly 30–40 s per point.

Reproduce

git clone https://github.com/cybertronai/sutro-problems
cd sutro-problems/sparse-parity        # needs python3 + numpy + matplotlib

python3 doc/generate_mask_graph.py     # tier-3 curve + accuracy bands, ~4 min
python3 mask_sparse_parity.py          # scan/ISD/enum reference measurements
python3 -m pytest test_mask_sparse_parity.py

Tiers 1 and 2 are no longer in the tree. To rerun their curves, check out a commit from before the cleanup — e.g. git checkout 8c83dad -- sparse-parity — which restores generate_graph.py, generate_scaled_graph.py and their modules.

Dev evaluations run in seconds on an Apple-silicon laptop (suites are cached after first build); a fresh-randomness adjudication run of one circuit — suite generation included — stays under a minute.