9x25dillon commited on
Commit
4867b06
·
verified ·
1 Parent(s): 3ea65b2

Create QaBCrI.py

Browse files
Files changed (1) hide show
  1. QaBCrI.py +698 -0
QaBCrI.py ADDED
@@ -0,0 +1,698 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Adaptive Bi‑Coupled Coherence Recovery (ABCR) — Regenerated v2
3
+
4
+ What’s new in v2 (integrated + implied fixes)
5
+ - Percentile‑based significant‑position detection with absolute floor (noise‑hardening)
6
+ - Mode‑aware significance percentiles (tunable per SystemMode)
7
+ - Safer math (no SciPy dependency; custom sigmoid)
8
+ - Guarded audits (no div/empty issues), cleaner logging
9
+ - End‑to‑end demo + PNG/JSON export
10
+ """
11
+
12
+ import numpy as np
13
+ from dataclasses import dataclass, field
14
+ from typing import Dict, List, Tuple, Optional, Any
15
+ from enum import Enum
16
+ import json
17
+ from datetime import datetime
18
+ import logging
19
+ import matplotlib.pyplot as plt
20
+
21
+ # ================================ LOGGING ================================
22
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
23
+ logger = logging.getLogger("ABCR")
24
+
25
+ # ================================ ENUMS ================================
26
+ class FrequencyBand(Enum):
27
+ DELTA = 'delta'
28
+ THETA = 'theta'
29
+ ALPHA = 'alpha'
30
+ BETA = 'beta'
31
+ GAMMA = 'gamma'
32
+
33
+ class StreamType(Enum):
34
+ STREAM_A = "Stream A: Hypo-coherence"
35
+ STREAM_B = "Stream B: Hyper-coherence"
36
+
37
+ class SeamType(Enum):
38
+ TYPE_I = "Type I: Perfect Recovery"
39
+ TYPE_II = "Type II: Acceptable Loss"
40
+ TYPE_III = "Type III: Failed Recovery"
41
+
42
+ class SystemMode(Enum):
43
+ STANDARD = "standard"
44
+ HIGH_SENSITIVITY = "high_sensitivity"
45
+ STABILITY = "stability"
46
+ RECOVERY = "recovery"
47
+ ADAPTIVE = "adaptive"
48
+
49
+ class ChainState(Enum):
50
+ HYPO = "hypo-coherent"
51
+ HYPER = "hyper-coherent"
52
+ INTACT = "intact"
53
+
54
+ # ================================ DATACLASSES ================================
55
+ @dataclass
56
+ class SpatialPosition:
57
+ x: float
58
+ y: float
59
+ m: int
60
+ n: int
61
+
62
+ def distance_to(self, other: 'SpatialPosition') -> float:
63
+ return np.hypot(self.x - other.x, self.y - other.y)
64
+
65
+ def radius(self) -> float:
66
+ return np.hypot(self.x, self.y)
67
+
68
+ @dataclass
69
+ class ChainComponent:
70
+ band: FrequencyBand
71
+ positions: List[SpatialPosition]
72
+ coherence: float
73
+ phase_std: float
74
+ state: ChainState
75
+ stream: StreamType
76
+
77
+ @dataclass
78
+ class DualAuditResult:
79
+ delta_kappa_A: float
80
+ s_A: float
81
+ delta_kappa_B: float
82
+ s_B: float
83
+ s_composite: float
84
+ tau_R: float
85
+ D_C: float
86
+ D_omega: float
87
+ R: float
88
+ I: float
89
+ seam_type: SeamType
90
+ audit_pass: bool
91
+ active_streams: List[StreamType]
92
+ details: Dict[str, Any] = field(default_factory=dict)
93
+
94
+ @dataclass
95
+ class AdaptiveThresholds:
96
+ tau_low: float
97
+ tau_high: float
98
+ tau_phase: float
99
+ alpha: float
100
+ stress: float
101
+ mode: SystemMode
102
+
103
+ # ================================ CONFIG ================================
104
+ class ABCRConfig:
105
+ SPATIAL_GRID_M = 8
106
+ SPATIAL_GRID_N = 8
107
+ SPATIAL_UNIT = 0.1
108
+ PROPAGATION_SPEED = 1.0
109
+
110
+ # Base coherence gates
111
+ TAU_BASE = 0.3
112
+ TAU_PHASE = 0.5
113
+
114
+ # Mode params
115
+ MODE_PARAMS = {
116
+ SystemMode.STANDARD: {'alpha_base': 0.60, 'alpha_mod': 0.10, 'rho': 0.70, 'novelty': 0.10, 'baseline': 0.60},
117
+ SystemMode.HIGH_SENSITIVITY:{'alpha_base': 0.65, 'alpha_mod': 0.15, 'rho': 0.60, 'novelty': 0.12, 'baseline': 0.65, 'tau_low_factor': 0.8, 'tau_high_factor': 1.2},
118
+ SystemMode.STABILITY: {'alpha_base': 0.50, 'alpha_mod': 0.05, 'rho': 0.80, 'novelty': 0.05, 'baseline': 0.50},
119
+ SystemMode.RECOVERY: {'alpha_base': 0.65, 'alpha_mod': 0.15, 'rho': 0.60, 'novelty': 0.15, 'baseline': 0.70},
120
+ SystemMode.ADAPTIVE: {'alpha_base': 0.60, 'alpha_mod': 0.12, 'rho': 0.65, 'novelty': 0.12, 'baseline': 0.60},
121
+ }
122
+
123
+ # Significance percentile per mode (for amplitude map) + absolute floor
124
+ MODE_PERCENTILES = {
125
+ SystemMode.STANDARD: 92.0,
126
+ SystemMode.HIGH_SENSITIVITY: 85.0,
127
+ SystemMode.STABILITY: 96.0,
128
+ SystemMode.RECOVERY: 90.0,
129
+ SystemMode.ADAPTIVE: 92.0,
130
+ }
131
+ ABS_NOISE_FLOOR = 1e-3
132
+
133
+ # Coupling
134
+ LAMBDA_CROSS_STREAM = 0.3
135
+
136
+ # Audit
137
+ AUDIT_TOLERANCE = 0.01
138
+ TYPE_I_THRESHOLD = 1e-6
139
+
140
+ # Emergency
141
+ EMERGENCY_HYPO_THRESHOLD = 0.10
142
+ EMERGENCY_HYPER_THRESHOLD = 0.90
143
+
144
+ # Reconstruction
145
+ MAX_RECONSTRUCTION_ITERATIONS = 100
146
+ CONVERGENCE_TOLERANCE = 1e-3
147
+
148
+ # Frequencies
149
+ BAND_FREQUENCIES = {
150
+ FrequencyBand.DELTA: 2.0,
151
+ FrequencyBand.THETA: 6.0,
152
+ FrequencyBand.ALPHA: 10.0,
153
+ FrequencyBand.BETA: 20.0,
154
+ FrequencyBand.GAMMA: 40.0,
155
+ }
156
+
157
+ # ================================ UTILS ================================
158
+
159
+ def sigmoid(x: np.ndarray) -> np.ndarray:
160
+ # numerically safer sigmoid
161
+ x = np.clip(x, -60, 60)
162
+ return 1.0 / (1.0 + np.exp(-x))
163
+
164
+ # ================================ ENCODER ================================
165
+ class DualStreamEncoder:
166
+ def __init__(self, M: int = ABCRConfig.SPATIAL_GRID_M, N: int = ABCRConfig.SPATIAL_GRID_N):
167
+ self.M = M
168
+ self.N = N
169
+ self.positions = self._init_positions()
170
+ self._spatial_cache: Dict[Any, float] = {}
171
+
172
+ def _init_positions(self) -> List[SpatialPosition]:
173
+ pos = []
174
+ for m in range(-self.M, self.M + 1):
175
+ for n in range(-self.N, self.N + 1):
176
+ pos.append(SpatialPosition(m * ABCRConfig.SPATIAL_UNIT, n * ABCRConfig.SPATIAL_UNIT, m, n))
177
+ return pos
178
+
179
+ def _k(self, band: FrequencyBand) -> float:
180
+ return 2 * np.pi * ABCRConfig.BAND_FREQUENCIES[band] / ABCRConfig.PROPAGATION_SPEED
181
+
182
+ def encode_forward(self, kappa: Dict[FrequencyBand, float], phi: Dict[FrequencyBand, float]) -> np.ndarray:
183
+ B = len(FrequencyBand)
184
+ C = np.zeros((2*self.M+1, 2*self.N+1, B), dtype=complex)
185
+ for p in self.positions:
186
+ r = p.radius()
187
+ G = np.exp(-r / (self.M * ABCRConfig.SPATIAL_UNIT))
188
+ for b_idx, band in enumerate(FrequencyBand):
189
+ total_phase = phi[band] - self._k(band) * r
190
+ C[p.m + self.M, p.n + self.N, b_idx] = G * kappa[band] * np.exp(1j * total_phase)
191
+ return C
192
+
193
+ def encode_mirror(self, kappa: Dict[FrequencyBand, float], phi: Dict[FrequencyBand, float]) -> np.ndarray:
194
+ B = len(FrequencyBand)
195
+ C = np.zeros((2*self.M+1, 2*self.N+1, B), dtype=complex)
196
+ for p in self.positions:
197
+ r = p.radius()
198
+ G = np.exp(-r / (self.M * ABCRConfig.SPATIAL_UNIT))
199
+ for b_idx, band in enumerate(FrequencyBand):
200
+ total_phase = (np.pi - phi[band]) + self._k(band) * r
201
+ C[p.m + self.M, p.n + self.N, b_idx] = G * (1.0 - kappa[band]) * np.exp(1j * total_phase)
202
+ return C
203
+
204
+ def spatial_coupling(self, p1: SpatialPosition, p2: SpatialPosition, b1: FrequencyBand, b2: FrequencyBand) -> float:
205
+ key = (p1.m, p1.n, p2.m, p2.n, b1.value, b2.value)
206
+ if key in self._spatial_cache:
207
+ return self._spatial_cache[key]
208
+ d = p1.distance_to(p2)
209
+ spatial = np.exp(-d / ABCRConfig.SPATIAL_UNIT)
210
+ fdiff = abs(ABCRConfig.BAND_FREQUENCIES[b1] - ABCRConfig.BAND_FREQUENCIES[b2])
211
+ freq_fac = np.exp(-fdiff / 10.0)
212
+ val = float(spatial * freq_fac)
213
+ self._spatial_cache[key] = val
214
+ return val
215
+
216
+ # ================================ THRESHOLDS ================================
217
+ class AdaptiveThresholdManager:
218
+ def compute_stress(self, kappa: Dict[FrequencyBand, float], history: List[Dict[FrequencyBand, float]]) -> float:
219
+ if history:
220
+ prev = history[-1]
221
+ num = sum(abs(kappa[b] - prev[b]) for b in FrequencyBand)
222
+ den = sum(kappa[b] for b in FrequencyBand) + 1e-9
223
+ s = min(1.0, num / den)
224
+ else:
225
+ bal = 0.5
226
+ s = np.mean([abs(k - bal) for k in kappa.values()]) * 2.0
227
+ return float(np.clip(s, 0, 1))
228
+
229
+ def compute(self, stress: float, mode: SystemMode) -> AdaptiveThresholds:
230
+ p = ABCRConfig.MODE_PARAMS[mode]
231
+ tau_low = ABCRConfig.TAU_BASE * (1 - 0.3 * stress)
232
+ tau_high = 1 - ABCRConfig.TAU_BASE * (1 - 0.3 * stress)
233
+ if mode == SystemMode.HIGH_SENSITIVITY:
234
+ tau_low *= p.get('tau_low_factor', 1.0)
235
+ tau_high *= p.get('tau_high_factor', 1.0)
236
+ elif mode == SystemMode.STABILITY:
237
+ tau_low *= 1.1
238
+ tau_high *= 0.9
239
+ alpha = np.clip(p['alpha_base'] + p['alpha_mod'] * stress, 0.3, 0.8)
240
+ tau_phase = ABCRConfig.TAU_PHASE * (1 + 0.1 * stress)
241
+ return AdaptiveThresholds(
242
+ tau_low=float(np.clip(tau_low, 0.1, 0.5)),
243
+ tau_high=float(np.clip(tau_high, 0.5, 0.9)),
244
+ tau_phase=float(tau_phase),
245
+ alpha=float(alpha),
246
+ stress=float(stress),
247
+ mode=mode,
248
+ )
249
+
250
+ # ================================ PROCESSOR ================================
251
+ class DualStreamProcessor:
252
+ def __init__(self, encoder: DualStreamEncoder, mode: SystemMode):
253
+ self.encoder = encoder
254
+ self.mode = mode
255
+
256
+ def _phase_coherence(self, C: np.ndarray, b_idx: int) -> float:
257
+ band_slice = C[:, :, b_idx]
258
+ mask = np.abs(band_slice) > 1e-9
259
+ if not np.any(mask):
260
+ return 0.0
261
+ phases = np.angle(band_slice[mask])
262
+ mean_vec = np.mean(np.exp(1j * phases))
263
+ return float(np.abs(mean_vec))
264
+
265
+ def _significant_positions(self, C: np.ndarray, b_idx: int) -> List[SpatialPosition]:
266
+ # --- Corrected logic: percentile + absolute floor, mode-aware ---
267
+ amps = np.abs(C[:, :, b_idx]).ravel()
268
+ perc = ABCRConfig.MODE_PERCENTILES.get(self.mode, 92.0)
269
+ # Avoid empty or all-zeros
270
+ if amps.size == 0:
271
+ thr = ABCRConfig.ABS_NOISE_FLOOR
272
+ else:
273
+ thr = max(np.percentile(amps, perc), ABCRConfig.ABS_NOISE_FLOOR)
274
+ positions = []
275
+ for p in self.encoder.positions:
276
+ a = np.abs(C[p.m + self.encoder.M, p.n + self.encoder.N, b_idx])
277
+ if a >= thr:
278
+ positions.append(p)
279
+ return positions
280
+
281
+ def detect_broken(self, kappa: Dict[FrequencyBand, float], C_F: np.ndarray, C_M: np.ndarray, thr: AdaptiveThresholds
282
+ ) -> Tuple[List[ChainComponent], List[ChainComponent], Dict[FrequencyBand, float]]:
283
+ broken_A: List[ChainComponent] = []
284
+ broken_B: List[ChainComponent] = []
285
+ intact: Dict[FrequencyBand, float] = {}
286
+ for b_idx, band in enumerate(FrequencyBand):
287
+ kb = kappa[band]
288
+ if kb < thr.tau_low: # hypo side
289
+ ph = self._phase_coherence(C_F, b_idx)
290
+ if ph < thr.tau_phase:
291
+ comp = ChainComponent(
292
+ band=band,
293
+ positions=self._significant_positions(C_F, b_idx),
294
+ coherence=kb,
295
+ phase_std=float(np.sqrt(max(0.0, 1 - ph))),
296
+ state=ChainState.HYPO,
297
+ stream=StreamType.STREAM_A,
298
+ )
299
+ broken_A.append(comp)
300
+ else:
301
+ intact[band] = kb
302
+ elif kb > thr.tau_high: # hyper side
303
+ ph = self._phase_coherence(C_M, b_idx)
304
+ if ph < thr.tau_phase:
305
+ comp = ChainComponent(
306
+ band=band,
307
+ positions=self._significant_positions(C_M, b_idx),
308
+ coherence=kb,
309
+ phase_std=float(np.sqrt(max(0.0, 1 - ph))),
310
+ state=ChainState.HYPER,
311
+ stream=StreamType.STREAM_B,
312
+ )
313
+ broken_B.append(comp)
314
+ else:
315
+ intact[band] = kb
316
+ else:
317
+ intact[band] = kb
318
+ return broken_A, broken_B, intact
319
+
320
+ # ================================ RECONSTRUCTOR ================================
321
+ class BiCoupledReconstructor:
322
+ def __init__(self, encoder: DualStreamEncoder):
323
+ self.encoder = encoder
324
+ self.H_A: Dict[FrequencyBand, complex] = {}
325
+ self.H_B: Dict[FrequencyBand, complex] = {}
326
+
327
+ def compute_hamiltonians(self, broken_A: List[ChainComponent], broken_B: List[ChainComponent],
328
+ intact: Dict[FrequencyBand, float], C_F: np.ndarray, C_M: np.ndarray) -> None:
329
+ # Stream A (hypo)
330
+ for comp in broken_A:
331
+ b = comp.band
332
+ b_idx = list(FrequencyBand).index(b)
333
+ hF = 0+0j
334
+ hM = 0+0j
335
+ for p in comp.positions:
336
+ hF += C_F[p.m + self.encoder.M, p.n + self.encoder.N, b_idx]
337
+ hM += C_M[p.m + self.encoder.M, p.n + self.encoder.N, b_idx]
338
+ J = 0.0
339
+ for intact_band, val in intact.items():
340
+ for p1 in comp.positions:
341
+ for p2 in self.encoder.positions:
342
+ J += self.encoder.spatial_coupling(p1, p2, b, intact_band) * val
343
+ self.H_A[b] = hF + ABCRConfig.LAMBDA_CROSS_STREAM * hM + J
344
+ # Stream B (hyper)
345
+ for comp in broken_B:
346
+ b = comp.band
347
+ b_idx = list(FrequencyBand).index(b)
348
+ hM = 0+0j
349
+ hF = 0+0j
350
+ for p in comp.positions:
351
+ hM += C_M[p.m + self.encoder.M, p.n + self.encoder.N, b_idx]
352
+ hF += C_F[p.m + self.encoder.M, p.n + self.encoder.N, b_idx]
353
+ J = 0.0
354
+ for intact_band, val in intact.items():
355
+ for p1 in comp.positions:
356
+ for p2 in self.encoder.positions:
357
+ J += self.encoder.spatial_coupling(p1, p2, b, intact_band) * (1 - val)
358
+ self.H_B[b] = hM + ABCRConfig.LAMBDA_CROSS_STREAM * hF + J
359
+
360
+ def reconstruct(self, broken_A: List[ChainComponent], broken_B: List[ChainComponent],
361
+ intact: Dict[FrequencyBand, float]) -> Dict[FrequencyBand, float]:
362
+ kappa = intact.copy()
363
+ for comp in broken_A:
364
+ kappa[comp.band] = 0.30
365
+ for comp in broken_B:
366
+ kappa[comp.band] = 0.70
367
+ for it in range(ABCRConfig.MAX_RECONSTRUCTION_ITERATIONS):
368
+ conv = True
369
+ for comp in broken_A:
370
+ b = comp.band
371
+ field = np.abs(self.H_A.get(b, 0.0))
372
+ new = float(sigmoid(field))
373
+ if abs(new - kappa[b]) > ABCRConfig.CONVERGENCE_TOLERANCE:
374
+ conv = False
375
+ kappa[b] = new
376
+ for comp in broken_B:
377
+ b = comp.band
378
+ field = np.abs(self.H_B.get(b, 0.0))
379
+ new = float(1.0 - sigmoid(field))
380
+ if abs(new - kappa[b]) > ABCRConfig.CONVERGENCE_TOLERANCE:
381
+ conv = False
382
+ kappa[b] = new
383
+ if conv:
384
+ logger.info(f"Reconstruction converged in {it+1} iterations")
385
+ break
386
+ return kappa
387
+
388
+ # ================================ AUDITOR ================================
389
+ class DualStreamAuditor:
390
+ def _stream_delta(self, orig: Dict[FrequencyBand, float], rec: Dict[FrequencyBand, float], comps: List[ChainComponent]) -> float:
391
+ if not comps:
392
+ return 0.0
393
+ vals = [rec[c.band] - orig[c.band] for c in comps]
394
+ return float(np.mean(vals)) if vals else 0.0
395
+
396
+ def _curvature_change(self, orig: Dict[FrequencyBand, float], rec: Dict[FrequencyBand, float]) -> float:
397
+ o = np.array(list(orig.values()))
398
+ r = np.array(list(rec.values()))
399
+ if o.size >= 3:
400
+ oc = np.mean(np.abs(np.diff(o, n=2)))
401
+ rc = np.mean(np.abs(np.diff(r, n=2)))
402
+ return float(abs(rc - oc))
403
+ return 0.0
404
+
405
+ def _entropy_drift(self, orig: Dict[FrequencyBand, float], rec: Dict[FrequencyBand, float]) -> float:
406
+ e = np.array([rec[b] - orig[b] for b in FrequencyBand])
407
+ return float(np.std(e))
408
+
409
+ def _return_credit(self, orig: Dict[FrequencyBand, float], rec: Dict[FrequencyBand, float]) -> float:
410
+ ratios = []
411
+ for b in FrequencyBand:
412
+ if orig[b] > 0:
413
+ r = np.clip(rec[b] / (orig[b] + 1e-12), 0, 2)
414
+ ratios.append(1 - abs(1 - r))
415
+ return float(np.mean(ratios)) if ratios else 0.0
416
+
417
+ def audit(self, kappa_orig: Dict[FrequencyBand, float], kappa_rec: Dict[FrequencyBand, float],
418
+ broken_A: List[ChainComponent], broken_B: List[ChainComponent],
419
+ t0: float, t1: float) -> DualAuditResult:
420
+ dkA = self._stream_delta(kappa_orig, kappa_rec, broken_A)
421
+ dkB = self._stream_delta(kappa_orig, kappa_rec, broken_B)
422
+ tau_R = abs(t1 - t0)
423
+ D_C = self._curvature_change(kappa_orig, kappa_rec)
424
+ D_w = self._entropy_drift(kappa_orig, kappa_rec)
425
+ R = self._return_credit(kappa_orig, kappa_rec)
426
+ s_A = R * tau_R - (dkA + D_w + D_C) if broken_A else 0.0
427
+ s_B = R * tau_R - (dkB + D_w + D_C) if broken_B else 0.0
428
+ active = []
429
+ if broken_A: active.append(StreamType.STREAM_A)
430
+ if broken_B: active.append(StreamType.STREAM_B)
431
+ if broken_A and broken_B:
432
+ wA = len(broken_A) / (len(broken_A) + len(broken_B))
433
+ wB = 1 - wA
434
+ s_comp = wA * s_A + wB * s_B
435
+ elif broken_A:
436
+ s_comp = s_A
437
+ elif broken_B:
438
+ s_comp = s_B
439
+ else:
440
+ s_comp = 0.0
441
+ dk_avg = float(np.mean([kappa_rec[b] - kappa_orig[b] for b in FrequencyBand]))
442
+ if abs(s_comp) < ABCRConfig.AUDIT_TOLERANCE:
443
+ seam = SeamType.TYPE_I if abs(dk_avg) < ABCRConfig.TYPE_I_THRESHOLD else SeamType.TYPE_II
444
+ ok = True
445
+ else:
446
+ seam = SeamType.TYPE_III
447
+ ok = False
448
+ I = float(np.exp(np.mean(list(kappa_rec.values()))))
449
+ return DualAuditResult(
450
+ delta_kappa_A=dkA, s_A=s_A, delta_kappa_B=dkB, s_B=s_B,
451
+ s_composite=s_comp, tau_R=tau_R, D_C=D_C, D_omega=D_w, R=R, I=I,
452
+ seam_type=seam, audit_pass=ok, active_streams=active,
453
+ details={'delta_kappa_avg': dk_avg, 'broken_A_count': len(broken_A), 'broken_B_count': len(broken_B)}
454
+ )
455
+
456
+ # ================================ RENEWAL ================================
457
+ class AdaptiveRenewalEngine:
458
+ def __init__(self):
459
+ self.Pi: Optional[Dict[FrequencyBand, float]] = None
460
+ self.renewal_history: List[Dict[str, Any]] = []
461
+
462
+ def init_field(self, kappa0: Dict[FrequencyBand, float]):
463
+ self.Pi = kappa0.copy()
464
+ logger.info(f"Invariant field initialized (mean κ={np.mean(list(self.Pi.values())):.3f})")
465
+
466
+ def update_field(self, kappa: Dict[FrequencyBand, float], beta: float = 0.1):
467
+ if self.Pi is None:
468
+ self.init_field(kappa)
469
+ return
470
+ for b in FrequencyBand:
471
+ self.Pi[b] = (1 - beta) * self.Pi[b] + beta * kappa[b]
472
+
473
+ def renew(self, kappa_frag: Dict[FrequencyBand, float], mode: SystemMode) -> Dict[FrequencyBand, float]:
474
+ if self.Pi is None:
475
+ return kappa_frag
476
+ p = ABCRConfig.MODE_PARAMS[mode]
477
+ rho, novelty, base = p['rho'], p['novelty'], p['baseline']
478
+ out: Dict[FrequencyBand, float] = {}
479
+ for b in FrequencyBand:
480
+ xi = float(np.random.normal(0.0, novelty))
481
+ out[b] = float(np.clip(rho * self.Pi[b] + (1 - rho) * base + xi, 0.0, 1.0))
482
+ self.renewal_history.append({'timestamp': datetime.now().isoformat(), 'mode': mode.value, 'kappa_after': out.copy()})
483
+ return out
484
+
485
+ # ================================ SYSTEM ================================
486
+ class AdaptiveBiCoupledCoherenceSystem:
487
+ def __init__(self, mode: SystemMode = SystemMode.STANDARD):
488
+ self.mode = mode
489
+ self.encoder = DualStreamEncoder()
490
+ self.thr_mgr = AdaptiveThresholdManager()
491
+ self.processor = DualStreamProcessor(self.encoder, mode)
492
+ self.recon = BiCoupledReconstructor(self.encoder)
493
+ self.audit = DualStreamAuditor()
494
+ self.renew = AdaptiveRenewalEngine()
495
+ self.capsules: Dict[str, Optional[np.ndarray]] = {'forward': None, 'mirror': None}
496
+ self.kappa_history: List[Dict[FrequencyBand, float]] = []
497
+ self.system_history: List[Dict[str, Any]] = []
498
+ logger.info(f"ABCR initialized in mode={mode.value}")
499
+
500
+ def set_mode(self, mode: SystemMode):
501
+ self.mode = mode
502
+ self.processor.mode = mode
503
+ logger.info(f"Mode changed to {mode.value}")
504
+
505
+ def process(self, kappa: Dict[FrequencyBand, float], phi: Dict[FrequencyBand, float], t: float) -> Optional[Dict[FrequencyBand, float]]:
506
+ # thresholds
507
+ stress = self.thr_mgr.compute_stress(kappa, self.kappa_history)
508
+ thr = self.thr_mgr.compute(stress, self.mode)
509
+ # encode
510
+ C_F = self.encoder.encode_forward(kappa, phi)
511
+ C_M = self.encoder.encode_mirror(kappa, phi)
512
+ self.capsules = {'forward': C_F, 'mirror': C_M}
513
+ # detect
514
+ broken_A, broken_B, intact = self.processor.detect_broken(kappa, C_F, C_M, thr)
515
+ # emergencies
516
+ mn, mx = min(kappa.values()), max(kappa.values())
517
+ if mn < ABCRConfig.EMERGENCY_HYPO_THRESHOLD or mx > ABCRConfig.EMERGENCY_HYPER_THRESHOLD or (len(broken_A) + len(broken_B) >= len(FrequencyBand)):
518
+ logger.critical("EMERGENCY DECOUPLE")
519
+ return None
520
+ if not broken_A and not broken_B:
521
+ self.kappa_history.append(kappa.copy())
522
+ return kappa
523
+ # reconstruct
524
+ self.recon.compute_hamiltonians(broken_A, broken_B, intact, C_F, C_M)
525
+ rec = self.recon.reconstruct(broken_A, broken_B, intact)
526
+ # audit
527
+ t0 = self.kappa_history[-1]['_t'] if (self.kappa_history and '_t' in self.kappa_history[-1]) else t
528
+ ar = self.audit.audit(kappa, rec, broken_A, broken_B, t0, t)
529
+ if ar.audit_pass:
530
+ final = self.renew.renew(rec, self.mode)
531
+ self.renew.update_field(final)
532
+ self._record('successful_recovery', t, kappa, final, ar)
533
+ k_with_t = final.copy(); k_with_t['_t'] = t # store time in history entry
534
+ self.kappa_history.append(k_with_t)
535
+ return final
536
+ else:
537
+ self._record('failed_recovery', t, kappa, rec, ar)
538
+ # fallback
539
+ if self.renew.Pi is not None:
540
+ fb = self.renew.Pi.copy()
541
+ k_with_t = fb.copy(); k_with_t['_t'] = t
542
+ self.kappa_history.append(k_with_t)
543
+ return fb
544
+ fb = {b: 0.5 for b in FrequencyBand}
545
+ k_with_t = fb.copy(); k_with_t['_t'] = t
546
+ self.kappa_history.append(k_with_t)
547
+ return fb
548
+
549
+ def _record(self, event: str, t: float, kb: Dict[FrequencyBand, float], ka: Dict[FrequencyBand, float], audit: DualAuditResult):
550
+ self.system_history.append({
551
+ 'timestamp': t,
552
+ 'event': event,
553
+ 'kappa_before': {b.value: float(kb[b]) for b in FrequencyBand},
554
+ 'kappa_after': {b.value: float(ka[b]) for b in FrequencyBand},
555
+ 'audit': {
556
+ 'seam_type': audit.seam_type.value,
557
+ 's_composite': audit.s_composite,
558
+ 's_A': audit.s_A,
559
+ 's_B': audit.s_B,
560
+ 'active_streams': [s.value for s in audit.active_streams],
561
+ }
562
+ })
563
+
564
+ # --------------- Simulation & Viz ---------------
565
+ def simulate(self, duration: float = 10.0, dt: float = 0.1, scenario: str = 'dual_stress') -> List[Dict[str, Any]]:
566
+ steps = int(duration / dt)
567
+ hist: List[Dict[str, Any]] = []
568
+ kappa = {FrequencyBand.DELTA: 0.72, FrequencyBand.THETA: 0.68, FrequencyBand.ALPHA: 0.75, FrequencyBand.BETA: 0.70, FrequencyBand.GAMMA: 0.65}
569
+ phi = {FrequencyBand.DELTA: 0.1, FrequencyBand.THETA: 0.3, FrequencyBand.ALPHA: 0.5, FrequencyBand.BETA: 0.7, FrequencyBand.GAMMA: 0.9}
570
+ if self.renew.Pi is None:
571
+ self.renew.init_field(kappa)
572
+ for i in range(steps):
573
+ t = i * dt
574
+ # scenario dynamics
575
+ if scenario == 'dual_stress':
576
+ if 2.0 <= t <= 4.0:
577
+ kappa[FrequencyBand.DELTA] = 0.15
578
+ kappa[FrequencyBand.THETA] = 0.20
579
+ kappa[FrequencyBand.BETA] = 0.18
580
+ elif 5.0 <= t <= 7.0:
581
+ kappa[FrequencyBand.ALPHA] = 0.92
582
+ kappa[FrequencyBand.GAMMA] = 0.88
583
+ kappa[FrequencyBand.BETA] = 0.85
584
+ elif scenario == 'oscillatory':
585
+ for b in FrequencyBand:
586
+ f = ABCRConfig.BAND_FREQUENCIES[b]
587
+ kappa[b] = 0.5 + 0.4 * np.sin(2 * np.pi * f * t / 20.0)
588
+ elif scenario == 'cascade':
589
+ if t > 3.0:
590
+ fail = int((t - 3.0) / 1.5)
591
+ for idx, b in enumerate(FrequencyBand):
592
+ if idx < fail:
593
+ kappa[b] = 0.1
594
+ # noise
595
+ for b in FrequencyBand:
596
+ kappa[b] = float(np.clip(kappa[b] + np.random.normal(0.0, 0.02), 0.0, 1.0))
597
+ rec = self.process(kappa, phi, t)
598
+ if rec is not None:
599
+ kappa = rec
600
+ hist.append({'timestamp': t, 'kappa_state': {b.value: kappa[b] for b in FrequencyBand}, 'recovered': rec is not None})
601
+ return hist
602
+
603
+ def visualize(self, history: List[Dict[str, Any]], save_path: Optional[str] = None):
604
+ ts = [h['timestamp'] for h in history]
605
+ bands = {b: [h['kappa_state'][b.value] for h in history] for b in FrequencyBand}
606
+ fig, axes = plt.subplots(3, 1, figsize=(14, 12))
607
+ colors = {FrequencyBand.DELTA: 'blue', FrequencyBand.THETA: 'green', FrequencyBand.ALPHA: 'red', FrequencyBand.BETA: 'orange', FrequencyBand.GAMMA: 'purple'}
608
+ ax1 = axes[0]
609
+ for b in FrequencyBand:
610
+ ax1.plot(ts, bands[b], label=b.value, color=colors[b], linewidth=2)
611
+ ax1.axhspan(0, ABCRConfig.TAU_BASE, alpha=0.1, color='blue', label='Hypo zone')
612
+ ax1.axhspan(1-ABCRConfig.TAU_BASE, 1, alpha=0.1, color='red', label='Hyper zone')
613
+ ax1.axhline(0.5, color='gray', linestyle=':')
614
+ ax1.set_ylabel('κ')
615
+ ax1.set_title('ABCR Dual-Stream Coherence Dynamics')
616
+ ax1.legend(loc='upper right')
617
+ ax1.grid(True, alpha=0.3)
618
+ ax1.set_ylim(-0.05, 1.05)
619
+ ax2 = axes[1]
620
+ A_times, B_times = [], []
621
+ for e in self.system_history:
622
+ if 'audit' in e:
623
+ streams = e['audit']['active_streams']
624
+ t = e['timestamp']
625
+ if StreamType.STREAM_A.value in streams:
626
+ A_times.append(t)
627
+ if StreamType.STREAM_B.value in streams:
628
+ B_times.append(t)
629
+ if A_times:
630
+ ax2.scatter(A_times, [0.3]*len(A_times), color='blue', s=50, alpha=0.7, label='Stream A')
631
+ if B_times:
632
+ ax2.scatter(B_times, [0.7]*len(B_times), color='red', s=50, alpha=0.7, label='Stream B')
633
+ ax2.set_ylabel('Stream Activity')
634
+ ax2.set_title('Dual-Stream Activity')
635
+ ax2.legend(); ax2.grid(True, alpha=0.3); ax2.set_ylim(0,1)
636
+ ax3 = axes[2]
637
+ audit_t, s_vals, seams = [], [], []
638
+ for e in self.system_history:
639
+ if 'audit' in e:
640
+ audit_t.append(e['timestamp']); s_vals.append(e['audit']['s_composite']); seams.append(e['audit']['seam_type'])
641
+ if audit_t:
642
+ seam_colors = ['green' if 'Type I' in s else 'orange' if 'Type II' in s else 'red' for s in seams]
643
+ ax3.scatter(audit_t, s_vals, c=seam_colors, s=30, alpha=0.8)
644
+ ax3.axhline(0, color='gray', linestyle='-')
645
+ ax3.axhline(ABCRConfig.AUDIT_TOLERANCE, color='green', linestyle='--', alpha=0.6)
646
+ ax3.axhline(-ABCRConfig.AUDIT_TOLERANCE, color='green', linestyle='--', alpha=0.6)
647
+ ax3.set_xlabel('Time (s)'); ax3.set_ylabel('Composite Residual'); ax3.set_title('Audit Results'); ax3.grid(True, alpha=0.3)
648
+ plt.tight_layout()
649
+ if save_path:
650
+ plt.savefig(save_path, dpi=300, bbox_inches='tight')
651
+ logger.info(f"Visualization saved to {save_path}")
652
+ plt.show()
653
+
654
+ # --------------- Persistence ---------------
655
+ def save_state(self, path: str):
656
+ state = {
657
+ 'mode': self.mode.value,
658
+ 'invariant_field': {b.value: float(self.renew.Pi[b]) for b in FrequencyBand} if self.renew.Pi else None,
659
+ 'system_history': self.system_history,
660
+ 'kappa_history': self.kappa_history[-10:] if self.kappa_history else []
661
+ }
662
+ with open(path, 'w') as f:
663
+ json.dump(state, f, indent=2)
664
+ logger.info(f"State saved to {path}")
665
+
666
+ def load_state(self, path: str):
667
+ with open(path, 'r') as f:
668
+ s = json.load(f)
669
+ self.mode = SystemMode(s['mode'])
670
+ if s['invariant_field']:
671
+ self.renew.Pi = {FrequencyBand(b): v for b, v in s['invariant_field'].items()}
672
+ self.system_history = s['system_history']
673
+ self.kappa_history = s['kappa_history']
674
+ logger.info(f"State loaded from {path}")
675
+
676
+ # ================================ DEMO ================================
677
+ def demonstrate_abcr():
678
+ print("=" * 70)
679
+ print("ADAPTIVE BI-COUPLED COHERENCE RECOVERY (ABCR) — v2")
680
+ print("=" * 70)
681
+ scenarios = [("dual_stress", SystemMode.ADAPTIVE), ("oscillatory", SystemMode.HIGH_SENSITIVITY), ("cascade", SystemMode.RECOVERY)]
682
+ for name, mode in scenarios:
683
+ print(f"
684
+ Scenario: {name} — mode={mode.value}")
685
+ sys = AdaptiveBiCoupledCoherenceSystem(mode)
686
+ hist = sys.simulate(10.0, 0.1, name)
687
+ succ = sum(1 for e in sys.system_history if e['event'] == 'successful_recovery')
688
+ total = len(sys.system_history)
689
+ print(f" Steps: {len(hist)} | Recovery attempts: {total} | Success: {succ}")
690
+ if total:
691
+ print(f" Success rate: {succ/total*100:.1f}%")
692
+ sys.visualize(hist, f"abcr_{name}_{mode.value}.png")
693
+ sys.save_state(f"abcr_state_{name}_{mode.value}.json")
694
+ print("
695
+ ABCR demonstration complete.")
696
+
697
+ if __name__ == '__main__':
698
+ demonstrate_abcr()