Add tap::dsp::nn: dense and GRU inference kernels - #15
Merged
Conversation
The seventh primitive, lifted from MuTap's learned residual suppressor at its wake-word plan's M3 so the keyword spotter can be a different head on the same arithmetic. basic_dense<Sample> (y = act(W x + b), row-major [out x in], linear / tanh / sigmoid) and basic_gru<Sample> (Cho et al. 2014 in PyTorch's nn.GRU convention, gates r, z, n), with the contract as numbers: float32 weight storage converted to Sample at the point of use, bias-first ascending-j accumulation in Sample (no double arithmetic in the float profile), zero state after construction and reset(), moved-in and owned weights so a copied layer stays valid, noexcept and allocation-free processing. The loop order is exactly the suppressor's, so MuTap's refactor onto these kernels is bit-identical. tests/test_nn.cpp pins the layout on hand-computed numbers, the activation forms, the gate order through each block's biases, the GRU formula against an independent long-double restatement that sums in the opposite order (measured: double 2.2e-16, float 1.0e-7), reset and deep-copy semantics, noexcept, and float-tracks-double at the suppressor's geometry (measured state 6.3e-7, gains 2.5e-7). 156 tests green with -DTAP_DSP_WERROR=ON; clang-tidy clean. README section and the primitive count in CLAUDE.md. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JuUg1ZBxm3fyBqWgQv6H1G
tap
pushed a commit
to tap/MuTap
that referenced
this pull request
Sep 5, 2026
The wake-word plan's M3: the dense and GRU arithmetic now lives in DspTap (tap::dsp::nn, tap/DspTap#15) and nn_suppressor consumes basic_dense and basic_gru. The weight arrays are moved into the kernels at construction (one copy in memory). The kernels' loop order is exactly the one this header carried, so the promotion is bit-identical in both profiles on the shipping v2 model against the pre-refactor binary; the six parity cases pass at the M2 depths, the M2 battery passes unchanged, and the nn_suppressor ratchet moved -0.12 % / -0.10 % on m55 and -0.19 % / -0.11 % on m33 (16 k / 48 k) with every other scenario unchanged to the instruction. submodules/dsptap points at the M3 tree; repoint at the identical tree on main once the DspTap PR merges. Follow-up to the M2 finding on the Cortex-M33 leg: the speech predictor's pitch search (lpc.h) accumulated its normalized correlation in double for every lag from 32 to 400 over a 1024-sample window — about a million software double operations per 64-sample block on a core without FP64, and two thirds of the tonal PEM headline's 1085 s under qemu mps2-an505 (the test harness's deliberate double room simulation was the other third: 772 s with it switched to float). The search now accumulates in Sample. The double profile is bit-for-bit unchanged; the float rows' measured numbers are unchanged to the MSG bisection's 0.5 dB quantum (kalman-loop tonal ASG +7.81 dB before and after, PEM tonal +10.84 -> +11.13, ERLE and misalignment identical). The tonal headline now takes 31 s on the M33 emulator and the full float selection 174 s, so the MUTAP_ON_TARGET_SOFT_FP64 exclusion is removed: the M33 leg runs exactly the M55's selection. docs/wake-word-plan.md carries the M3 record and the corrected M2 finding; HANDOFF.md points at M4. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JuUg1ZBxm3fyBqWgQv6H1G
tap
pushed a commit
to tap/MuTap
that referenced
this pull request
Sep 5, 2026
tap/DspTap#15 merged by squash; this is the identical tree, reachable after branch cleanup. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JuUg1ZBxm3fyBqWgQv6H1G
tap
added a commit
to tap/MuTap
that referenced
this pull request
Sep 5, 2026
…h accumulates in Sample (#46) * M3: nn_suppressor on tap::dsp::nn; pitch search accumulates in Sample The wake-word plan's M3: the dense and GRU arithmetic now lives in DspTap (tap::dsp::nn, tap/DspTap#15) and nn_suppressor consumes basic_dense and basic_gru. The weight arrays are moved into the kernels at construction (one copy in memory). The kernels' loop order is exactly the one this header carried, so the promotion is bit-identical in both profiles on the shipping v2 model against the pre-refactor binary; the six parity cases pass at the M2 depths, the M2 battery passes unchanged, and the nn_suppressor ratchet moved -0.12 % / -0.10 % on m55 and -0.19 % / -0.11 % on m33 (16 k / 48 k) with every other scenario unchanged to the instruction. submodules/dsptap points at the M3 tree; repoint at the identical tree on main once the DspTap PR merges. Follow-up to the M2 finding on the Cortex-M33 leg: the speech predictor's pitch search (lpc.h) accumulated its normalized correlation in double for every lag from 32 to 400 over a 1024-sample window — about a million software double operations per 64-sample block on a core without FP64, and two thirds of the tonal PEM headline's 1085 s under qemu mps2-an505 (the test harness's deliberate double room simulation was the other third: 772 s with it switched to float). The search now accumulates in Sample. The double profile is bit-for-bit unchanged; the float rows' measured numbers are unchanged to the MSG bisection's 0.5 dB quantum (kalman-loop tonal ASG +7.81 dB before and after, PEM tonal +10.84 -> +11.13, ERLE and misalignment identical). The tonal headline now takes 31 s on the M33 emulator and the full float selection 174 s, so the MUTAP_ON_TARGET_SOFT_FP64 exclusion is removed: the M33 leg runs exactly the M55's selection. docs/wake-word-plan.md carries the M3 record and the corrected M2 finding; HANDOFF.md points at M4. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JuUg1ZBxm3fyBqWgQv6H1G * Repoint the DspTap pin at the merged tap::dsp::nn tree on main tap/DspTap#15 merged by squash; this is the identical tree, reachable after branch cleanup. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JuUg1ZBxm3fyBqWgQv6H1G --------- Co-authored-by: Claude <noreply@anthropic.com>
tap
pushed a commit
to tap/MuTap-Max
that referenced
this pull request
Sep 5, 2026
tap/MuTap#46 (96b4572), which carries DspTap 58210ae (tap/DspTap#15) as its nested pin. The learned post engine mutap.aec~ instantiates is bit-identical on the shipping v2 model in both profiles; the speech predictor's pitch search now accumulates in Sample (double profile unchanged). No source change in the externals. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JuUg1ZBxm3fyBqWgQv6H1G
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
The seventh primitive,
include/tap/dsp/nn.h:basic_dense<Sample>(y = act(W x + b), row-major[out x in], linear / tanh / sigmoid) andbasic_gru<Sample>(Cho et al. 2014 in PyTorch'snn.GRUconvention, gates r, z, n), lifted from MuTap's learned residual suppressor at its wake-word plan's milestone M3. Typed battery intests/test_nn.cpp, README section, primitive count in CLAUDE.md.Why
The keyword spotter (MuTap's
mutap.wake~) is a different head on the same layers the suppressor already runs, and the audit of the plan asked for those layers to be promoted into DspTap after M2 gave them oracles in both profiles, so that "no behaviour change" is testable. That is now the case: MuTap's refactor onto these kernels (its companion PR) is bit-identical in both profiles on the shipping v2 model, its Python parity job passes at the same depths, and its instruction-count ratchet moved by −0.1 to −0.2 % on m55 and m33.The contract is stated as numbers in the header: float32 weight storage converted to
Sampleat the point of use (so the double profile computes what a float64 numpy reference over the same model file computes), bias-first ascending-j accumulation inSamplewith no double arithmetic in the float profile, the GRU formula and gate order, zero state after construction andreset(), and the per-step cost. Weight vectors are moved in and owned, so a copied layer is a deep copy that stays valid;apply()/step()are noexcept and allocation-free. Fixed-point profiles are deliberately not provided: a Q-format design for these layers is a documented per-primitive decision, not a template instantiation.Verification
Built and run here on Linux (GCC 13, Release,
-DTAP_DSP_WERROR=ON): 156 tests, all passing. clang-tidy clean the way CI runs it.Pinned by
tests/test_nn.cpp, measured on this branch:Also pinned: the row-major layout on hand-computed numbers, the three activation forms, the gate order by isolating each block of the
3*hiddenaxis through its biases, reset and deep-copy semantics, and noexcept on the processing path.Not run here, so CI is the first real gate for: Windows MSVC, macOS AppleClang, the Cortex-M55 compile leg, and the drift check. The end-to-end oracle is MuTap's: its suppressor tests, Python parity job and ratchet on the refactored header, in the companion PR.
Notes for the reviewer
tap::dsp::nn::k_contract_version1).mainafter this merges by rebase/squash.🤖 Generated with Claude Code
https://claude.ai/code/session_01JuUg1ZBxm3fyBqWgQv6H1G
Generated by Claude Code