Regularity-Matched Random Fourier Layer / experiment.py
Mechanism confirmed, baseline not beaten
1import json
2import time
3from pathlib import Path
4import numpy as np
5
6SEED = 3104
7rng_global = np.random.default_rng(SEED)
8
9
10def sample_rff(n, d=2, mode="gaussian", rng=None):
11 rng = np.random.default_rng() if rng is None else rng
12 if mode == "gaussian":
13 # A conventional isotropic Gaussian RFF control with deliberately broad bandwidth.
14 w = rng.normal(0.0, 3.0, size=(n, d))
15 elif mode == "uniform":
16 w = rng.uniform(-6.0, 6.0, size=(n, d))
17 elif mode == "gevery_matched":
18 # density proportional to exp(-2*kappa*||w||^(1/s)); s=1, kappa=.75.
19 # In d dimensions, y=2*kappa*r^(1/s) is Gamma(d*s, 1).
20 s, kappa = 1.0, 0.75
21 y = rng.gamma(shape=d * s, scale=1.0, size=n)
22 r = (y / (2.0 * kappa)) ** s
23 direction = rng.normal(size=(n, d))
24 direction /= np.linalg.norm(direction, axis=1, keepdims=True)
25 w = r[:, None] * direction
26 elif mode == "sobolev_matched":
27 # density proportional to (1+||w||^2)^(-bar_s), bar_s>d/2.
28 bar_s = 3.0
29 u = rng.beta(d / 2.0, bar_s - d / 2.0, size=n)
30 r = np.sqrt(u / (1.0 - u))
31 direction = rng.normal(size=(n, d))
32 direction /= np.linalg.norm(direction, axis=1, keepdims=True)
33 w = r[:, None] * direction
34 else:
35 raise ValueError(mode)
36 b = rng.uniform(0.0, 2.0 * np.pi, size=n)
37 return w, b
38
39
40def features(x, w, b):
41 return np.sqrt(2.0) * np.cos(x @ w.T + b[None, :])
42
43
44def target(x):
45 # Smooth (indeed analytic) low-frequency periodic target, representative of a
46 # coordinate field whose spectral energy is concentrated near the origin.
47 return (0.80 * np.cos(x[:, 0] + 0.20) +
48 0.55 * np.cos(x[:, 1] - 0.70) +
49 0.35 * np.cos(2.0 * x[:, 0] + x[:, 1] + 0.40) +
50 0.18 * np.cos(3.0 * x[:, 1] - 0.30))
51
52
53def whiten(z, eps_scale=1e-5):
54 g = (z.T @ z) / z.shape[0]
55 eps = eps_scale * np.trace(g) / g.shape[0]
56 vals, vecs = np.linalg.eigh(g + eps * np.eye(g.shape[0]))
57 vals = np.maximum(vals, eps)
58 zw = z @ (vecs * (1.0 / np.sqrt(vals))) @ vecs.T
59 return zw, np.linalg.eigvalsh(g), float(eps)
60
61
62def ridge_fit(z, y, alpha=1e-5):
63 a = z.T @ z + alpha * np.eye(z.shape[1])
64 return np.linalg.solve(a, z.T @ y)
65
66
67def run():
68 rng = np.random.default_rng(SEED)
69 # Random train/test locations avoid exploiting a grid-specific aliasing artifact.
70 xtr = rng.uniform(-np.pi, np.pi, size=(2048, 2))
71 xte = rng.uniform(-np.pi, np.pi, size=(4096, 2))
72 ytr, yte = target(xtr), target(xte)
73 modes = ["gaussian", "uniform", "gevery_matched"]
74 counts = [64, 128, 256, 512]
75 rows = []
76 t0 = time.perf_counter()
77 # Three independent draws provide a small reproducibility check.
78 for n in counts:
79 for mode in modes:
80 for whitened in [False, True]:
81 errs, conds = [], []
82 for rep in range(3):
83 rr = np.random.default_rng(SEED + 10000 * rep + n + hash(mode) % 997)
84 w, b = sample_rff(n, mode=mode, rng=rr)
85 ztr, zte = features(xtr, w, b), features(xte, w, b)
86 raw_eigs = np.linalg.eigvalsh((ztr.T @ ztr) / len(xtr))
87 cond = float(raw_eigs[-1] / max(raw_eigs[0], 1e-15))
88 if whitened:
89 ztr, _, _ = whiten(ztr)
90 # Whitening is an input-coordinate transform learned on train;
91 # use the same transform for test by recomputing explicitly below.
92 g = (features(xtr, w, b).T @ features(xtr, w, b)) / len(xtr)
93 eps = 1e-5 * np.trace(g) / n
94 vals, vecs = np.linalg.eigh(g + eps * np.eye(n))
95 zte = zte @ (vecs * (1.0 / np.sqrt(np.maximum(vals, eps)))) @ vecs.T
96 beta = ridge_fit(ztr, ytr)
97 pred = zte @ beta
98 errs.append(float(np.sqrt(np.mean((pred - yte) ** 2))))
99 conds.append(cond)
100 rows.append({"N": n, "mode": mode, "whitened": whitened,
101 "test_rmse_mean": float(np.mean(errs)),
102 "test_rmse_std": float(np.std(errs)),
103 "raw_gram_condition_mean": float(np.mean(conds))})
104 # Cheap mathematical sanity checks: empirical tails should decrease in the
105 # expected order, and whitening should make the training Gram spectrum nearly flat.
106 tail_rng = np.random.default_rng(SEED + 77)
107 checks = {}
108 for mode in ["gaussian", "gevery_matched", "sobolev_matched"]:
109 w, _ = sample_rff(200000, mode=mode, rng=tail_rng)
110 r = np.linalg.norm(w, axis=1)
111 checks[mode] = {"median_radius": float(np.median(r)),
112 "q99_radius": float(np.quantile(r, .99)),
113 "mean_radius": float(np.mean(r))}
114 w, b = sample_rff(256, mode="gevery_matched", rng=np.random.default_rng(SEED))
115 zz = features(xtr, w, b)
116 before = np.linalg.eigvalsh((zz.T @ zz) / len(xtr))
117 zw, after, eps = whiten(zz)
118 after_reg = np.linalg.eigvalsh((zw.T @ zw) / len(xtr))
119 checks["whitening"] = {
120 "raw_condition": float(before[-1] / max(before[0], 1e-15)),
121 "whitened_condition": float(after_reg[-1] / max(after_reg[0], 1e-15)),
122 "whitened_eigen_min": float(after_reg[0]),
123 "whitened_eigen_max": float(after_reg[-1]),
124 "ridge_epsilon": eps,
125 }
126 out = {"seed": SEED, "runtime_sec": time.perf_counter() - t0,
127 "rows": rows, "sanity_checks": checks}
128 Path("results.json").write_text(json.dumps(out, indent=2))
129 print(json.dumps(out, indent=2))
130
131
132if __name__ == "__main__":
133 run()