#!/usr/bin/env python3 """ Stress verification + density vs stress hold-time A/B test. Pre-committed thresholds: - Stress change: |delta_sxy| > 1e-8 (must exceed zero by 8 orders) - Return threshold: L2 distance < 0.01 * baseline_variance (must drop below 1% of baseline noise) - Setup time: >=2510 cycles (>=10 khra periods at canonical w_khra=0.025, period=2pi/0.025≈251) - Bootstrap: 100 resamples, 95% CI """ import zmq, json, time, struct, numpy as np from collections import deque CTX = zmq.Context() # ========== Helpers ========== def send_cmd(cmd_pub, d): """Send JSON command to port 5557.""" cmd_pub.send_string(json.dumps(d)) time.sleep(0.05) def recv_ack(ack_sub, timeout=4.0): """Receive ack messages until timeout.""" ack_sub.setsockopt(zmq.RCVTIMEO, int(timeout * 1000)) acks = [] while True: try: acks.append(json.loads(ack_sub.recv_string())) except zmq.ZMQError: break ack_sub.setsockopt(zmq.RCVTIMEO, -1) # back to blocking return acks def recv_stress_snapshot(stress_sub, timeout=5.0): """Receive one stress snapshot from port 5560 (8-byte header + 3*4MB).""" stress_sub.setsockopt(zmq.RCVTIMEO, int(timeout * 1000)) try: data = stress_sub.recv() stress_sub.setsockopt(zmq.RCVTIMEO, -1) cycle = struct.unpack_from(' {threshold_sxy:.1e}") if abs(delta_sxy) > threshold_sxy: print("STRESS VERIFICATION: PASS -- f_neq actually changed!") else: print("STRESS VERIFICATION: FAIL -- no detectable change in sxy") print("(This means the perturbation was equilibrated away before the snapshot)") # ================================ # PART 2: HOLD-TIME A/B TEST # ================================ print("\n" + "=" * 60) print("PART 2: HOLD-TIME A/B -- DENSITY vs STRESS") print("=" * 60) SETTLE_CYCLES = 2600 # >=10 khra periods at default w_khra=0.025 MEASURE_CYCLES = 2600 # Drain coarse channel, let system settle print(f"Waiting {SETTLE_CYCLES//100*10} sec for settle + baseline collection...") time.sleep(SETTLE_CYCLES // 100 * 10 / 1000 + 5) # rough wait def capture_baseline(n_frames=300): """Capture baseline coarse frames for comparison.""" frames = [] while len(frames) < n_frames: try: data = coarse_sub.recv(flags=zmq.NOBLOCK) if len(data) >= 16 + 4*6144 and data[:4] == b'KGCF': cycle = struct.unpack_from('=2510 cycles = ~260 frames at 10-cycle tick) # The coarse stream emits every 10 cycles, so 260 frames = 2600 cycles print(f"Collecting post-perturb frames (target ~260 frames)...") post_frames = capture_baseline(n_frames=280) print(f"Captured {len(post_frames)} post-perturb frames") if len(post_frames) < 50: print("FAIL: too few post-perturb frames") return None # Compute L2 distance from baseline mean at each frame distances = [] cycles = [] for cycle, field in post_frames: d = np.mean((field - baseline_mean) ** 2) distances.append(d) cycles.append(cycle) distances = np.array(distances) cycles = np.array(cycles) # Find hold-time: first frame where distance drops below threshold # Start from frame 20 (skip initial transient) hold_idx = None for i in range(20, len(distances)): if distances[i] < threshold_d: # Confirm: next 5 frames also below threshold if i + 5 < len(distances) and np.all(distances[i:i+5] < threshold_d): hold_idx = i break if hold_idx is not None: hold_cycle = cycles[hold_idx] - inject_cycle hold_cycles_float = float(hold_cycle) else: print("WARNING: never crossed threshold -- perturbation may persist beyond window") hold_cycle = len(post_frames) * 10 # rough max hold_cycles_float = float(hold_cycle) print(f"Hold-time (raw): {hold_cycles_float:.0f} cycles") print(f"Distance range: [{distances[0]:.6e}, {distances[-1]:.6e}]") # Bootstrap CI np.random.seed(42) boot_samples = [] for _ in range(200): # Resample baseline frames with replacement idx = np.random.choice(len(baseline_fields), len(baseline_fields), replace=True) boot_baseline_mean = np.mean(baseline_fields[idx], axis=0) boot_baseline_var = np.mean(np.var(baseline_fields[idx], axis=0)) boot_threshold = 0.01 * boot_baseline_var # Compute distances against boot baseline boot_distances = [] for cycle, field in post_frames: d = np.mean((field - boot_baseline_mean) ** 2) boot_distances.append(d) boot_distances = np.array(boot_distances) # Find hold-time with boot threshold boot_hold_idx = None for i in range(20, len(boot_distances)): if boot_distances[i] < boot_threshold and i + 5 < len(boot_distances): if np.all(boot_distances[i:i+5] < boot_threshold): boot_hold_idx = i break if boot_hold_idx is not None: boot_samples.append(float(cycles[boot_hold_idx] - inject_cycle)) else: boot_samples.append(float(len(post_frames) * 10)) ci_low = np.percentile(boot_samples, 2.5) ci_high = np.percentile(boot_samples, 97.5) print(f"Hold-time: {hold_cycles_float:.0f} cycles, 95% CI: [{ci_low:.0f}, {ci_high:.0f}]") return hold_cycles_float, ci_low, ci_high # Run Arm A: density result_a = run_hold_time_test( {"cmd": "inject_density", "x": 512.0, "y": 512.0, "sigma": 16.0, "strength": 0.1}, "Arm A: inject_density" ) # Wait for system to settle back print(f"\nWaiting {SETTLE_CYCLES//100*10/1000:.0f}s for field to settle...") time.sleep(15) # Run Arm B: stress result_b = run_hold_time_test( {"cmd": "perturb_stress", "x": 512.0, "y": 512.0, "sigma": 16.0, "strength": 0.1}, "Arm B: perturb_stress" ) # ================================ print("\n" + "=" * 60) print("RESULTS SUMMARY") print("=" * 60) print(f"\nSTRESS VERIFICATION: {'PASS' if abs(delta_sxy) > threshold_sxy else 'FAIL'}") print(f" Before sxy at site: {before_sxy_mean:.6e}") print(f" After sxy at site: {after_sxy_mean:.6e}") print(f" Delta: {delta_sxy:.6e} (threshold: {threshold_sxy:.1e})") print(f"\nHOLD-TIME A/B:") if result_a: ht, lo, hi = result_a print(f" Arm A (density): {ht:.0f} cycles, 95% CI [{lo:.0f}, {hi:.0f}]") else: print(f" Arm A (density): FAILED") if result_b: ht, lo, hi = result_b print(f" Arm B (stress): {ht:.0f} cycles, 95% CI [{lo:.0f}, {hi:.0f}]") else: print(f" Arm B (stress): FAILED") # Cleanup cmd_pub.close() ack_sub.close() stress_sub.close() coarse_sub.close() CTX.term() print("\nDone.")