Kernel-Prompted Random Transformer / kernel_prompt_experiment.py
Mechanism confirmed, baseline not beaten
1import json, time
2import numpy as np
3
4SEED = 2052
5rng = np.random.default_rng(SEED)
6
7
8def softmax(a):
9 a = a - np.max(a, axis=-1, keepdims=True)
10 z = np.exp(a)
11 return z / z.sum(axis=-1, keepdims=True)
12
13
14def make_frozen(d, dh, m, seed):
15 r = np.random.default_rng(seed)
16 # Variance-normalized frozen Gaussian maps.
17 Q = r.normal(0, 1 / np.sqrt(dh), size=(dh, dh))
18 K = r.normal(0, 1 / np.sqrt(dh), size=(dh, dh))
19 V = r.normal(0, 1 / np.sqrt(dh), size=(m, dh))
20 A = (Q.T @ K)[: d + 1] / np.sqrt(dh)
21 return Q, K, V, A
22
23
24def targets(Xs, sigma):
25 # b_i(x), modulo the query-only term -||x||^2/(2 sigma^2).
26 return np.column_stack((Xs / sigma**2, -np.sum(Xs * Xs, axis=1) / (2 * sigma**2)))
27
28
29def construct(Xs, ys, dh, sigma, ridge=0.0, seed=0):
30 d = Xs.shape[1]
31 m = ys.shape[1]
32 _, _, V, A = make_frozen(d, dh, m, seed)
33 C = targets(Xs, sigma)
34 B = np.vstack((A, V))
35 rhs = np.hstack((C, ys)).T # (d+1+m, N)
36 if ridge == 0:
37 P = np.linalg.lstsq(B, rhs, rcond=None)[0]
38 else:
39 P = np.linalg.solve(B.T @ B + ridge * np.eye(dh), B.T @ rhs)
40 return P.T, A, V, C
41
42
43def predict(Xq, P, A, V, d):
44 E = np.column_stack((Xq, np.ones(len(Xq)), np.zeros((len(Xq), A.shape[1] - d - 1))))
45 logits = E[:, : d + 1] @ (A @ P.T)
46 W = softmax(logits)
47 vals = (V @ P.T).T
48 return W @ vals, W, logits
49
50
51def nw(Xq, Xs, ys, sigma):
52 b = -((Xq[:, None, :] - Xs[None, :, :]) ** 2).sum(axis=2) / (2 * sigma**2)
53 return softmax(b) @ ys, softmax(b)
54
55
56def knn(Xq, Xs, ys, k=3):
57 dist = ((Xq[:, None, :] - Xs[None, :, :]) ** 2).sum(axis=2)
58 ix = np.argpartition(dist, k - 1, axis=1)[:, :k]
59 return ys[ix].mean(axis=1)
60
61
62def holder(x):
63 return (np.sin(2.3 * x[:, :1]) + 0.6 * np.cos(3.1 * x[:, 1:2]) +
64 0.25 * np.sin(5 * (x[:, :1] + x[:, 1:2])))
65
66
67def math_verification():
68 d, m, N, sigma = 2, 1, 24, 0.35
69 Xs = rng.uniform(-1, 1, (N, d)); ys = holder(Xs)
70 rows = []
71 # Prediction 1: generic full row rank appears at dh >= d+1+m = 4.
72 for dh in [3, 4, 5, 8, 16]:
73 P, A, V, C = construct(Xs, ys, dh, sigma, 0, 11)
74 B = np.vstack((A, V)); rhs = np.vstack((C.T, ys.T))
75 residual = np.linalg.norm((B @ P.T - rhs).T) / np.sqrt(N)
76 rank = int(np.linalg.matrix_rank(B, tol=1e-10))
77 rows.append({'dh': dh, 'joint_rank': rank, 'joint_rows': d+1+m,
78 'rms_joint_residual': float(residual),
79 'mean_prompt_norm': float(np.linalg.norm(P, axis=1).mean())})
80 # Prediction 2: ridge regularization trades residual for prompt norm monotonically.
81 ridge_rows = []
82 for lam in [0, 1e-5, 1e-3, 1e-1, 1.0]:
83 P, A, V, C = construct(Xs, ys, 16, sigma, lam, 11)
84 B = np.vstack((A, V)); rhs = np.vstack((C.T, ys.T))
85 ridge_rows.append({'lambda': lam,
86 'rms_residual': float(np.linalg.norm((B @ P.T-rhs).T)/np.sqrt(N)),
87 'mean_prompt_norm': float(np.linalg.norm(P,axis=1).mean())})
88 # Prediction 3: target coefficient and prompt norm scale as sigma^-2, while
89 # normalized kernel agreement remains exact in the full-rank regime.
90 sigma_rows = []
91 Xq = rng.uniform(-1, 1, (100, d))
92 for s in [0.25, 0.35, 0.5, 0.7]:
93 P, A, V, C = construct(Xs, ys, 16, s, 0, 11)
94 pred, W, logits = predict(Xq, P, A, V, d)
95 ref, Wref = nw(Xq, Xs, ys, s)
96 sigma_rows.append({'sigma': s, 'mean_prompt_norm': float(np.linalg.norm(P,axis=1).mean()),
97 'weight_max_abs_error': float(np.max(np.abs(W-Wref))),
98 'prediction_mse_to_nw': float(np.mean((pred-ref)**2))})
99 return {'rank_prediction': rows, 'ridge_prediction': ridge_rows,
100 'bandwidth_prediction': sigma_rows}
101
102
103def mini_experiment():
104 d, m, dh, sigma = 2, 1, 16, 0.38
105 r = np.random.default_rng(77)
106 Xtest = r.uniform(-1, 1, (1000, d)); ytest = holder(Xtest)
107 result = []
108 for N in [32, 64, 128, 256]:
109 Xs = r.uniform(-1, 1, (N, d)); ys = holder(Xs)
110 t0 = time.perf_counter()
111 P, A, V, C = construct(Xs, ys, dh, sigma, 0, 19)
112 adaptation_ms = (time.perf_counter()-t0)*1000
113 pred, W, logits = predict(Xtest, P, A, V, d)
114 ref, Wref = nw(Xtest, Xs, ys, sigma)
115 result.append({'N': N, 'analytic_mse': float(np.mean((pred[:,0]-ytest[:,0])**2)),
116 'nw_mse': float(np.mean((ref[:,0]-ytest[:,0])**2)),
117 'knn3_mse': float(np.mean((knn(Xtest,Xs,ys)[:,0]-ytest[:,0])**2)),
118 'attention_weight_error': float(np.max(np.abs(W-Wref))),
119 'logit_rms_residual_mod_common': float(np.sqrt(np.mean((logits - (Xtest @ Xs.T / sigma**2 - (Xs**2).sum(axis=1)[None,:] / (2*sigma**2)))**2))),
120 'prompt_memory_floats': int(P.size), 'adaptation_ms': adaptation_ms})
121 return result
122
123
124def main():
125 out = {'seed': SEED, 'math_verification': math_verification(),
126 'mini_experiment': mini_experiment()}
127 with open('results.json', 'w') as f: json.dump(out, f, indent=2)
128 print(json.dumps(out, indent=2))
129
130if __name__ == '__main__':
131 main()