Critical Spectral Mode Compression / experiment.py
Mechanism failed
1import json
2from pathlib import Path
3import numpy as np
4
5SEED = 1109
6rng = np.random.default_rng(SEED)
7D = 256
8A = 1.0
9u = (np.arange(D) + 0.5) / D
10omega = 2*np.pi * u**(1/(A+1))
11q = np.ones(D) / D
12
13
14def return_proxy(lam, weights, ks):
15 lam = np.asarray(lam, complex)
16 weights = np.asarray(weights, float)
17 weights = weights / weights.sum()
18 amp = np.sum(weights[None, :] * lam[None, :] ** ks[:, None], axis=1)
19 return np.abs(amp)**2
20
21
22def fit_slope(ks, vals, lo=20, hi=180):
23 sel = (ks >= lo) & (ks <= hi) & (vals > 1e-14)
24 if sel.sum() < 3:
25 return float("nan")
26 return float(np.polyfit(np.log(ks[sel]), np.log(vals[sel]), 1)[0])
27
28ks = np.arange(1, 401)
29rho0 = 0.9998
30teacher_lam = rho0 * np.exp(1j * omega)
31R_teacher = return_proxy(teacher_lam, q, ks)
32
33
34def select_frequency(M):
35 return np.linspace(0, D - 1, M).round().astype(int)
36
37
38def select_random(M):
39 return np.sort(rng.choice(D, M, replace=False))
40
41
42def select_magnitude(M):
43 # Equal modal magnitudes make magnitude pruning unable to distinguish modes.
44 return np.arange(M)
45
46
47def relative_log_error(a, b):
48 return float(np.sqrt(np.mean((np.log(a + 1e-16) - np.log(b + 1e-16)) ** 2)))
49
50# Compression comparison. Selected modes inherit teacher weights and are renormalized.
51compression = []
52for M in [8, 16, 32, 64]:
53 for name, selector in [("frequency", select_frequency), ("random", select_random), ("magnitude", select_magnitude)]:
54 idx = selector(M)
55 R = return_proxy(teacher_lam[idx], q[idx], ks)
56 compression.append({
57 "M": M, "method": name,
58 "log_rmse": relative_log_error(R_teacher, R),
59 "slope": fit_slope(ks, R),
60 "teacher_slope": fit_slope(ks, R_teacher),
61 "slope_relative_error": abs(fit_slope(ks, R) - fit_slope(ks, R_teacher)) / max(abs(fit_slope(ks, R_teacher)), 1e-12),
62 })
63
64# Prediction 1: stable radius gives an exponential envelope with time constant
65# tau = -1/(2 log rho), because R(k) contains rho^(2k).
66radii = [0.90, 0.95, 0.99, 0.999]
67stability = []
68for rho in radii:
69 R = return_proxy(rho * np.exp(1j * omega[:32]), q[:32], ks)
70 # Fit log envelope after averaging oscillations by a robust upper quantile in bins.
71 bins = np.array_split(np.arange(len(ks)), 20)
72 kb = np.array([np.mean(ks[b]) for b in bins])
73 rb = np.array([np.quantile(R[b], 0.8) for b in bins])
74 observed_slope = float(np.polyfit(kb, np.log(rb + 1e-30), 1)[0])
75 predicted_slope = 2*np.log(rho)
76 stability.append({"rho": rho, "predicted_log_slope": predicted_slope,
77 "observed_log_slope": observed_slope,
78 "relative_error": abs(observed_slope-predicted_slope)/abs(predicted_slope)})
79
80# Prediction 2: at rho=1 there is no exponential decay; rho>1 grows at rate 2 log rho.
81boundary = []
82for rho in [0.999, 1.0, 1.001, 1.01]:
83 R = return_proxy(rho * np.exp(1j * omega[:1]), np.array([1.0]), ks)
84 observed = float(np.polyfit(ks, np.log(R), 1)[0])
85 predicted = 2*np.log(rho)
86 boundary.append({"rho": rho, "predicted_log_slope": predicted, "observed_log_slope": observed,
87 "absolute_error": abs(observed-predicted)})
88
89# Prediction 3: increasing mode count improves quadrature of the pair-difference spectrum.
90scaling = []
91for M in [4, 8, 16, 32, 64, 128]:
92 idx = select_frequency(M)
93 R = return_proxy(teacher_lam[idx], q[idx], ks)
94 scaling.append({"M": M, "log_rmse": relative_log_error(R_teacher, R),
95 "slope_relative_error": abs(fit_slope(ks, R)-fit_slope(ks, R_teacher))/max(abs(fit_slope(ks, R_teacher)),1e-12)})
96
97out = {"seed": SEED, "D": D, "ks": [1, 400], "teacher_slope": fit_slope(ks, R_teacher),
98 "stability_sweep": stability, "boundary_sweep": boundary,
99 "compression": compression, "mode_scaling": scaling}
100Path("results.json").write_text(json.dumps(out, indent=2))
101print(json.dumps(out, indent=2))