Skip to content

A2 inference has never been compared against NeuralAmpModelerCore, and R-9's retirement rests on an unreproducible figure #37

Description

@ErwanLegrand

A2 loads and runs, and a lot about it is genuinely well tested. What does not exist is any executable comparison of A2 against the reference implementation — the thing FR-NAM-030 names and FR-NAM-150 imports by reference. This is also, specifically, the dimension risk R-9 was raised to protect.

1. No A2 golden exists

crates/namir-nam/tests/golden/ holds exactly five files: input_10s.wav, wavenet_nano.nam + its reference render, and lstm_tiny.nam + its reference render. There is no A2 model and no A2 reference render. wavenet_nano.nam is a pure A1 file — scalar kernel_size: 3, activation: "Tanh", no bottleneck, no kernel_sizes, no nested head, no layer1x1.

2. The comment saying an A2 golden isn't needed is wrong on the facts

crates/namir-nam/tests/golden_reference.rs justifies its plain // trace: FR-NAM-030 with:

"D-9.12 keeps A2 inside the 'WaveNet' architecture this test already covers, so a third variant isn't needed"

D-9.12 is a dispatch decision — A2 files declare architecture: "WaveNet" — not a claim of shared execution. wavenet.rs's own module doc says A2's structural additions are "provably inert when the file is A1", which is precisely why an A1 golden cannot reach them. Each is a distinct branch the A1 fixture takes the other side of:

A2-only path Branch What the A1 golden does instead
per-layer kernel_sizes wavenet.rs:1124 scalar kernel_size
bottleneckchannels wavenet.rs:1138 absent → defaults to channels
nested convolutional head wavenet.rs:1152 kernel=1, dilation=1
per-layer activation array wavenet.rs:1167 single ActivationSpec::One
LeakyReLU/SiLU/Hardswish/Softsign/LeakyHardtanh/PReLU wavenet.rs:139-158 Tanh only — 6 of 10 variants unreached
k-tap causal head conv (head_history/head_padded) wavenet.rs:683-712 hits the hl == 0 fast path; head_padded allocated zero-length

Real A2 is one 23-layer array, kernel sizes 6/15, dilations to 239, LeakyReLU, head kernel 16. The golden pair reaches none of it.

Stated fairly: A1's dilated conv does exercise the general Conv1D path at kernel 3, and A2's head reuses that same code, so the [out][in][k] tap-flatten convention is validated against the real reference for the dilated conv at least.

3. A2's only executable parity is Rust-vs-Rust

a2_fixtures.rs compares namir-nam against namir_fixtures::nam::reference_infer_a2 — a second in-house port. That is the exact position the A1 and LSTM parity tests were in before golden_reference.rs existed, and it is why M9a demoted both to trace-partial:.

Two aggravating details:

  • The probe is deterministic_signal(99, 4_000) — the same ~83 ms sine+noise the demoted tests used, not FR-NAM-030's specified 10-second clean/transient/saturated signal that FR-NAM-150's "to the accuracy of FR-NAM-030" clause imports.
  • The real A2 shape's receptive field is roughly 6 300 samples — longer than the 4 000-sample probe. The comparison never leaves the zero-padded startup transient.

4. R-9's retirement rests on a figure nobody can reproduce, and two documents disagree about it

R-9 is recorded retired partly on "a real NeuralAmpModelerCore render … measured A2 Full/A2 Lite at −90.31 dB each". There is no committed A2 fixture, no reference WAV, no xtask subcommand that can write an A2 fixture for a reference render, and no manual-test write-up (LSTM has one; A2 has none).

And the two records contradict each other. The roadmap attributes −90.31/−90.31 to A2 Full and A2 Lite. golden_reference.rs restates the same measurement — same −90.3 to −90.9 range, same "flat regardless of signal length or complexity", same non-associativity explanation — as "a much larger WaveNet shape (Standard, ten times the layers)", with A2 unmentioned. WaveNetShape::Standard is an A1-only enum; A2 shapes are A2Shape::Full/Lite. Two structurally different models (channels 8 vs 3, different weights, different layer counts) also would not normally report the same figure to two decimal places.

R-9's specific concern was a silently-wrong flat weight order. Bit-exact agreement between two in-house ports proves they derived the same order, not the right one — so the other two legs cannot substitute for the one that is unverifiable.

5. No genuine A2 export has ever been loaded, and both sides share one reading of the C++

Every A2 test input is namir_fixtures::nam::generate_a2. Both the generator and the parser derive the A2 JSON schema from the same reading of the same upstream C++. A shared misreading would be bit-exactly invisible to every test in the tree. Two amplifiers:

  • Every A2 field in file.rs is #[serde(default)] Option<_> and the struct is not deny_unknown_fields, so a real file carrying a feature under an unanticipated key is silently ignored rather than rejected — which undercuts FR-NAM-140's guarantee for real files even though its test is honest.
  • head_dilation absent defaults to 1, so a name mismatch there is silent and wrong rather than loud.

AGENTS.md warns about exactly this class, citing the post-M6 null-vs-omitted parser bug. Note the generated A2 fixtures never emit loudness, input_level_dbu or output_level_dbu — the very A2-era metadata D-9.12 cited as the reason A2 unblocks FR-NAM-090.

6. Real A2 models in the wild take a path Namir has never been compared against

NAM_ENABLE_A2_FAST defaults to ON upstream, and its is_a2_shape detector matches exactly the two shapes FR-NAM-150 names. So a host built with upstream defaults runs a real A2-standard/A2-nano model through a2_fast.cpp, while the one (unrecorded) Namir comparison used -DNAM_ENABLE_A2_FAST=OFF — the generic path.

Choosing the generic path as the definitional reference is defensible. But the choice has no recorded rationale: golden_reference.rs attributes the flag to "D-9.12's PR #264 consequence note", and that note discusses only NAM_USE_INLINE_GEMM and never mentions A2-Fast.

What would close this

  1. A committed A2 golden — a generated A2-Full (and ideally A2-Lite) fixture rendered through the pinned NeuralAmpModelerCore build over the same input_10s.wav, asserted in-process beside the two existing golden tests. This is the single most important missing thing, and it is the same shape of work golden_reference.rs already did twice.
  2. One recorded run against a genuine A2 export, written up the way docs/manual-tests/fr-nam-020-real-lstm-models.md was for LSTM.
  3. Record why the fast path is excluded, or compare against it.
  4. Until (1) exists, R-9 should not read as retired.

Related: the probe for (1) should be long enough to clear A2's ~6 300-sample receptive field.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions