AbstractPhil commited on
Commit
b22847f
Β·
verified Β·
1 Parent(s): 747a57b

Create notebook_cell_2_bulk_test_try1.py

Browse files
Files changed (1) hide show
  1. notebook_cell_2_bulk_test_try1.py +668 -0
notebook_cell_2_bulk_test_try1.py ADDED
@@ -0,0 +1,668 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ FLEighConduit β€” Sweeping Deterministic Analysis
3
+ =================================================
4
+ No models. No training. Pure numerical scrutiny.
5
+
6
+ Tests every major theorem, every conduit field, every vulnerability
7
+ identified by the council. Results speak for themselves.
8
+
9
+ Sections:
10
+ 1. Parity Verification (Theorem 1)
11
+ 2. Characteristic Coefficients Validation
12
+ 3. Friction Signal β€” Spectral Gap Correlation
13
+ 4. Friction Signal β€” Controlled Gap Sweep
14
+ 5. Settle Time Analysis
15
+ 6. Extraction Order Determinism
16
+ 7. Near-Degenerate Behavior (Theorem 4 stress test)
17
+ 8. Sign Canonicalization (Theorem 5)
18
+ 9. Refinement Residual Analysis
19
+ 10. Static Reconstruction Test (Theorem 2 β€” static side)
20
+ 11. Dynamic Non-Reconstructibility (Theorem 2 β€” dynamic side)
21
+ 12. Dimension Agnostic Scaling (n=3,4,6,8)
22
+ 13. Research Mode β€” Mstore & Trajectory Inspection
23
+ 14. Freckles CIFAR-10 β€” Class Discriminability of Conduit Fields
24
+
25
+ Usage:
26
+ Run each section as a separate Colab cell.
27
+ All sections are independent β€” no state carries between them.
28
+ """
29
+
30
+ # ═══════════════════════════════════════════════════════════════
31
+ # SETUP (run this cell first)
32
+ # ═══════════════════════════════════════════════════════════════
33
+
34
+ import torch
35
+ import torch.nn.functional as F
36
+ import numpy as np
37
+ import time
38
+
39
+ from geolip_core.linalg import FLEigh, eigh
40
+ from geolip_core.linalg.conduit import (
41
+ FLEighConduit, ConduitPacket, canonicalize_eigenvectors, verify_parity
42
+ )
43
+
44
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
45
+ torch.manual_seed(42)
46
+
47
+ def section(title):
48
+ print(f"\n{'=' * 70}")
49
+ print(f" {title}")
50
+ print(f"{'=' * 70}\n")
51
+
52
+ def make_symmetric(B, n, device='cuda'):
53
+ """Random symmetric matrices."""
54
+ A = torch.randn(B, n, n, device=device)
55
+ return (A + A.transpose(-1, -2)) / 2
56
+
57
+ def make_with_eigenvalues(eigenvalues, B=1, device='cuda'):
58
+ """Construct symmetric matrices with prescribed eigenvalues.
59
+ eigenvalues: (n,) or (B, n)"""
60
+ if eigenvalues.dim() == 1:
61
+ eigenvalues = eigenvalues.unsqueeze(0).expand(B, -1)
62
+ n = eigenvalues.shape[-1]
63
+ # Random orthogonal basis
64
+ Q, _ = torch.linalg.qr(torch.randn(B, n, n, device=device))
65
+ return Q @ torch.diag_embed(eigenvalues.to(device)) @ Q.transpose(-1, -2)
66
+
67
+ print(f"Device: {device}")
68
+ print("Setup complete.\n")
69
+
70
+
71
+ # ═══════════════════════════════════════════════════════════════
72
+ # 1. PARITY VERIFICATION (Theorem 1)
73
+ # ═══════════════════════════════════════════════════════════════
74
+
75
+ section("1. PARITY VERIFICATION")
76
+
77
+ ref_solver = FLEigh().to(device)
78
+ conduit_solver = FLEighConduit().to(device)
79
+
80
+ n_tests = 0
81
+ n_pass = 0
82
+ max_eval_err = 0
83
+ max_evec_err = 0
84
+
85
+ for n in [3, 4, 5, 6, 8]:
86
+ for trial in range(20):
87
+ A = make_symmetric(16, n, device)
88
+ ref_evals, ref_evecs = ref_solver(A)
89
+ packet = conduit_solver(A)
90
+ cond_evals, cond_evecs = packet.eigenpairs()
91
+
92
+ eval_err = (ref_evals - cond_evals).abs().max().item()
93
+ # Eigenvectors: compare via absolute dot products (sign-agnostic)
94
+ dots = (ref_evecs * cond_evecs).sum(dim=-2).abs()
95
+ evec_err = (1.0 - dots).abs().max().item()
96
+
97
+ max_eval_err = max(max_eval_err, eval_err)
98
+ max_evec_err = max(max_evec_err, evec_err)
99
+ n_tests += 1
100
+ if eval_err < 1e-4 and evec_err < 1e-3:
101
+ n_pass += 1
102
+
103
+ print(f" Tests: {n_tests}")
104
+ print(f" Passed: {n_pass}/{n_tests}")
105
+ print(f" Max eval error: {max_eval_err:.2e}")
106
+ print(f" Max evec error: {max_evec_err:.2e}")
107
+ print(f" VERDICT: {'PASS' if n_pass == n_tests else 'FAIL'}")
108
+
109
+
110
+ # ═══════════════════════════════════════════════════════════════
111
+ # 2. CHARACTERISTIC COEFFICIENTS VALIDATION
112
+ # ═══════════════════════════════════════════════════════════════
113
+
114
+ section("2. CHARACTERISTIC COEFFICIENTS VALIDATION")
115
+
116
+ # For known eigenvalues, verify c[] matches elementary symmetric polynomials
117
+ test_evals = torch.tensor([1.0, 2.0, 3.0, 4.0], device=device)
118
+ A = make_with_eigenvalues(test_evals, B=8)
119
+ packet = conduit_solver(A)
120
+
121
+ # Elementary symmetric polynomials of eigenvalues:
122
+ # e1 = sum = 10, e2 = sum of pairs = 35, e3 = sum of triples = 50, e4 = product = 24
123
+ # Char poly: x^4 - e1*x^3 + e2*x^2 - e3*x + e4
124
+ # c[3] = -e1, c[2] = e2, c[1] = -e3, c[0] = e4
125
+ # But FLEigh works on scaled matrices, so we check relative structure
126
+
127
+ computed_evals = packet.eigenvalues # should be close to [1,2,3,4]
128
+ computed_coeffs = packet.char_coeffs
129
+
130
+ print(" Prescribed eigenvalues: [1, 2, 3, 4]")
131
+ print(f" Recovered eigenvalues: {computed_evals[0].tolist()}")
132
+ print(f" Char coeffs (sample): {computed_coeffs[0].tolist()}")
133
+
134
+ # Verify: coefficients reconstruct the polynomial that has these roots
135
+ # p(x) = x^4 + c3*x^3 + c2*x^2 + c1*x + c0
136
+ # p(lambda_i) should be ~0 for each eigenvalue
137
+ evals_d = computed_evals[0].double()
138
+ c = computed_coeffs[0].double()
139
+ # Note: FLEigh scales A, so char_coeffs are for the scaled matrix
140
+ # We verify that the STRUCTURE is correct by checking ratios
141
+ print(f"\n Coefficient ratios (should be consistent across batch):")
142
+ for i in range(min(4, len(c))):
143
+ vals = computed_coeffs[:, i]
144
+ print(f" c[{i}]: mean={vals.mean():.4f} std={vals.std():.6f} "
145
+ f"cv={vals.std()/vals.mean().abs():.4f}")
146
+
147
+ print(f"\n VERDICT: Coefficients {'consistent' if computed_coeffs.std(0).max() < 0.01 else 'inconsistent'} across batch")
148
+
149
+
150
+ # ═══════════════════════════════════════════════════════════════
151
+ # 3. FRICTION SIGNAL β€” SPECTRAL GAP CORRELATION
152
+ # ═══════════════════════════════════════════════════════════════
153
+
154
+ section("3. FRICTION vs SPECTRAL GAP CORRELATION")
155
+
156
+ # Generate matrices with varying spectral gaps
157
+ B = 512
158
+ A = make_symmetric(B, 4, device)
159
+ packet = conduit_solver(A)
160
+
161
+ # For each eigenvalue, compute minimum gap to neighbors
162
+ evals = packet.eigenvalues # (B, 4)
163
+ gaps = torch.zeros(B, 4, device=device)
164
+ for i in range(4):
165
+ diffs = (evals - evals[:, i:i+1]).abs()
166
+ diffs[:, i] = float('inf') # exclude self
167
+ gaps[:, i] = diffs.min(dim=-1).values
168
+
169
+ friction = packet.friction
170
+
171
+ # Correlation between gap and friction
172
+ # Theory: smaller gap β†’ higher friction (solver struggles more)
173
+ gap_flat = gaps.reshape(-1).cpu()
174
+ fric_flat = friction.reshape(-1).cpu()
175
+
176
+ # Remove inf/nan
177
+ valid = torch.isfinite(gap_flat) & torch.isfinite(fric_flat)
178
+ gap_v = gap_flat[valid]
179
+ fric_v = fric_flat[valid]
180
+
181
+ corr = torch.corrcoef(torch.stack([gap_v, fric_v]))[0, 1].item()
182
+
183
+ print(f" Samples: {B} matrices, {B*4} eigenvalues")
184
+ print(f" Gap range: [{gap_v.min():.4f}, {gap_v.max():.4f}]")
185
+ print(f" Friction range: [{fric_v.min():.2f}, {fric_v.max():.2f}]")
186
+ print(f" Correlation (gap vs friction): {corr:.4f}")
187
+ print(f" Expected: NEGATIVE (small gap β†’ high friction)")
188
+ print(f" VERDICT: {'CONFIRMED' if corr < -0.1 else 'WEAK' if corr < 0 else 'UNEXPECTED'}")
189
+
190
+ # Binned analysis
191
+ print(f"\n Binned friction by gap size:")
192
+ sorted_idx = gap_v.argsort()
193
+ n_bins = 5
194
+ bin_size = len(sorted_idx) // n_bins
195
+ for b in range(n_bins):
196
+ start = b * bin_size
197
+ end = (b + 1) * bin_size if b < n_bins - 1 else len(sorted_idx)
198
+ idx = sorted_idx[start:end]
199
+ print(f" Gap [{gap_v[idx].min():.3f}-{gap_v[idx].max():.3f}]: "
200
+ f"friction mean={fric_v[idx].mean():.2f} "
201
+ f"std={fric_v[idx].std():.2f}")
202
+
203
+
204
+ # ═══════════════════════════════════════════════════════════════
205
+ # 4. FRICTION β€” CONTROLLED GAP SWEEP
206
+ # ═══════════════════════════════════════════════════════════════
207
+
208
+ section("4. CONTROLLED GAP SWEEP")
209
+
210
+ # Sweep the gap between eigenvalues 2 and 3, keep others fixed
211
+ gaps_to_test = [0.001, 0.01, 0.05, 0.1, 0.5, 1.0, 2.0, 5.0]
212
+ print(f" Fixed eigenvalues: Ξ»β‚€=1.0, λ₁=3.0, λ₃=10.0")
213
+ print(f" Sweeping: Ξ»β‚‚ from 3.001 to 8.0 (gap to λ₁)\n")
214
+
215
+ print(f" {'Gap':>8s} {'Ξ»β‚‚':>6s} {'fric[0]':>8s} {'fric[1]':>8s} "
216
+ f"{'fric[2]':>8s} {'fric[3]':>8s} {'settle':>12s}")
217
+ print(f" {'-'*70}")
218
+
219
+ for gap in gaps_to_test:
220
+ evals = torch.tensor([1.0, 3.0, 3.0 + gap, 10.0], device=device)
221
+ A = make_with_eigenvalues(evals, B=32)
222
+ p = conduit_solver(A)
223
+
224
+ fric_mean = p.friction.mean(0)
225
+ settle_mean = p.settle.mean(0)
226
+
227
+ print(f" {gap:8.3f} {3.0+gap:6.3f} {fric_mean[0]:8.2f} {fric_mean[1]:8.2f} "
228
+ f"{fric_mean[2]:8.2f} {fric_mean[3]:8.2f} "
229
+ f"{settle_mean.tolist()}")
230
+
231
+ print(f"\n Expected: friction[1] and friction[2] spike as gap β†’ 0")
232
+
233
+
234
+ # ═══════════════════════════════════════════════════════════════
235
+ # 5. SETTLE TIME ANALYSIS
236
+ # ═══════════════════════════════════════════���═══════════════════
237
+
238
+ section("5. SETTLE TIME ANALYSIS")
239
+
240
+ B = 1024
241
+ A = make_symmetric(B, 4, device)
242
+ packet = conduit_solver(A)
243
+
244
+ settle = packet.settle
245
+ print(f" Samples: {B} matrices")
246
+ print(f"\n Settle distribution per root position:")
247
+ for i in range(4):
248
+ vals = settle[:, i]
249
+ print(f" Root {i}: mean={vals.mean():.2f} "
250
+ f"mode={vals.mode().values.item():.0f} "
251
+ f"min={vals.min():.0f} max={vals.max():.0f}")
252
+
253
+ # How often do all roots settle in 1 iteration?
254
+ all_settle_1 = (settle == 1.0).all(dim=-1).float().mean().item()
255
+ any_slow = (settle >= 3.0).any(dim=-1).float().mean().item()
256
+ print(f"\n All roots settle in 1 iter: {all_settle_1:.1%}")
257
+ print(f" Any root needs β‰₯3 iters: {any_slow:.1%}")
258
+ print(f" VERDICT: {'SPARSE' if all_settle_1 > 0.5 else 'DENSE'} settle signal at n=4")
259
+
260
+
261
+ # ═══════════════════════════════════════════════════════════════
262
+ # 6. EXTRACTION ORDER DETERMINISM
263
+ # ═══════════════════════════════════════════════════════════════
264
+
265
+ section("6. EXTRACTION ORDER DETERMINISM")
266
+
267
+ # Same matrix, same extraction order?
268
+ A_fixed = make_symmetric(1, 4, device).expand(32, -1, -1)
269
+ packet = conduit_solver(A_fixed)
270
+
271
+ orders = packet.extraction_order # (32, 4)
272
+ order_consistent = (orders == orders[0:1]).all().item()
273
+
274
+ print(f" Same matrix repeated 32 times")
275
+ print(f" Extraction order[0]: {orders[0].tolist()}")
276
+ print(f" All identical: {order_consistent}")
277
+
278
+ # Different matrices β€” does order vary?
279
+ A_varied = make_symmetric(64, 4, device)
280
+ packet2 = conduit_solver(A_varied)
281
+ unique_orders = packet2.extraction_order.unique(dim=0)
282
+ print(f"\n 64 different matrices")
283
+ print(f" Unique extraction orders: {len(unique_orders)}")
284
+ print(f" VERDICT: Order is {'deterministic' if order_consistent else 'NON-DETERMINISTIC'} "
285
+ f"for identical inputs, {'varies' if len(unique_orders) > 1 else 'fixed'} across inputs")
286
+
287
+
288
+ # ═══════════════════════════════════════════════════════════════
289
+ # 7. NEAR-DEGENERATE BEHAVIOR (Theorem 4 stress)
290
+ # ═══════════════════════════════════════════════════════════════
291
+
292
+ section("7. NEAR-DEGENERATE BEHAVIOR")
293
+
294
+ # Construct matrices where two eigenvalues approach each other
295
+ print(f" Two eigenvalues converging: Ξ»β‚‚ = 5.0, λ₃ = 5.0 + Ξ΅\n")
296
+ print(f" {'Ξ΅':>12s} {'fric[2]':>8s} {'fric[3]':>8s} {'settle[2]':>10s} "
297
+ f"{'settle[3]':>10s} {'refine_res':>10s} {'eval_err':>10s}")
298
+ print(f" {'-'*75}")
299
+
300
+ for eps in [1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 1e-7, 1e-10]:
301
+ evals = torch.tensor([1.0, 3.0, 5.0, 5.0 + eps], device=device)
302
+ A = make_with_eigenvalues(evals, B=16)
303
+ p = conduit_solver(A)
304
+
305
+ # Check eigenvalue recovery accuracy
306
+ recovered = p.eigenvalues
307
+ eval_err = (recovered.sort(dim=-1).values - evals.unsqueeze(0)).abs().max().item()
308
+
309
+ print(f" {eps:12.1e} {p.friction[:, 2].mean():8.2f} {p.friction[:, 3].mean():8.2f} "
310
+ f"{p.settle[:, 2].mean():10.1f} {p.settle[:, 3].mean():10.1f} "
311
+ f"{p.refinement_residual.mean():10.2e} {eval_err:10.2e}")
312
+
313
+ print(f"\n Expected: friction spikes, settle increases, eigenvalue error grows as Ξ΅ β†’ 0")
314
+
315
+
316
+ # ═══════════════════════════════════════════════════════════════
317
+ # 8. SIGN CANONICALIZATION (Theorem 5)
318
+ # ═══════════════════════════════════════════════════════════════
319
+
320
+ section("8. SIGN CANONICALIZATION")
321
+
322
+ A = make_symmetric(16, 4, device)
323
+ packet = conduit_solver(A)
324
+ V = packet.eigenvectors # (16, 4, 4) β€” sign-canonicalized
325
+
326
+ # Verify: largest absolute value per column is positive
327
+ for b in range(min(4, V.shape[0])):
328
+ for col in range(4):
329
+ v_col = V[b, :, col]
330
+ max_idx = v_col.abs().argmax()
331
+ max_val = v_col[max_idx].item()
332
+ ok = max_val > 0
333
+ if not ok:
334
+ print(f" FAIL: batch={b} col={col} max_val={max_val:.4f}")
335
+
336
+ # Test that sign flip of input produces same canonicalized output
337
+ A2 = A.clone()
338
+ packet2 = conduit_solver(A2)
339
+ V2 = packet2.eigenvectors
340
+
341
+ # Compare: should be identical since same matrix
342
+ v_match = torch.allclose(V, V2, atol=1e-5)
343
+
344
+ # Now test with slightly perturbed matrix β€” should be CLOSE but not identical
345
+ A3 = A + torch.randn_like(A) * 1e-6
346
+ A3 = (A3 + A3.transpose(-1, -2)) / 2
347
+ packet3 = conduit_solver(A3)
348
+ V3 = packet3.eigenvectors
349
+
350
+ # Measure stability: how much do eigenvectors change under tiny perturbation?
351
+ v_drift = (V - V3).pow(2).sum((-2, -1)).sqrt().mean().item()
352
+
353
+ print(f" Canonicalization preserves positive max entry: {v_match}")
354
+ print(f" Eigenvector drift under 1e-6 perturbation: {v_drift:.2e}")
355
+ print(f" VERDICT: Canonicalization {'STABLE' if v_drift < 0.01 else 'UNSTABLE'}")
356
+
357
+ # Edge case: near-degenerate eigenvalues β€” gauge instability?
358
+ evals_degen = torch.tensor([1.0, 3.0, 5.0, 5.001], device=device)
359
+ A_degen = make_with_eigenvalues(evals_degen, B=32)
360
+ p_degen = conduit_solver(A_degen)
361
+ V_degen = p_degen.eigenvectors
362
+
363
+ # How consistent are eigenvectors across batch for same eigenvalues?
364
+ # (Different random orthogonal bases, so eigenvectors differ)
365
+ # But the CANONICAL sign should be consistent per-column
366
+ max_entries = V_degen.abs().argmax(dim=-2) # (32, 4) β€” which row has max
367
+ max_entry_consistent = (max_entries == max_entries[0:1]).all(dim=0)
368
+ print(f"\n Near-degenerate (gap=0.001):")
369
+ print(f" Max-entry row consistent: {max_entry_consistent.tolist()}")
370
+ print(f" CONCERN: Degenerate columns may have inconsistent gauge")
371
+
372
+
373
+ # ═══════════════════════════════════════════════════════════════
374
+ # 9. REFINEMENT RESIDUAL ANALYSIS
375
+ # ═══════════════════════════════════════════════════════════════
376
+
377
+ section("9. REFINEMENT RESIDUAL")
378
+
379
+ B = 1024
380
+ A = make_symmetric(B, 4, device)
381
+ packet = conduit_solver(A)
382
+ rr = packet.refinement_residual
383
+
384
+ print(f" Samples: {B}")
385
+ print(f" Mean: {rr.mean():.2e}")
386
+ print(f" Std: {rr.std():.2e}")
387
+ print(f" Min: {rr.min():.2e}")
388
+ print(f" Max: {rr.max():.2e}")
389
+ print(f" < 1e-6: {(rr < 1e-6).float().mean():.1%}")
390
+ print(f" < 1e-4: {(rr < 1e-4).float().mean():.1%}")
391
+ print(f" VERDICT: {'UNIFORMLY TINY' if rr.max() < 1e-4 else 'HAS VARIATION'} "
392
+ f"β€” {'no discriminative signal' if rr.max() < 1e-4 else 'may carry signal'}")
393
+
394
+
395
+ # ═══════════════════════════════════════════════════════════════
396
+ # 10. STATIC RECONSTRUCTION TEST (Theorem 2 β€” static side)
397
+ # ═══════════════════════════════════════════════════════════════
398
+
399
+ section("10. STATIC RECONSTRUCTION β€” char_coeffs from eigenvalues")
400
+
401
+ B = 128
402
+ A = make_symmetric(B, 4, device)
403
+ packet = FLEighConduit(research=True).to(device)(A)
404
+
405
+ evals = packet.eigenvalues # (B, 4)
406
+ coeffs = packet.char_coeffs # (B, 4)
407
+
408
+ # The char poly of the SCALED matrix has coefficients that are elementary
409
+ # symmetric polynomials. Since FLEigh scales by ||A||/sqrt(n), we can
410
+ # verify the STRUCTURE rather than exact values.
411
+ #
412
+ # For the original (unscaled) eigenvalues, the elementary symmetric polys are:
413
+ # e1 = Σλᡒ, e2 = Ξ£α΅’<β±Ό Ξ»α΅’Ξ»β±Ό, e3 = Ξ£α΅’<β±Ό<β‚– Ξ»α΅’Ξ»β±ΌΞ»β‚–, e4 = Ξ Ξ»α΅’
414
+ # char poly: t⁴ - e1·t³ + e2·t² - e3·t + e4
415
+
416
+ # Verify Mstore is reconstructible from (Ξ», V)
417
+ evals_d = evals.double()
418
+ V = packet.eigenvectors.double()
419
+ Mstore = packet.mstore # (5, B, 4, 4) β€” research mode
420
+
421
+ # Reconstruct Mstore[k] from V, Ξ›, c
422
+ # Mstore[k] = Ξ£β±Ό c[n-j] Β· A^j (up to index mapping)
423
+ # Since A = V diag(Ξ») V^T, A^j = V diag(Ξ»^j) V^T
424
+ # So Mstore[k] is reconstructible from (Ξ», V)
425
+
426
+ A_d = A.double()
427
+ recon_errors = []
428
+ for k in range(1, 5):
429
+ Mk_actual = Mstore[k].to(device)
430
+ # Reconstruct via A^j: Mstore[k] = A*Mstore[k-1] + c[n-k+1]*I
431
+ # We just check if V * f(Ξ›) * V^T matches, for SOME f
432
+ # The simplest check: project Mstore[k] into eigenbasis
433
+ Mk_eigenbasis = torch.bmm(torch.bmm(V.transpose(-1, -2), Mk_actual), V)
434
+ # Should be diagonal (since Mstore[k] = polynomial in A)
435
+ off_diag = Mk_eigenbasis - torch.diag_embed(Mk_eigenbasis.diagonal(dim1=-2, dim2=-1))
436
+ off_diag_norm = off_diag.pow(2).sum((-2, -1)).sqrt().mean().item()
437
+ diag_norm = Mk_eigenbasis.diagonal(dim1=-2, dim2=-1).pow(2).sum(-1).sqrt().mean().item()
438
+ recon_errors.append(off_diag_norm)
439
+ print(f" Mstore[{k}]: off-diag norm = {off_diag_norm:.2e}, "
440
+ f"diag norm = {diag_norm:.2e}, "
441
+ f"ratio = {off_diag_norm / (diag_norm + 1e-10):.2e}")
442
+
443
+ print(f"\n VERDICT: Mstore IS diagonal in eigenbasis β†’ "
444
+ f"{'CONFIRMED reconstructible from (Ξ»,V)' if max(recon_errors) < 1e-4 else 'UNEXPECTED'}")
445
+
446
+
447
+ # ═══════════════════════════════════════════════════════════════
448
+ # 11. DYNAMIC NON-RECONSTRUCTIBILITY (Theorem 2 β€” dynamic side)
449
+ # ═════════════════════════════════════════════��═════════════════
450
+
451
+ section("11. DYNAMIC NON-RECONSTRUCTIBILITY")
452
+
453
+ # Key test: can we predict friction from eigenvalues alone?
454
+ B = 2048
455
+ A = make_symmetric(B, 4, device)
456
+ packet = conduit_solver(A)
457
+
458
+ evals = packet.eigenvalues
459
+ friction = packet.friction
460
+
461
+ # Compute the "static prediction" of friction:
462
+ # p'(λᡒ) = Π_{j≠i} (λᡒ - λⱼ)
463
+ # The static friction proxy would be 5 / (|p'(Ξ»α΅’)| + Ξ΄)
464
+ # (5 iterations, each contributing ~1/|p'| if converged)
465
+ static_proxy = torch.zeros_like(friction)
466
+ for i in range(4):
467
+ dp = torch.ones(B, device=device)
468
+ for j in range(4):
469
+ if j != i:
470
+ dp = dp * (evals[:, i] - evals[:, j])
471
+ static_proxy[:, i] = 5.0 / (dp.abs() + 1e-8)
472
+
473
+ # How well does the static proxy predict actual friction?
474
+ for i in range(4):
475
+ corr = torch.corrcoef(
476
+ torch.stack([friction[:, i].cpu(), static_proxy[:, i].cpu()])
477
+ )[0, 1].item()
478
+ residual = (friction[:, i] - static_proxy[:, i]).abs()
479
+ print(f" Root {i}: corr(actual, static_proxy) = {corr:.4f}, "
480
+ f"residual mean = {residual.mean():.2f}, "
481
+ f"residual std = {residual.std():.2f}")
482
+
483
+ # The residual between actual and static proxy is the DYNAMIC EXCESS
484
+ # If this residual is non-trivial, friction carries information beyond eigenvalues
485
+ total_var = friction.var().item()
486
+ proxy_var = static_proxy.var().item()
487
+ residual_var = (friction - static_proxy).var().item()
488
+ print(f"\n Total friction variance: {total_var:.4f}")
489
+ print(f" Static proxy variance: {proxy_var:.4f}")
490
+ print(f" Residual (dynamic) variance: {residual_var:.4f}")
491
+ print(f" Dynamic fraction: {residual_var / (total_var + 1e-10):.1%}")
492
+ print(f"\n VERDICT: Dynamic excess is "
493
+ f"{'SIGNIFICANT' if residual_var / (total_var + 1e-10) > 0.05 else 'NEGLIGIBLE'}")
494
+
495
+
496
+ # ═══════════════════════════════════════════════════════════════
497
+ # 12. DIMENSION AGNOSTIC SCALING
498
+ # ═══════════════════════════════════════════════════════════════
499
+
500
+ section("12. DIMENSION AGNOSTIC SCALING")
501
+
502
+ solver = FLEighConduit().to(device)
503
+
504
+ for n in [3, 4, 5, 6, 8]:
505
+ B = 64
506
+ A = make_symmetric(B, n, device)
507
+ t0 = time.time()
508
+ packet = solver(A)
509
+ elapsed = time.time() - t0
510
+
511
+ parity = verify_parity(A, atol=1e-4)
512
+ fric_range = (packet.friction.min().item(), packet.friction.max().item())
513
+ settle_range = (packet.settle.min().item(), packet.settle.max().item())
514
+
515
+ print(f" n={n}: packet OK, parity={parity}, "
516
+ f"friction=[{fric_range[0]:.1f}, {fric_range[1]:.1f}], "
517
+ f"settle=[{settle_range[0]:.0f}, {settle_range[1]:.0f}], "
518
+ f"time={elapsed*1000:.1f}ms")
519
+
520
+ print(f"\n VERDICT: Scales cleanly across dimensions")
521
+
522
+
523
+ # ═══════════════════════════════════════════════════════════════
524
+ # 13. RESEARCH MODE β€” Mstore & Trajectory
525
+ # ═══════════════════════════════════════════════════════════════
526
+
527
+ section("13. RESEARCH MODE")
528
+
529
+ solver_r = FLEighConduit(research=True).to(device)
530
+ A = make_symmetric(8, 4, device)
531
+ packet = solver_r(A)
532
+
533
+ print(f" Mstore shape: {packet.mstore.shape}")
534
+ print(f" z_trajectory shape: {packet.z_trajectory.shape}")
535
+ print(f" dp_trajectory shape: {packet.dp_trajectory.shape}")
536
+
537
+ # Inspect one patch's Laguerre trajectory
538
+ print(f"\n Laguerre trajectory for patch 0:")
539
+ for ri in range(4):
540
+ z_path = packet.z_trajectory[0, ri].tolist()
541
+ dp_path = packet.dp_trajectory[0, ri].tolist()
542
+ final_eval = packet.eigenvalues[0, ri].item()
543
+ print(f" Root {ri} (final Ξ»={final_eval:.4f}):")
544
+ for t in range(5):
545
+ print(f" iter {t}: z={z_path[t]:8.4f} |p'|={dp_path[t]:10.4f}")
546
+
547
+ # Mstore progression
548
+ print(f"\n Mstore diagonal progression for patch 0:")
549
+ for k in range(1, 5):
550
+ diag = packet.mstore[k, 0].diagonal().tolist()
551
+ print(f" Mstore[{k}] diag: [{', '.join(f'{v:.4f}' for v in diag)}]")
552
+
553
+
554
+ # ═══════════════════════════════════════════════════════════════
555
+ # 14. FRECKLES CIFAR-10 β€” CLASS DISCRIMINABILITY
556
+ # ═══════════════════════════════════════════════════════════════
557
+
558
+ section("14. FRECKLES CIFAR-10 β€” CLASS DISCRIMINABILITY")
559
+
560
+ print(" Loading Freckles v40 and CIFAR-10...")
561
+ try:
562
+ from geolip_svae import load_model
563
+ import torchvision
564
+ import torchvision.transforms as T
565
+
566
+ freckles, cfg = load_model(hf_version='v40_freckles_noise', device=device)
567
+ freckles.eval()
568
+
569
+ transform = T.Compose([T.Resize(64), T.ToTensor()])
570
+ cifar = torchvision.datasets.CIFAR10(
571
+ root='/content/data', train=False, download=True, transform=transform)
572
+ loader = torch.utils.data.DataLoader(cifar, batch_size=64, shuffle=False)
573
+
574
+ CLASSES = ['airplane', 'auto', 'bird', 'cat', 'deer',
575
+ 'dog', 'frog', 'horse', 'ship', 'truck']
576
+
577
+ # Collect conduit telemetry per class
578
+ class_friction = {c: [] for c in range(10)}
579
+ class_settle = {c: [] for c in range(10)}
580
+
581
+ conduit = FLEighConduit().to(device)
582
+
583
+ n_batches = 10 # quick pass
584
+ for batch_idx, (images, labels) in enumerate(loader):
585
+ if batch_idx >= n_batches:
586
+ break
587
+
588
+ with torch.no_grad():
589
+ out = freckles(images.to(device))
590
+ # Get the Gram matrices from the SVD
591
+ # enc_out is (B, N, V, D) β†’ Gram is (B*N, D, D)
592
+ S = out['svd']['S'] # (B, N, D)
593
+ B_img, N, D = S.shape
594
+
595
+ # Build Gram matrices from the enc_out
596
+ # The SVAE computes G = M^T M where M is enc_out reshaped
597
+ # For conduit testing, we construct symmetric matrices from S
598
+ # Actually, we need the Gram matrix that FLEigh decomposed.
599
+ # S^2 are the eigenvalues of G. We can construct G = Vt^T diag(S^2) Vt
600
+ Vt = out['svd']['Vt'] # (B, N, D, D)
601
+ S2 = S.pow(2) # eigenvalues of Gram matrix
602
+
603
+ G = torch.einsum('bnij,bnj,bnjk->bnik',
604
+ Vt.transpose(-2, -1), S2, Vt)
605
+ # G: (B, N, D, D) β€” the Gram matrices
606
+
607
+ G_flat = G.reshape(B_img * N, D, D)
608
+ packet = conduit(G_flat)
609
+
610
+ # Reshape friction back to (B, N, D)
611
+ fric = packet.friction.reshape(B_img, N, D)
612
+ sett = packet.settle.reshape(B_img, N, D)
613
+
614
+ for c in range(10):
615
+ mask = labels == c
616
+ if mask.sum() > 0:
617
+ class_friction[c].append(fric[mask].cpu())
618
+ class_settle[c].append(sett[mask].cpu())
619
+
620
+ # Analyze
621
+ print(f"\n Per-class friction statistics (mean across patches):\n")
622
+ print(f" {'Class':<10s} {'fric_mean':>10s} {'fric_std':>10s} "
623
+ f"{'settle_mean':>12s}")
624
+ print(f" {'-'*44}")
625
+
626
+ class_fric_means = []
627
+ for c in range(10):
628
+ if class_friction[c]:
629
+ fric_cat = torch.cat(class_friction[c])
630
+ sett_cat = torch.cat(class_settle[c])
631
+ fm = fric_cat.mean().item()
632
+ fs = fric_cat.std().item()
633
+ sm = sett_cat.mean().item()
634
+ class_fric_means.append(fm)
635
+ print(f" {CLASSES[c]:<10s} {fm:10.2f} {fs:10.2f} {sm:12.2f}")
636
+
637
+ if class_fric_means:
638
+ spread = max(class_fric_means) - min(class_fric_means)
639
+ mean_fric = np.mean(class_fric_means)
640
+ print(f"\n Inter-class friction spread: {spread:.2f}")
641
+ print(f" Mean friction: {mean_fric:.2f}")
642
+ print(f" Spread/Mean ratio: {spread/mean_fric:.2%}")
643
+ print(f"\n VERDICT: {'CLASS-DISCRIMINATIVE' if spread/mean_fric > 0.05 else 'NOT DISCRIMINATIVE'} "
644
+ f"friction signal")
645
+
646
+ except ImportError as e:
647
+ print(f" SKIPPED β€” missing dependency: {e}")
648
+ except Exception as e:
649
+ print(f" SKIPPED β€” error: {e}")
650
+
651
+
652
+ # ═══════════════════════════════════════════════════════════════
653
+ # SUMMARY
654
+ # ═══════════════════════════════════════════════════════════════
655
+
656
+ section("SUMMARY β€” ALL TESTS COMPLETE")
657
+ print(" Review each section's VERDICT above.")
658
+ print(" Key questions answered:")
659
+ print(" 1. Does FLEighConduit match FLEigh?")
660
+ print(" 2. Does friction correlate with spectral gaps?")
661
+ print(" 3. Does friction spike at near-degeneracy?")
662
+ print(" 4. Is the dynamic signal non-trivial at n=4?")
663
+ print(" 5. Are static conduits reconstructible from eigenvalues?")
664
+ print(" 6. Is sign canonicalization stable?")
665
+ print(" 7. Does friction differ across CIFAR-10 classes?")
666
+ print(" 8. Is refinement residual uniformly tiny?")
667
+ print(" 9. Does settle time carry signal?")
668
+ print(" 10. Does the system scale to higher n?")