Files
soothe2-re/dsp/fn529fe0_check.cpp
T
Matiq b41d7a40cb chain: wire IIR4 generator (RT_IIR4_GEN) + add chain smoke test
- iir4_bidir: env-gated generate_iir4_coefs (C/tau/p/mult/sr) vs proxy kIIR
- fix exp sign: up=exp(-c*g*tau/mult) → down=1-up in 0..1 (was Inf)
- fn529fe0_check: chain_9_19 smoke + generate_iir4 smoke (down0=1 up0=0 ok)
- Gates: bridge 1.594, structural 2.689 (RT_CASC=0) unchanged
2026-09-02 21:38:44 +03:00

221 lines
9.9 KiB
C++

#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include "fn529fe0.hpp"
#include "rt_mask_tables.hpp"
#include "rt_weights.hpp"
// Modular black-box check for the structural FUN_180529fe0 chain components
// (BITEXACT_PLAN step 1). Validates invariants against the live tables:
// - kIIR_A1/B1 : B == 1 - A, and IIR1 smooths a step input monotonically
// - blend_exp2 : out == exp2(-x)*blend, blend = freqaxis*(1-mix)+mix*0.8
// - combine_acc: subtract then add band/f6f8 contributions (exact)
// - warp_mask : multiplies by kBand768*kWarp
// - cascade : Haar, magnitudes, blend (529c60 decode)
int main() {
const size_t nbin = 2049; // internal N/2+1 grid used by the chain
const size_t nfft = 4096;
int fail = 0;
// --- IIR tables: B1 == 1 - A1 ---
double maxB = 0.0;
for (size_t i = 0; i < nbin; i++)
maxB = std::fmax(maxB, std::fabs(kIIR_B1[i] - (1.0 - kIIR_A1[i])));
std::printf("IIR: max|B1-(1-A1)| = %.3e (%s)\n", maxB, maxB < 1e-12 ? "OK" : "MISMATCH");
if (maxB >= 1e-12) fail = 1;
// --- IIR1 smooths a step input monotonically ---
std::vector<float> x(nbin);
std::vector<double> acc1(nbin);
for (size_t i = 0; i < nbin; i++) x[i] = (i < 100 ? 0.0f : 1.0f);
std::vector<float> orig = x;
fn529fe0::iir1(x.data(), kIIR_A1, kIIR_B1, nbin, 0.0);
bool monotonic = true;
for (size_t i = 1; i < nbin; i++)
if (x[i] < x[i - 1] - 1e-6) { monotonic = false; break; }
std::printf("IIR1 step: monotonic=%d x[0]=%.3f x[mid]=%.3f x[last]=%.3f\n",
monotonic, x[0], x[nbin/2], x[nbin-1]);
if (!monotonic || std::fabs(x[0] - 0.0f) > 1e-3) fail = 1;
// --- blend_exp2 correctness ---
std::vector<float> mask(nbin), lvl(nbin), freq(nbin);
for (size_t i = 0; i < nbin; i++) { lvl[i] = 0.5f * (1.0f + float(i) / nbin); freq[i] = 1.0f; }
const float mix = 1.0f;
fn529fe0::blend_exp2(mask.data(), lvl.data(), freq.data(), mix, nbin);
double max_e = 0.0;
for (size_t i = 0; i < nbin; i++) {
double expect = std::exp2(-(double)lvl[i]) * 0.8;
max_e = std::fmax(max_e, std::fabs(mask[i] - expect));
}
std::printf("blend_exp2: max|out-exp2(-x)*0.8| = %.3e (%s)\n",
max_e, max_e < 1e-6 ? "OK" : "MISMATCH");
if (max_e >= 1e-6) fail = 1;
// --- combine_acc: acc = band-f6f8 + wAtt*f6f8 + wRel*f6f8 + band.
// With band=1, f6f8=0, weights=0: acc = band - 0 + 0 + 0 + band = 2 everywhere. ---
std::vector<double> acc(nfft, 0.0);
std::vector<float> band(nbin, 1.0f), f6f8(nbin, 0.0f), wA(nbin, 0.0f), wR(nbin, 0.0f);
fn529fe0::combine_acc(acc.data(), band.data(), f6f8.data(), wA.data(), wR.data(), nfft);
double max_c = 0.0;
for (size_t i = 0; i < nfft; i++) max_c = std::fmax(max_c, std::fabs(acc[i] - 2.0));
std::printf("combine: acc=2 for band=1,f6f8=0,w=0 max|d|=%.3e (%s)\n",
max_c, max_c < 1e-12 ? "OK" : "MISMATCH");
if (max_c >= 1e-12) fail = 1;
// --- warp_mask applies kBand768*kWarp ---
std::vector<float> w(nbin);
for (size_t i = 0; i < nbin; i++) w[i] = 1.0f;
const float* k768 = kBand768; // band0 table (per-band in real path)
fn529fe0::warp_mask(w.data(), k768, kWarp, nbin);
double max_w = 0.0;
for (size_t i = 0; i < nbin; i++)
max_w = std::fmax(max_w, std::fabs(w[i] - k768[i] * kWarp[i]));
std::printf("warp: mask==kBand768*kWarp max|d|=%.3e (%s)\n",
max_w, max_w < 1e-6 ? "OK" : "MISMATCH");
if (max_w >= 1e-6) fail = 1;
// --- live table ranges ---
std::printf("live: kWarp[0]=%.3f kWarp[2048]=%.3f kBand768[0]=%.3f kBand768[2048]=%.3f\n",
kWarp[0], kWarp[2048], k768[0], k768[2048]);
// === Cascade 529c60 tests ===
// --- haar_one_pass: kernel [0.25, 0.5, 0.25] ---
{
// Input: [1, 3, 5, 7, 9] (5 elements)
// Expected: b[0]=0.5*(1+3)=2.0; b[1]=0.25*1+0.5*3+0.25*5=3.0;
// b[2]=0.25*3+0.5*5+0.25*7=5.0; b[3]=0.25*5+0.5*7+0.25*9=7.0;
// b[4]=0.25*7+0.75*9=8.5 (boundary)
float data[] = {1.0f, 3.0f, 5.0f, 7.0f, 9.0f};
float expected[] = {2.0f, 3.0f, 5.0f, 7.0f, 8.0f};
fn529fe0::haar_one_pass(data, 5);
double max_h = 0.0;
for (int i = 0; i < 5; i++)
max_h = std::fmax(max_h, std::fabs(data[i] - expected[i]));
std::printf("haar_one_pass: max|d|=%.3e (%s)\n", max_h,
max_h < 1e-6 ? "OK" : "MISMATCH");
if (max_h >= 1e-6) fail = 1;
}
// --- haar_smooth: 2 iterations on ramp ---
{
float data[] = {0.0f, 0.25f, 0.5f, 0.75f, 1.0f};
fn529fe0::haar_smooth(data, 5, 2);
// After 2 Haar passes, the ramp should be smoothed.
// Just check monotonicity and bounds [0, 1].
bool ok = true;
for (int i = 0; i < 5; i++) {
if (data[i] < -0.01f || data[i] > 1.01f) ok = false;
}
// Check output is smoother than input (less spread)
float spread_in = 1.0f - 0.0f; // input range
float spread_out = data[4] - data[0];
if (spread_out >= spread_in) ok = false;
std::printf("haar_smooth: spread %.3f→%.3f (%s)\n",
spread_in, spread_out, ok ? "OK" : "MISMATCH");
if (!ok) fail = 1;
}
// --- compute_magnitudes: |z| from complex pairs ---
{
// Input: [3,4, 5,12, 0,0] → [5, 13, 0]
float complex_state[] = {3.0f, 4.0f, 5.0f, 12.0f, 0.0f, 0.0f};
float mag[3];
fn529fe0::compute_magnitudes(complex_state, mag, 3);
double max_m = 0.0;
max_m = std::fmax(max_m, std::fabs(mag[0] - 5.0f));
max_m = std::fmax(max_m, std::fabs(mag[1] - 13.0f));
max_m = std::fmax(max_m, std::fabs(mag[2] - 0.0f));
std::printf("compute_magnitudes: max|d|=%.3e (%s)\n", max_m,
max_m < 1e-5 ? "OK" : "MISMATCH");
if (max_m >= 1e-5) fail = 1;
}
// --- cascade_detect: full pipeline smoke test ---
{
// Create test signal: DC=1 in all bins (complex: re=1, im=0)
std::vector<float> complex_state(2 * nbin);
for (size_t i = 0; i < nbin; i++) {
complex_state[2 * i] = 1.0f; // re
complex_state[2 * i + 1] = 0.0f; // im
}
std::vector<float> bands_curve(nbin, 0.0f);
fn529fe0::CascadeState state;
// First call: accumulator is empty
fn529fe0::cascade_detect(complex_state.data(), bands_curve.data(),
state, nbin, 2,
0.0f, // sin_peak_param=0 (disabled)
10.0f, // ctx24
1, // ctx1a0
4); // ctx1ac
// All magnitudes are 1.0, Haar-smoothed should be ~1.0
// Peak should be ~1.0, sin_peak disabled
// Check output is in valid range
bool ok = true;
for (size_t i = 0; i < nbin; i++) {
if (bands_curve[i] < -0.01f || bands_curve[i] > 2.0f) ok = false;
}
std::printf("cascade_detect DC: [0]=%.4f [mid]=%.4f [end]=%.4f (%s)\n",
bands_curve[0], bands_curve[nbin/2], bands_curve[nbin-1],
ok ? "OK" : "MISMATCH");
if (!ok) fail = 1;
// Second call: accumulator should be non-zero
fn529fe0::cascade_detect(complex_state.data(), bands_curve.data(),
state, nbin, 2, 0.0f, 10.0f, 1, 4);
std::printf("cascade_detect DC 2nd: acc[0]=%.6f out[0]=%.4f\n",
state.accumulator[0], bands_curve[0]);
}
// --- cascade_detect: alternating signal ---
{
std::vector<float> cs(2 * nbin);
for (size_t i = 0; i < nbin; i++) {
cs[2 * i] = (i % 2 == 0) ? 2.0f : 0.5f;
cs[2 * i + 1] = 0.0f;
}
std::vector<float> bc(nbin, 0.0f);
fn529fe0::CascadeState st;
fn529fe0::cascade_detect(cs.data(), bc.data(), st, nbin, 2,
0.0f, 10.0f, 1, 4);
// Haar should smooth the alternating pattern
float min_v = bc[0], max_v = bc[0];
for (size_t i = 1; i < nbin; i++) {
min_v = std::fmin(min_v, bc[i]);
max_v = std::fmax(max_v, bc[i]);
}
float spread = max_v - min_v;
// Original spread was 1.5, after 2 Haar passes should be much smaller
bool ok = spread < 0.5f;
std::printf("cascade_detect alt: spread=%.4f [0]=%.4f [1]=%.4f (%s)\n",
spread, bc[0], bc[1], ok ? "OK" : "MISMATCH");
if (!ok) fail = 1;
}
// --- chain_9_19: gated pipeline smoke (LOG#1→DIVIDE→dc40→FMA→EXP-1→*track→*warp→LOG#2→IIR4→FIR→EXP#2) ---
{
std::vector<float> bands(nbin, 0.5f), tmp(nbin, 0.1f), acc(nbin, 0.0f);
std::vector<float> warp(nbin, 1.0f), att(nbin, 0.0f), rel(nbin, 0.0f);
std::vector<float> bands0 = bands;
fn529fe0::chain_9_19(bands.data(), tmp.data(), acc.data(), nullptr, warp.data(), att.data(), rel.data(), nbin);
bool ok = true;
for (size_t i = 0; i < nbin; i++) if (!std::isfinite(bands[i]) || bands[i] < 0.0f || bands[i] > 5.0f) ok = false;
std::printf("chain_9_19 smoke: in0=%.3f out0=%.3f outmid=%.3f finite=%d (%s)\n",
bands0[0], bands[0], bands[nbin/2], ok, ok ? "OK" : "MISMATCH");
if (!ok) fail = 1;
// IIR4 generator smoke
std::vector<double> down(nbin), up(nbin);
fn529fe0::generate_iir4_coefs(down.data(), up.data(), (int)nbin, 1000.0, 1200.0, 48000.0, 0.5, 360.0);
bool gok = std::fabs(down[0]-1.0)<1e-9 && std::fabs(up[0])<1e-9 && down[1] < 1.0 && down[1] > 0.0;
std::printf("generate_iir4: down0=%.3f up0=%.3f down1=%.4f up1=%.4f (%s)\n",
down[0], up[0], down[1], up[1], gok ? "OK" : "MISMATCH");
if (!gok) fail = 1;
}
std::printf("fn529fe0 check %s\n", fail ? "FAIL" : "PASS");
return fail;
}