Skip to content

rational M2: the design spike pins every stage length - #57

Merged
tap merged 1 commit into
mainfrom
claude/sample-rate-expansion-strategies-ezqzu6
Oct 2, 2026
Merged

tap merged 1 commit into
mainfrom
claude/sample-rate-expansion-strategies-ezqzu6

Conversation

@tap

@tap tap commented Oct 2, 2026 •

Copy link
Copy Markdown
Owner

What this changes

Milestone M2 of rational/PLAN.md: the stage lengths are pinned numbers.

  • The pinned table (design.h's profile docstring, PLAN.md section 6): for every band of the vocabulary (2, 3, 4, 6, 8) and every profile, the smallest m whose L-th-band design meets the stopband with ≥ 1 dB margin on a 16384-point grid, N = 2mB − 1. profile carries the pins as taps_per_branch (0 = search), design_stage<R> reads them, a custom profile without pins is searched on the same grid (bounded at 128 branch taps), and stage_taps_per_phase<R> gives the mixed stages' ⌈N / L⌉.
  • notebooks/design_spike.ipynb, committed executed — the independent leg, as bridge's M2 spike was: the designer re-derived in numpy from the same published math, the search, the minimality check, the measured worst stopband and ripple per row, the mixed ratios' taps per phase, and the economy half-band decimator checked end to end through scipy's upfirdn (a tone just past the stopband edge aliases at −72.1 dB; passband tones within 0.001 dB). figure_digest.py as in bridge/notebooks/.
  • test_design.cpp enforces every row against the shipping designer: the pin equals the search, m − 1 misses the margin, the worst stopband is the table's within 0.05 dB, the ripple is within PLAN.md 2.3's candidate (now a bound: 0.01 dB per 70 dB stage, 0.0001 dB at transparent; measured ≤ 0.0025 / 0.00001), the mixed T values, the unpinned-profile search, and the two rows the coarse grid gets wrong.
  • Submodule pin → 8994f2f, nyquist: the spec search's grid is a parameter DspTap#50's merge on main (search_nyquist_m with a grid_points parameter; the identical tree to the branch commit this PR was drafted against).
  • The emulated legs skip the fine-grid sweeps of the long designs and the searches (bare_metal_main.cpp; Hexagon also the two throw tests); the 70 dB rows' pins, minimality and structure run on target.
  • rational/PLAN.md v0.5 fills the M2 table from the measurements and records M2 done; the family PLAN.md, both READMEs and both CLAUDE files follow.

Why

PLAN.md section 6, M2: a deployed stage's length must be a number in a table, not a search at construction, and the table must be measured from the designer that ships, not estimated.

Finding. nyquist.h's 1024-point default grid steps over a sidelobe of the long designs: the 8th-band transparent candidate reads −121.7 dB on 1024 points and −119.3 dB on 8192, so a 1024-point search under-pins two rows of the full table — band 4 balanced (103 → 111 taps) and band 8 transparent (383 → 399). 16384 points agree with 65536 within 0.003 dB for every design up to N = 399. Per the substrate rule the search's grid became a parameter in DspTap first (#50, default unchanged so DspTap's own pins hold); the rational engine pins on 16384 and states the grid as part of the pin. The half- and third-band rows of v0.3 are unchanged.

Verification

  • Clang 18 -Werror and GCC -Werror (all three TAP_SR_*_WERROR): the whole family battery, 200 / 200 (rational 25, with the pinned table measured through design_stage).
  • The notebook executed in the root requirements.lock environment (numpy 2.4.6, scipy 1.17.1): every row's N, worst stopband and ripple match the C++ measurement; its assertions (margin, minimality, grid convergence, structure, the upfirdn check) pass.
  • scripts/tidy.sh over the changed TUs: clean.
  • Cortex-M33 under QEMU: the one-shot battery passes in 67 s with the filter above.
  • M55, Hexagon, MSVC and macOS: CI.

The measured table (N / nonzero, worst stopband on 16384 points):

band super_economy economy balanced transparent
2 35 / 19 (−72.8) 43 / 23 (−71.9) 51 / 27 (−71.5) 123 / 63 (−121.7)
3 47 / 33 (−71.2) 65 / 45 (−71.3) 77 / 53 (−71.2) 149 / 101 (−121.1)
4 63 / 49 (−71.4) 87 / 67 (−71.1) 111 / 85 (−72.3) 199 / 151 (−121.4)
6 95 / 81 (−71.5) 143 / 121 (−72.1) 167 / 141 (−72.2) 299 / 251 (−121.3)
8 127 / 113 (−71.5) 191 / 169 (−72.1) 223 / 197 (−72.1) 399 / 351 (−121.2)

Mixed stages' taps per phase at economy: 3/2 22, 2/3 33, 4/3 22, 3/4 29, 8/3 24, 3/8 64 (a 2/3 stage costs 33 MACs per output, not the 26 the v0.2 estimate carried; the matrix's MAC figures are regenerated at M4, risk S1).

Notes for the reviewer

  • Submodule pin moved — to nyquist: the spec search's grid is a parameter DspTap#50's rebase merge on main (8994f2f), so it stays reachable.
  • Contract change, pre-release: profile gains taps_per_branch and design_stage no longer searches for the four named profiles; nothing shipped consumes the engine yet (M1 landed yesterday).
  • Notebook executed (new).
  • The v0.2 plan's mixed-stage estimates (T per phase) were optimistic by up to 27 %; the plan's M2 table now records the measured T beside a note, and the MAC-based factoring decisions of R3 are re-checked at M4 from these counts.

🤖 Generated with Claude Code

https://claude.ai/code/session_015VR1VC4SDGxHZQQsQvPBaA

The pinned table (notebooks/design_spike.ipynb, executed; design.h's
profile docstring): for every band of the vocabulary (2, 3, 4, 6, 8) and
every profile, the smallest m whose L-th-band design meets the stopband
with >= 1 dB margin on a 16384-point grid, N = 2mB - 1. profile carries
the pins as taps_per_branch (0 = search), design_stage<R> reads them, a
custom profile without pins is searched on the same grid, and
stage_taps_per_phase<R> gives the mixed stages' ceil(N / L).

The notebook is the independent leg, as bridge's M2 spike was: the
designer re-derived in numpy from the same published math, the search,
the minimality check, the measured worst stopband and ripple per row, the
mixed ratios' taps per phase, and the economy half-band decimator checked
end to end through scipy's upfirdn (a tone just past the stopband edge
aliases at -72.1 dB, passband tones within 0.001 dB). test_design.cpp
enforces every row against the shipping designer: the pin equals the
search, m - 1 misses the margin, the worst stopband is the table's within
0.05 dB, the ripple is within PLAN.md 2.3's candidate (now a bound).

Finding: nyquist.h's 1024-point default grid steps over a sidelobe of the
long designs (the 8th-band transparent candidate reads -121.7 dB on 1024
points and -119.3 dB on 8192) and under-pins two rows, band 4 balanced
(103 -> 111) and band 8 transparent (383 -> 399); 16384 points agree with
65536 within 0.003 dB. The search's grid became a parameter in DspTap
first (tap/DspTap#50) and the pin moves to that merge.

The emulated legs skip the fine-grid sweeps of the long designs and the
searches (bare_metal_main.cpp; Hexagon also the throw tests): the 70 dB
rows' pins, minimality and structure run on target (67 s on M33).
rational/PLAN.md v0.5 fills the M2 table from the measurements and
records M2 done; the family plan, READMEs and CLAUDE files follow.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015VR1VC4SDGxHZQQsQvPBaA
@tap
tap force-pushed the claude/sample-rate-expansion-strategies-ezqzu6 branch from 5918e11 to 1ca7762 Compare October 2, 2026 00:47
@tap
tap marked this pull request as ready for review October 2, 2026 00:47
@tap
tap merged commit 907aad5 into main Oct 2, 2026
2 checks passed
@tap
tap deleted the claude/sample-rate-expansion-strategies-ezqzu6 branch October 2, 2026 00:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants