Local Characteristic Residual Gating / characteristic_gate_experiment.py

✓✓ Beats tuned baseline

Raw ⬇ ZIP
  1import json
  2import numpy as np
  3
  4
  5def build_q(h, q, theta):
  6    h = max(float(h), 1e-6)
  7    theta = max(float(theta), 1e-6)
  8    c = np.sqrt(h * theta)
  9    Q = np.array([
 10        [0, 0, -q/(2*theta), c/theta, -c/theta],
 11        [0, 0, 0, 1, 1],
 12        [0, 1, 0, 0, 0],
 13        [0, 0, 1, 0, 0],
 14        [1, 0, 0, 0, 0]], dtype=float)
 15    # This is the algebraic inverse of the displayed Q.  Solving the
 16    # first/fourth rows gives a PLUS q/(4c) term in rows 4 and 5.
 17    Qi = np.array([
 18        [0, 0, 0, 0, 1],
 19        [0, 0, 1, 0, 0],
 20        [0, 0, 0, 1, 0],
 21        [theta/(2*c), .5, 0, q/(4*c), 0],
 22        [-theta/(2*c), .5, 0, -q/(4*c), 0]], dtype=float)
 23    return Q, Qi
 24
 25
 26def displayed_inverse(h, q, theta):
 27    h = max(float(h), 1e-6); theta = max(float(theta), 1e-6)
 28    c = np.sqrt(h * theta)
 29    return np.array([
 30        [0, 0, 0, 0, 1], [0, 0, 1, 0, 0], [0, 0, 0, 1, 0],
 31        [theta/(2*c), .5, 0, -q/(4*c), 0],
 32        [-theta/(2*c), .5, 0, -q/(4*c), 0]], dtype=float)
 33
 34
 35def characteristic_gate(a, alpha=2.0, eps=1e-8):
 36    left, right = a[:-1], a[1:]
 37    s = np.abs(right-left) / (np.abs(right)+np.abs(left)+eps)
 38    g = 1.0 / (1.0 + alpha*s)
 39    return left * g, g
 40
 41
 42def scalar_tv_gate(a, alpha=2.0, eps=1e-8):
 43    left, right = a[:-1], a[1:]
 44    num = np.linalg.norm(right-left, axis=1)
 45    den = np.linalg.norm(right, axis=1)+np.linalg.norm(left, axis=1)+eps
 46    g = 1.0/(1.0+alpha*num/den)
 47    return left*g[:, None], np.broadcast_to(g[:, None], left.shape)
 48
 49
 50def metrics(pred, gates, target, base):
 51    acoustic_rms = float(np.sqrt(np.mean(pred[:,3:]**2)))
 52    base_acoustic = float(np.sqrt(np.mean(base[:,3:]**2)))
 53    return {
 54        'acoustic_rms': acoustic_rms,
 55        'acoustic_suppression_fraction': 1-acoustic_rms/base_acoustic,
 56        'equilibrium_mse_to_clean': float(np.mean((pred[:,:3]-target[:,:3])**2)),
 57        'total_mse_to_clean': float(np.mean((pred-target)**2)),
 58        'mean_gate_equilibrium': float(np.mean(gates[:,:3])),
 59        'mean_gate_acoustic': float(np.mean(gates[:,3:])),
 60        'min_gate': float(np.min(gates)), 'max_gate': float(np.max(gates)),
 61    }
 62
 63
 64def main():
 65    rng = np.random.default_rng(631)
 66    identity_errors = []
 67    literal_errors = []
 68    for _ in range(1000):
 69        h = 10**rng.uniform(-6, 1); theta = 10**rng.uniform(-6, 1)
 70        q = rng.normal() * np.sqrt(h*theta)
 71        Q, Qi = build_q(h, q, theta)
 72        identity_errors.append(max(np.max(np.abs(Q@Qi-np.eye(5))), np.max(np.abs(Qi@Q-np.eye(5)))))
 73        Qil = displayed_inverse(h, q, theta)
 74        literal_errors.append(max(np.max(np.abs(Q@Qil-np.eye(5))), np.max(np.abs(Qil@Q-np.eye(5)))))
 75
 76    n = 256; x = np.linspace(-1, 1, n)
 77    shock = .5*(1+np.tanh(x/.045))
 78    a = np.zeros((n,5))
 79    a[:,0] = 1.0 + .4*shock
 80    a[:,1] = .15*np.sin(2*np.pi*x)
 81    a[:,2] = .08*np.cos(np.pi*x)
 82    envelope = np.exp(-(x/.22)**2)
 83    ringing = envelope*((-1.0)**np.arange(n))
 84    a[:,3] = .18*ringing; a[:,4] = -.15*ringing
 85    a += rng.normal(0, .002, a.shape)
 86    clean = a.copy(); clean[:,3:] = 0.0
 87    base, base_g = a[:-1], np.ones((n-1,5)); target = clean[:-1]
 88    gated, g_char = characteristic_gate(a)
 89    scalar, g_scalar = scalar_tv_gate(a)
 90
 91    Q, Qi = build_q(2.0, .7, 1.3); r = rng.normal(size=5)
 92    result = {
 93        'math': {
 94            'corrected_inverse_max_identity_error': float(max(identity_errors)),
 95            'corrected_inverse_median_identity_error': float(np.median(identity_errors)),
 96            'literal_displayed_inverse_max_identity_error': float(max(literal_errors)),
 97            'literal_displayed_inverse_median_identity_error': float(np.median(literal_errors)),
 98            'representative_roundtrip_error': float(np.max(np.abs(Q @ (Qi @ r) - r))),
 99            'gate_min': float(np.min(g_char)), 'gate_max': float(np.max(g_char)),
100            'all_gates_bounded': bool(np.all((g_char > 0) & (g_char <= 1))),
101        },
102        'baseline_no_gate': metrics(base, base_g, target, base),
103        'characteristic_gate': metrics(gated, g_char, target, base),
104        'scalar_tv_gate': metrics(scalar, g_scalar, target, base),
105        'setup': {'n': n, 'alpha': 2.0, 'seed': 631,
106                  'description': 'smooth equilibrium jump plus localized alternating acoustic ringing'}
107    }
108    print(json.dumps(result, indent=2))
109
110if __name__ == '__main__':
111    main()