Finite-Excitation Latent Replay / finite_excitation_replay.py

Failed on benchmark

Raw ⬇ ZIP
  1import json
  2import numpy as np
  3
  4
  5def opnorm(a):
  6    return np.linalg.norm(a, 2)
  7
  8
  9def gram(stack, p=None):
 10    if p is None:
 11        p = stack[0].shape[1]
 12    return sum((w.T @ w for w in stack), np.zeros((p, p)))
 13
 14
 15def theorem_bound(true_stack, errors):
 16    return sum(2 * opnorm(w) * e + e * e for w, e in zip(true_stack, errors))
 17
 18
 19def computable_bound(estimated_stack, errors):
 20    # Since ||Omega|| <= ||Omega_hat|| + ||Delta||, this is a valid
 21    # estimated-state-only inflation of the theorem bound.
 22    return sum(2 * (opnorm(h) + e) * e + e * e
 23               for h, e in zip(estimated_stack, errors))
 24
 25
 26def greedy_stack(candidates, max_n=8):
 27    selected, selected_ids, current = [], [], -np.inf
 28    p = candidates[0].shape[1]
 29    for i, w in enumerate(candidates):
 30        trial = selected + [w]
 31        score = np.linalg.eigvalsh(gram(trial, p))[0]
 32        if len(selected) < p or score > current + 1e-10:
 33            selected, selected_ids, current = trial, selected_ids + [i], score
 34        if len(selected) >= max_n:
 35            break
 36    return selected, selected_ids
 37
 38
 39def mechanism_checks(rng):
 40    p, m = 2, 18
 41    true = [rng.normal(size=(3, p)) for _ in range(m)]
 42    true = [w / opnorm(w) for w in true]
 43
 44    rows = []
 45    violations = 0
 46    conservative_violations = 0
 47    for e in np.geomspace(1e-5, 0.8, 12):
 48        estimated, errors = [], []
 49        for w in true:
 50            d = rng.normal(size=w.shape); d *= e / opnorm(d)
 51            estimated.append(w + d); errors.append(e)
 52        diff = opnorm(gram(estimated) - gram(true))
 53        b = theorem_bound(true, errors)
 54        bc = computable_bound(estimated, errors)
 55        violations += int(diff > b * (1 + 1e-10))
 56        conservative_violations += int(diff > bc * (1 + 1e-10))
 57        rows.append((e, diff, b, bc, diff / b))
 58
 59    data = np.array(rows)
 60    # Prediction 1: exact perturbation is bounded for every mismatch size.
 61    # Prediction 2: theorem bound has O(e) scaling at small e.
 62    small_slope = np.median(data[:5, 1] / data[:5, 0])
 63    # Prediction 3: the quadratic term becomes non-negligible at large e;
 64    # report observed quadratic/theorem-bound contribution fraction.
 65    large_fraction = np.median((m * data[-4:, 0] ** 2) / data[-4:, 2])
 66
 67    # Certificate threshold with one fixed perturbation direction.  This
 68    # isolates the predicted monotone loss of the conservative certificate.
 69    gamma = 0.18
 70    directions = []
 71    for w in true:
 72        d = rng.normal(size=w.shape)
 73        directions.append(d / opnorm(d))
 74    threshold_rows = []
 75    for e in np.linspace(0, 0.55, 56):
 76        estimated = [w + e*d for w, d in zip(true, directions)]
 77        gh, gt = gram(estimated), gram(true)
 78        rho = theorem_bound(true, [e] * m)
 79        q = np.linalg.eigvalsh(gh)[0] - rho
 80        actual = np.linalg.eigvalsh(gt)[0]
 81        threshold_rows.append((e, q, actual))
 82    cert = np.array(threshold_rows)
 83    positive = cert[cert[:, 1] > gamma]
 84    observed_e = positive[-1, 0] if len(positive) else np.nan
 85    pred_e = np.nan
 86    for a, b in zip(cert[:-1], cert[1:]):
 87        if (a[1] - gamma) * (b[1] - gamma) <= 0:
 88            pred_e = a[0] + (gamma-a[1]) * (b[0]-a[0]) / (b[1]-a[1])
 89            break
 90    # Directly verify Weyl at every sweep point, not only q-positive points.
 91    cert_margin = float(np.min(cert[:, 2] - cert[:, 1]))
 92    return {
 93        'bound_violations': int(violations),
 94        'computable_bound_violations': int(conservative_violations),
 95        'bound_max_ratio': float(max(r[4] for r in rows)),
 96        'small_epsilon_slope_median': float(small_slope),
 97        'large_epsilon_quadratic_fraction': float(large_fraction),
 98        'gamma': gamma,
 99        'certificate_crossing_epsilon_observed': float(observed_e),
100        'certificate_crossing_epsilon_interpolated': float(pred_e),
101        'minimum_actual_minus_q': cert_margin,
102        'bound_table': [[float(x) for x in r] for r in rows],
103    }
104
105
106def identification(rng):
107    p, n_candidates = 2, 45
108    theta = np.array([1.25, -0.8])
109    candidates = []
110    for i in range(n_candidates):
111        angle = 0.12 * i
112        base = np.array([np.cos(angle), np.sin(angle)])
113        w = np.tile(base, (4, 1)) + 0.08 * rng.normal(size=(4, p))
114        candidates.append(w / opnorm(w))
115    noise = 0.035
116    y = [w @ theta + noise * rng.normal(size=4) for w in candidates]
117    greedy, idx = greedy_stack(candidates, max_n=8)
118    g = gram(greedy)
119
120    def estimate(ids):
121        H = gram([candidates[i] for i in ids], p)
122        b = sum((candidates[i].T @ y[i] for i in ids), np.zeros(p))
123        return np.linalg.solve(H + 1e-5*np.eye(p), b)
124
125    errors_gated, errors_all, activated = [], [], False
126    for t in range(1, n_candidates + 1):
127        errors_all.append(float(np.linalg.norm(estimate(range(t)) - theta)))
128        gs = [i for i in idx if i < t]
129        if len(gs) >= p and np.linalg.eigvalsh(gram([candidates[i] for i in gs], p))[0] > 0.05:
130            activated = True
131            est = estimate(gs)
132        else:
133            est = estimate(gs) if activated else np.zeros(p)
134        errors_gated.append(float(np.linalg.norm(est-theta)))
135    return {
136        'greedy_stack_size': len(greedy),
137        'greedy_lambda_min': float(np.linalg.eigvalsh(g)[0]),
138        'baseline_final_parameter_error': errors_all[-1],
139        'idea_final_parameter_error': errors_gated[-1],
140        'baseline_best_error': min(errors_all),
141        'idea_best_error': min(errors_gated),
142        'activation_step': next((t for t in range(1, n_candidates + 1) if len([i for i in idx if i < t]) >= p and np.linalg.eigvalsh(gram([candidates[i] for i in idx if i < t], p))[0] > 0.05), None),
143        'errors_baseline': errors_all,
144        'errors_idea': errors_gated,
145    }
146
147
148def main():
149    rng = np.random.default_rng(2914)
150    result = {'seed': 2914, 'mechanism': mechanism_checks(rng), 'identification': identification(rng)}
151    with open('results.json', 'w') as f: json.dump(result, f, indent=2)
152    print(json.dumps(result, indent=2))
153
154
155if __name__ == '__main__':
156    main()