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:
+107
-53
@@ -91,10 +91,14 @@ def collect():
|
||||
|
||||
store = {}
|
||||
traj_bin = '/tmp/opencode/lawfit_traj.bin'
|
||||
resp_bin = '/tmp/opencode/lawfit_res.bin'
|
||||
for key, u in sorted(uniq.items()):
|
||||
if os.path.exists(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'],
|
||||
capture_output=True, text=True, env=env,
|
||||
cwd='/home/m/re-tools')
|
||||
@@ -104,9 +108,26 @@ def collect():
|
||||
continue
|
||||
lvl = arr.reshape(-1, u['nb'], RBIN) # [frame][band][bin]
|
||||
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)
|
||||
print(f'\nwrote {len(store)} trajectories -> {TRAJ}')
|
||||
print(f'\nwrote {len(store)} entries -> {TRAJ}')
|
||||
|
||||
|
||||
def load_traj():
|
||||
@@ -135,16 +156,24 @@ class CaseInput:
|
||||
return lvl.shape[0] == self.nf
|
||||
|
||||
|
||||
def masks_from_lvl(lvl, nb, A, S):
|
||||
"""Per-frame lower-half mask exactly as bare chain + LAWAFFINE produces."""
|
||||
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.
|
||||
|
||||
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]
|
||||
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)
|
||||
if low.any():
|
||||
mm[low] = np.exp2(-lv64[low])
|
||||
if res is not None and rp:
|
||||
mm = mm * res[None].astype(np.float64) ** rp
|
||||
if nb > 1:
|
||||
lo = np.min(mm, axis=1)
|
||||
lo = np.min(mm, axis=1) # min across bands
|
||||
else:
|
||||
lo = mm[:, 0, :]
|
||||
full = np.ones((nf, NFFT))
|
||||
@@ -153,10 +182,10 @@ def masks_from_lvl(lvl, nb, A, S):
|
||||
return full
|
||||
|
||||
|
||||
def replay(ci, lvl, nb, A, S):
|
||||
"""Return trimmed 44.1k output for law (A,S) on prepared CaseInput ci."""
|
||||
def replay(ci, lvl, nb, A, S, Q=0.0, res=None, rp=0.0):
|
||||
"""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}'
|
||||
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, :]
|
||||
yspec = np.fft.fft(segs, axis=1) * full
|
||||
td = np.real(np.fft.ifft(yspec, axis=1))
|
||||
@@ -203,20 +232,18 @@ def build_eval_index(trajs):
|
||||
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 = {}
|
||||
ycache = {}
|
||||
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:
|
||||
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]
|
||||
if ref_cache is None:
|
||||
errs[e['name']] = corpus.db(corpus.ta(y44, 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']]
|
||||
errs[e['name']] = corpus.db(corpus.ta(y44, e['f'])) - \
|
||||
corpus.db(corpus.ta(corpus.load_mono(e['ref']), e['f']))
|
||||
return errs
|
||||
|
||||
|
||||
@@ -250,45 +277,72 @@ def sanity(A, S, json_path):
|
||||
def fit(A0, S0):
|
||||
trajs = load_traj()
|
||||
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
|
||||
return group_stats(sim_errors(trajs, ii, A, S))
|
||||
return group_stats(sim_errors(trajs, ii, A, S, Q, rp))
|
||||
|
||||
best = (A0, S0)
|
||||
bst = objective(*best)
|
||||
print(f'start A={A0} S={S0}: TOTAL={bst["TOTAL"]:.3f}')
|
||||
stepA, stepS = 0.8, 0.25
|
||||
for it in range(4):
|
||||
moved = False
|
||||
for A, S in [(best[0] + stepA, best[1]), (best[0] - stepA, best[1]),
|
||||
(best[0], best[1] + stepS), (best[0], best[1] - stepS)]:
|
||||
st = objective(A, S)
|
||||
mark = ''
|
||||
if st['TOTAL'] < bst['TOTAL'] - 1e-4:
|
||||
best, bst = (A, S), st
|
||||
moved = True
|
||||
mark = ' *'
|
||||
print(f' [{it}] A={A:+7.3f} S={S:+6.3f}: TOTAL={st["TOTAL"]:.3f}{mark}')
|
||||
if not moved:
|
||||
stepA /= 2
|
||||
stepS /= 2
|
||||
if stepA < 0.05:
|
||||
break
|
||||
print(f'\nBEST global: A={best[0]:.3f} S={best[1]:.3f} TOTAL={bst["TOTAL"]:.3f}')
|
||||
for k, v in bst.items():
|
||||
print(f' {k:>6}: {v:.3f}')
|
||||
def descend(p0, steps, sub=None, label=''):
|
||||
best = list(p0)
|
||||
bst = objective(tuple(best), sub)
|
||||
print(f'{label} start {[round(v, 4) for v in best]}: '
|
||||
f'TOTAL={bst["TOTAL"]:.3f}', flush=True)
|
||||
while all(s > 1e-4 for s in steps.values()):
|
||||
moved = False
|
||||
for i, nm in enumerate(['A', 'S', 'Q', 'rp']):
|
||||
if nm not in steps:
|
||||
continue
|
||||
st = steps[nm]
|
||||
for d in (+st, -st):
|
||||
cand = list(best)
|
||||
cand[i] = round(cand[i] + d, 6)
|
||||
if cand[3] < 0 or (nm == 'Q' and abs(cand[2]) > 3):
|
||||
continue
|
||||
s2 = objective(tuple(cand), sub)
|
||||
if s2['TOTAL'] < bst['TOTAL'] - 1e-4:
|
||||
best, bst = cand, s2
|
||||
moved = True
|
||||
print(f' {label} {nm}{d:+.4g}: TOTAL={s2["TOTAL"]:.3f} '
|
||||
f'{[round(v, 4) for v in best]}', flush=True)
|
||||
if not moved:
|
||||
for nm in steps:
|
||||
steps[nm] /= 2
|
||||
return best, bst
|
||||
|
||||
print('\n=== per-group greedy optima (grid around global best) ===')
|
||||
for g in ['t1kq', 't1k', 'al', 'res', 'dual', 'comb']:
|
||||
sub = [e for e in idx if e['grp'] == g]
|
||||
bA, bS, bval = None, None, 1e9
|
||||
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):
|
||||
st = objective(float(A), float(S), sub)
|
||||
if st[g] < bval:
|
||||
bA, bS, bval = float(A), float(S), st[g]
|
||||
print(f'{g:>6}: A={bA:6.2f} S={bS:5.2f} mean|e|={bval:.3f}', flush=True)
|
||||
res_all = {}
|
||||
|
||||
b, t = descend([A0, S0, 0.0, 0.0], {'A': 0.4, 'S': 0.15}, label='[scalar]')
|
||||
res_all['scalar'] = (list(b), dict(t))
|
||||
|
||||
bA, bS, _, _ = res_all['scalar'][0]
|
||||
b, t = descend([bA, bS, 0.0, 0.0], {'A': 0.3, 'S': 0.15, 'Q': 0.06},
|
||||
label='[quad]')
|
||||
res_all['quad'] = (list(b), dict(t))
|
||||
|
||||
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):
|
||||
|
||||
Reference in New Issue
Block a user