22t: two-factor laws REFUTED by descent (quad Q->0, resrp rp->0 — geometry already in lvl=am/res); real render of sim-optimum 7.6/1.694 = 1.898 with group regressions, canon stays; error budget: dual = 62% of corpus abs-error -> inter-band acc/f6f8 consumer is priority #1
This commit is contained in:
@@ -245,6 +245,17 @@ static void process_band_structural(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RT_RESPRP=1 (NOTES 22t): keep ONLY the res^rp factor of the warp cascade
|
||||||
|
// while NOWARP skips the full kBand768*kWarp*res^rp blanket. Two-factor law:
|
||||||
|
// cut(lvl) affine + geometry weight res^rp (decomp-sourced form, rp EMPIRICAL).
|
||||||
|
static const int resrp_only = getenv("RT_RESPRP") ? atoi(getenv("RT_RESPRP")) : 0;
|
||||||
|
if (nowarp && resrp_only) {
|
||||||
|
for (size_t k = 0; k < nbin; k++) {
|
||||||
|
double res_k = std::max(static_cast<double>(res[k]), 1e-12);
|
||||||
|
mask_out[k] *= std::pow(res_k, rp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Step 9 (NOTES_LEVEL:830 + consumers_out.txt:955-1075): IIR3 inline,
|
// Step 9 (NOTES_LEVEL:830 + consumers_out.txt:955-1075): IIR3 inline,
|
||||||
// TWO bidirectional passes [reset, forward, backward] x2 (state persists
|
// TWO bidirectional passes [reset, forward, backward] x2 (state persists
|
||||||
// from forward into backward within a pair; reset between pairs).
|
// from forward into backward within a pair; reset between pairs).
|
||||||
@@ -330,6 +341,11 @@ void FramedDetector::setParams(const std::vector<DetectorBand>& bands) {
|
|||||||
res_.clear();
|
res_.clear();
|
||||||
track_.clear();
|
track_.clear();
|
||||||
|
|
||||||
|
// RT_DUMPRESPATH=<file> (NOTES 22t): static twin-response spectra per band,
|
||||||
|
// binary {int32 band, int32 nbin, float res[nbin]} records (append).
|
||||||
|
FILE* rp_dump = nullptr;
|
||||||
|
if (const char* dp = getenv("RT_DUMPRESPATH")) rp_dump = fopen(dp, "ab");
|
||||||
|
|
||||||
for (const auto& b : bands_) {
|
for (const auto& b : bands_) {
|
||||||
std::vector<float> r(half + 1, 1.0f);
|
std::vector<float> r(half + 1, 1.0f);
|
||||||
float sens_lin = std::pow(10.0f, b.sens * SENS_SCALE / 20.0f);
|
float sens_lin = std::pow(10.0f, b.sens * SENS_SCALE / 20.0f);
|
||||||
@@ -348,8 +364,16 @@ void FramedDetector::setParams(const std::vector<DetectorBand>& bands) {
|
|||||||
r[k] = std::sqrt(out[k].re * out[k].re + out[k].im * out[k].im);
|
r[k] = std::sqrt(out[k].re * out[k].re + out[k].im * out[k].im);
|
||||||
r[k] = std::max(r[k], 1e-12f);
|
r[k] = std::max(r[k], 1e-12f);
|
||||||
}
|
}
|
||||||
|
if (rp_dump) {
|
||||||
|
int32_t bi = static_cast<int32_t>(res_.size());
|
||||||
|
int32_t nb = static_cast<int32_t>(r.size());
|
||||||
|
fwrite(&bi, sizeof(int32_t), 1, rp_dump);
|
||||||
|
fwrite(&nb, sizeof(int32_t), 1, rp_dump);
|
||||||
|
fwrite(r.data(), sizeof(float), r.size(), rp_dump);
|
||||||
|
}
|
||||||
res_.push_back(std::move(r));
|
res_.push_back(std::move(r));
|
||||||
}
|
}
|
||||||
|
if (rp_dump) fclose(rp_dump);
|
||||||
track_.assign(bands_.size(), std::vector<float>(half + 1, 1.0f));
|
track_.assign(bands_.size(), std::vector<float>(half + 1, 1.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2016,3 +2016,45 @@ x_i=−dA*/dS — точная эффективная координата ке
|
|||||||
не решается, подтверждено покейсовой бисекцией.
|
не решается, подтверждено покейсовой бисекцией.
|
||||||
3. Реплей-инфраструктура готова для быстрого перебора ЛЮБЫХ параметрических законов
|
3. Реплей-инфраструктура готова для быстрого перебора ЛЮБЫХ параметрических законов
|
||||||
(2 c корпус) — использовать вместо реальных рендеров до финальной валидации.
|
(2 c корпус) — использовать вместо реальных рендеров до финальной валидации.
|
||||||
|
|
||||||
|
## ============ UPDATE 2026-08-23b (22t): ДВУХФАКТОРНЫЕ ЗАКОНЫ ОТВЕРГНУТЫ — БЮДЖЕТ ОШИБКИ В DUAL ============
|
||||||
|
|
||||||
|
Инструменты расширены:
|
||||||
|
- `RT_DUMPRESPATH=<file>` в FramedDetector::setParams — статические спектры twin-response
|
||||||
|
res[band][bin] ({i32 band,i32 nbin,f32[]}, append); lawfit collect кладёт их в npz рядом с lvl.
|
||||||
|
- `RT_RESPRP=1` в process_band_structural — применять res^rp при NOWARP (для реальных
|
||||||
|
рендеров двухфакторного закона).
|
||||||
|
- `lawfit22r.py fit`: координатный спуск по семействам законов на реплее (2 c/корпус).
|
||||||
|
|
||||||
|
### Результат фит-семейств (офлайн, старт 7.6/1.6)
|
||||||
|
| семейство | A | S | Q | rp | TOTAL(sim) |
|
||||||
|
|-----------|---|---|---|----|-----------|
|
||||||
|
| scalar | 7.600 | 1.694 | 0 | 0 | **1.860** |
|
||||||
|
| quad (Q·x²) | →scalar | | **→0** | | 1.860 |
|
||||||
|
| resrp (mask·res^rp) | →scalar | | | **→0** | 1.860 |
|
||||||
|
| quad+resrp | 7.452 | 1.540 | +0.069 | 0 | 1.865 |
|
||||||
|
|
||||||
|
ОБА вторых фактора отброшены спуском: кривизна не нужна, res-вес не нужен.
|
||||||
|
Объяснение resrp: lvl = am/res УЖЕ содержит геометрию твин-юбки; домножение на
|
||||||
|
res^rp сверху — двойной учёт (в каноне res^rp компенсировал другое — отсутствие
|
||||||
|
res в xv-домене LUT).
|
||||||
|
|
||||||
|
### Реальный рендер скалярного оптимума (7.6, 1.694)
|
||||||
|
t1kq 0.954 / t1k 0.842 / al 0.799 / res **0.117** / dual **3.246** / comb 4.855
|
||||||
|
⇒ TOTAL **1.898** (sim 1.860 + стат. смещения реплея ~0.04). Против HEAD 1.931:
|
||||||
|
выигрыш мизерный, t1kq/t1k/comb РЕГРЕССИРУЮТ ⇒ гейт не пройден, канон НЕ тронут.
|
||||||
|
|
||||||
|
### Бюджет ошибки — где лежит остаток (решающий факт)
|
||||||
|
abs-сумма по корпусу при (7.6,1.694): 115.4 дБ·кейсов, из них:
|
||||||
|
- **dual: 22 кейса × 3.246 = 71.4 (62%!)** — межполосный механизм (Шаг 2/9, консюмер acc/f6f8);
|
||||||
|
- comb: 24.3 (21%) — мультиполосное взаимодействие;
|
||||||
|
- всё прочее: ~20 (17%).
|
||||||
|
Если бы dual был на уровне bridge (0.726), TOTAL упал бы до ~0.96. Никакая
|
||||||
|
донастройка закона это не даст — ПРИОРИТЕТ №1 подтверждён количественно:
|
||||||
|
искать точку потребления acc/f6f8 (межполосный каскад), а не крутить маску.
|
||||||
|
|
||||||
|
### NEXT
|
||||||
|
1. dual/межполосный консюмер: декомп-охота вокруг FUN_180529fe0 callers
|
||||||
|
(52e260 render-loop, multiband combine), трассировка acc/f6f8 в живом плагине.
|
||||||
|
2. comb: после dual — пере-тест 4-полосного взаимодействия.
|
||||||
|
3. Реплей-инфра готова; для любых новых гипотез сначала офлайн (2 c), потом рендер.
|
||||||
|
|||||||
+107
-53
@@ -91,10 +91,14 @@ def collect():
|
|||||||
|
|
||||||
store = {}
|
store = {}
|
||||||
traj_bin = '/tmp/opencode/lawfit_traj.bin'
|
traj_bin = '/tmp/opencode/lawfit_traj.bin'
|
||||||
|
resp_bin = '/tmp/opencode/lawfit_res.bin'
|
||||||
for key, u in sorted(uniq.items()):
|
for key, u in sorted(uniq.items()):
|
||||||
if os.path.exists(traj_bin):
|
if os.path.exists(traj_bin):
|
||||||
os.remove(traj_bin)
|
os.remove(traj_bin)
|
||||||
env = {**os.environ, **BASE_ENV, 'RT_DUMP_ALL': traj_bin}
|
if os.path.exists(resp_bin):
|
||||||
|
os.remove(resp_bin)
|
||||||
|
env = {**os.environ, **BASE_ENV, 'RT_DUMP_ALL': traj_bin,
|
||||||
|
'RT_DUMPRESPATH': resp_bin}
|
||||||
subprocess.run([corpus.RB, u['inp'], '/tmp/opencode/lawfit_out.wav'] + u['args'],
|
subprocess.run([corpus.RB, u['inp'], '/tmp/opencode/lawfit_out.wav'] + u['args'],
|
||||||
capture_output=True, text=True, env=env,
|
capture_output=True, text=True, env=env,
|
||||||
cwd='/home/m/re-tools')
|
cwd='/home/m/re-tools')
|
||||||
@@ -104,9 +108,26 @@ def collect():
|
|||||||
continue
|
continue
|
||||||
lvl = arr.reshape(-1, u['nb'], RBIN) # [frame][band][bin]
|
lvl = arr.reshape(-1, u['nb'], RBIN) # [frame][band][bin]
|
||||||
store[key] = lvl
|
store[key] = lvl
|
||||||
print(f'{key}: {lvl.shape[0]} frames x {u["nb"]} bands')
|
# static twin-response spectra per band ({i32 band, i32 nbin, f32[nbin]})
|
||||||
|
res = None
|
||||||
|
if os.path.exists(resp_bin):
|
||||||
|
raw = open(resp_bin, 'rb').read()
|
||||||
|
off = 0
|
||||||
|
bands = []
|
||||||
|
while off < len(raw):
|
||||||
|
bi, nb = struct.unpack_from('<ii', raw, off)
|
||||||
|
off += 8
|
||||||
|
r = np.frombuffer(raw, dtype='<f4', count=nb, offset=off)
|
||||||
|
off += 4 * nb
|
||||||
|
bands.append(r.astype(np.float32))
|
||||||
|
if len(bands) == u['nb'] and all(b.size == RBIN for b in bands):
|
||||||
|
res = np.stack(bands)
|
||||||
|
if res is not None:
|
||||||
|
store[key + '|res'] = res
|
||||||
|
print(f'{key}: {lvl.shape[0]} frames x {u["nb"]} bands'
|
||||||
|
+ (' +res' if res is not None else ' NORES'))
|
||||||
np.savez_compressed(TRAJ, **store)
|
np.savez_compressed(TRAJ, **store)
|
||||||
print(f'\nwrote {len(store)} trajectories -> {TRAJ}')
|
print(f'\nwrote {len(store)} entries -> {TRAJ}')
|
||||||
|
|
||||||
|
|
||||||
def load_traj():
|
def load_traj():
|
||||||
@@ -135,16 +156,24 @@ class CaseInput:
|
|||||||
return lvl.shape[0] == self.nf
|
return lvl.shape[0] == self.nf
|
||||||
|
|
||||||
|
|
||||||
def masks_from_lvl(lvl, nb, A, S):
|
def masks_from_lvl(lvl, nb, A, S, Q=0.0, res=None, rp=0.0):
|
||||||
"""Per-frame lower-half mask exactly as bare chain + LAWAFFINE produces."""
|
"""Per-frame lower-half mask exactly as bare chain + LAWAFFINE produces.
|
||||||
|
|
||||||
|
Law families (22t): scalar cut=A+S*log2(lvl); quad adds curvature Q*x^2;
|
||||||
|
resrp multiplies per-band mask by res^rp (decomp warp-cascade factor,
|
||||||
|
applied BEFORE cross-band min like the C++ warp section).
|
||||||
|
"""
|
||||||
nf = lvl.shape[0]
|
nf = lvl.shape[0]
|
||||||
lv64 = lvl.astype(np.float64)
|
lv64 = lvl.astype(np.float64)
|
||||||
mm = np.exp2(-((A + S * np.log2(np.maximum(lv64, 1e-12))) / 6.0174))
|
x = np.log2(np.maximum(lv64, 1e-12))
|
||||||
|
mm = np.exp2(-((A + S * x + Q * x * x) / 6.0174))
|
||||||
low = lv64 <= 1e-6 # C++ fallback: exp2(-level)
|
low = lv64 <= 1e-6 # C++ fallback: exp2(-level)
|
||||||
if low.any():
|
if low.any():
|
||||||
mm[low] = np.exp2(-lv64[low])
|
mm[low] = np.exp2(-lv64[low])
|
||||||
|
if res is not None and rp:
|
||||||
|
mm = mm * res[None].astype(np.float64) ** rp
|
||||||
if nb > 1:
|
if nb > 1:
|
||||||
lo = np.min(mm, axis=1)
|
lo = np.min(mm, axis=1) # min across bands
|
||||||
else:
|
else:
|
||||||
lo = mm[:, 0, :]
|
lo = mm[:, 0, :]
|
||||||
full = np.ones((nf, NFFT))
|
full = np.ones((nf, NFFT))
|
||||||
@@ -153,10 +182,10 @@ def masks_from_lvl(lvl, nb, A, S):
|
|||||||
return full
|
return full
|
||||||
|
|
||||||
|
|
||||||
def replay(ci, lvl, nb, A, S):
|
def replay(ci, lvl, nb, A, S, Q=0.0, res=None, rp=0.0):
|
||||||
"""Return trimmed 44.1k output for law (A,S) on prepared CaseInput ci."""
|
"""Return trimmed 44.1k output for law params on prepared CaseInput ci."""
|
||||||
assert ci.frames_ok(lvl), f'traj {lvl.shape[0]} != frames {ci.nf}'
|
assert ci.frames_ok(lvl), f'traj {lvl.shape[0]} != frames {ci.nf}'
|
||||||
full = masks_from_lvl(lvl, nb, A, S)
|
full = masks_from_lvl(lvl, nb, A, S, Q, res, rp)
|
||||||
segs = ci.xext[ci.offs[:, None] + np.arange(NFFT)[None, :]] * _WIN[None, :]
|
segs = ci.xext[ci.offs[:, None] + np.arange(NFFT)[None, :]] * _WIN[None, :]
|
||||||
yspec = np.fft.fft(segs, axis=1) * full
|
yspec = np.fft.fft(segs, axis=1) * full
|
||||||
td = np.real(np.fft.ifft(yspec, axis=1))
|
td = np.real(np.fft.ifft(yspec, axis=1))
|
||||||
@@ -203,20 +232,18 @@ def build_eval_index(trajs):
|
|||||||
return idx
|
return idx
|
||||||
|
|
||||||
|
|
||||||
def sim_errors(trajs, idx, A, S, ref_cache=None):
|
def sim_errors(trajs, idx, A, S, Q=0.0, rp=0.0):
|
||||||
errs = {}
|
errs = {}
|
||||||
ycache = {}
|
ycache = {}
|
||||||
for e in idx:
|
for e in idx:
|
||||||
ck = (e['key'], e['nb'])
|
ck = (e['key'], e['nb'], round(Q, 6), round(rp, 6))
|
||||||
if ck not in ycache:
|
if ck not in ycache:
|
||||||
ci = case_input(e['key'], e['inp'])
|
ci = case_input(e['key'], e['inp'])
|
||||||
ycache[ck] = replay(ci, trajs[e['key']], e['nb'], A, S)
|
ycache[ck] = replay(ci, trajs[e['key']], e['nb'], A, S,
|
||||||
|
Q, trajs.get(e['key'] + '|res'), rp)
|
||||||
y44 = ycache[ck]
|
y44 = ycache[ck]
|
||||||
if ref_cache is None:
|
errs[e['name']] = corpus.db(corpus.ta(y44, e['f'])) - \
|
||||||
errs[e['name']] = corpus.db(corpus.ta(y44, e['f'])) - \
|
corpus.db(corpus.ta(corpus.load_mono(e['ref']), e['f']))
|
||||||
corpus.db(corpus.ta(corpus.load_mono(e['ref']), e['f']))
|
|
||||||
else:
|
|
||||||
errs[e['name']] = corpus.db(corpus.ta(y44, e['f'])) - ref_cache[e['name']]
|
|
||||||
return errs
|
return errs
|
||||||
|
|
||||||
|
|
||||||
@@ -250,45 +277,72 @@ def sanity(A, S, json_path):
|
|||||||
def fit(A0, S0):
|
def fit(A0, S0):
|
||||||
trajs = load_traj()
|
trajs = load_traj()
|
||||||
idx = build_eval_index(trajs)
|
idx = build_eval_index(trajs)
|
||||||
|
fit2(trajs, idx, A0, S0)
|
||||||
|
|
||||||
def objective(A, S, sub=None):
|
|
||||||
|
def fit2(trajs, idx, A0, S0):
|
||||||
|
"""Law-family comparison: scalar / quad / resrp / quad+resrp (NOTES 22t)."""
|
||||||
|
|
||||||
|
def objective(p, sub=None):
|
||||||
|
A, S, Q, rp = p
|
||||||
ii = idx if sub is None else sub
|
ii = idx if sub is None else sub
|
||||||
return group_stats(sim_errors(trajs, ii, A, S))
|
return group_stats(sim_errors(trajs, ii, A, S, Q, rp))
|
||||||
|
|
||||||
best = (A0, S0)
|
def descend(p0, steps, sub=None, label=''):
|
||||||
bst = objective(*best)
|
best = list(p0)
|
||||||
print(f'start A={A0} S={S0}: TOTAL={bst["TOTAL"]:.3f}')
|
bst = objective(tuple(best), sub)
|
||||||
stepA, stepS = 0.8, 0.25
|
print(f'{label} start {[round(v, 4) for v in best]}: '
|
||||||
for it in range(4):
|
f'TOTAL={bst["TOTAL"]:.3f}', flush=True)
|
||||||
moved = False
|
while all(s > 1e-4 for s in steps.values()):
|
||||||
for A, S in [(best[0] + stepA, best[1]), (best[0] - stepA, best[1]),
|
moved = False
|
||||||
(best[0], best[1] + stepS), (best[0], best[1] - stepS)]:
|
for i, nm in enumerate(['A', 'S', 'Q', 'rp']):
|
||||||
st = objective(A, S)
|
if nm not in steps:
|
||||||
mark = ''
|
continue
|
||||||
if st['TOTAL'] < bst['TOTAL'] - 1e-4:
|
st = steps[nm]
|
||||||
best, bst = (A, S), st
|
for d in (+st, -st):
|
||||||
moved = True
|
cand = list(best)
|
||||||
mark = ' *'
|
cand[i] = round(cand[i] + d, 6)
|
||||||
print(f' [{it}] A={A:+7.3f} S={S:+6.3f}: TOTAL={st["TOTAL"]:.3f}{mark}')
|
if cand[3] < 0 or (nm == 'Q' and abs(cand[2]) > 3):
|
||||||
if not moved:
|
continue
|
||||||
stepA /= 2
|
s2 = objective(tuple(cand), sub)
|
||||||
stepS /= 2
|
if s2['TOTAL'] < bst['TOTAL'] - 1e-4:
|
||||||
if stepA < 0.05:
|
best, bst = cand, s2
|
||||||
break
|
moved = True
|
||||||
print(f'\nBEST global: A={best[0]:.3f} S={best[1]:.3f} TOTAL={bst["TOTAL"]:.3f}')
|
print(f' {label} {nm}{d:+.4g}: TOTAL={s2["TOTAL"]:.3f} '
|
||||||
for k, v in bst.items():
|
f'{[round(v, 4) for v in best]}', flush=True)
|
||||||
print(f' {k:>6}: {v:.3f}')
|
if not moved:
|
||||||
|
for nm in steps:
|
||||||
|
steps[nm] /= 2
|
||||||
|
return best, bst
|
||||||
|
|
||||||
print('\n=== per-group greedy optima (grid around global best) ===')
|
res_all = {}
|
||||||
for g in ['t1kq', 't1k', 'al', 'res', 'dual', 'comb']:
|
|
||||||
sub = [e for e in idx if e['grp'] == g]
|
b, t = descend([A0, S0, 0.0, 0.0], {'A': 0.4, 'S': 0.15}, label='[scalar]')
|
||||||
bA, bS, bval = None, None, 1e9
|
res_all['scalar'] = (list(b), dict(t))
|
||||||
for A in np.arange(best[0] - 2.5, best[0] + 2.51, 0.5):
|
|
||||||
for S in np.arange(max(0.25, best[1] - 1.0), best[1] + 1.01, 0.25):
|
bA, bS, _, _ = res_all['scalar'][0]
|
||||||
st = objective(float(A), float(S), sub)
|
b, t = descend([bA, bS, 0.0, 0.0], {'A': 0.3, 'S': 0.15, 'Q': 0.06},
|
||||||
if st[g] < bval:
|
label='[quad]')
|
||||||
bA, bS, bval = float(A), float(S), st[g]
|
res_all['quad'] = (list(b), dict(t))
|
||||||
print(f'{g:>6}: A={bA:6.2f} S={bS:5.2f} mean|e|={bval:.3f}', flush=True)
|
|
||||||
|
b, t = descend([bA, bS, 0.0, 0.03], {'A': 0.3, 'S': 0.15, 'rp': 0.01},
|
||||||
|
label='[resrp]')
|
||||||
|
res_all['resrp'] = (list(b), dict(t))
|
||||||
|
|
||||||
|
bA, bS, bQ, _ = res_all['quad'][0]
|
||||||
|
b, t = descend([bA, bS, bQ, 0.03], {'A': 0.25, 'S': 0.12, 'Q': 0.05,
|
||||||
|
'rp': 0.008}, label='[quad+resrp]')
|
||||||
|
res_all['quad+resrp'] = (list(b), dict(t))
|
||||||
|
|
||||||
|
print('\n================ LAW FAMILY SUMMARY ================')
|
||||||
|
for fam, (p, st) in res_all.items():
|
||||||
|
gs = ' '.join(f'{g}={st[g]:.3f}' for g in
|
||||||
|
['t1kq', 't1k', 'al', 'res', 'dual', 'comb'])
|
||||||
|
print(f'{fam:>12}: A={p[0]:7.3f} S={p[1]:6.3f} Q={p[2]:+6.3f} '
|
||||||
|
f'rp={p[3]:5.3f} TOTAL={st["TOTAL"]:.3f}\n{"":>14}{gs}')
|
||||||
|
json.dump({f: {'params': p, 'groups': s} for f, (p, s) in res_all.items()},
|
||||||
|
open('/tmp/opencode/lawfit_fit2.json', 'w'), indent=1)
|
||||||
|
print('\nwrote /tmp/opencode/lawfit_fit2.json')
|
||||||
|
|
||||||
|
|
||||||
def freq_of(name):
|
def freq_of(name):
|
||||||
|
|||||||
Reference in New Issue
Block a user