Files
resonance-engine/lattice_peek.py
T
2026-06-08 17:34:31 +07:00

147 lines
5.6 KiB
Python

"""Peek into the running lattice — read-only. Verify whether the Fractonaut's
'directional horizontal shear' claim is hallucination or grounded.
Subscribes to:
5556 - telemetry (scalar summaries)
5560 - stress snapshots (full spatial stress field, if any)
Sends:
5557 - stress_snapshot_now ONCE (read-only, no field perturbation)
"""
import zmq, json, time, numpy as np, sys
ctx = zmq.Context()
# Telemetry SUB
tel = ctx.socket(zmq.SUB)
tel.connect("tcp://127.0.0.1:5556")
tel.setsockopt_string(zmq.SUBSCRIBE, "")
# Stress snapshot SUB
ssub = ctx.socket(zmq.SUB)
ssub.connect("tcp://127.0.0.1:5560")
ssub.setsockopt_string(zmq.SUBSCRIBE, "")
# Command PUB (only sends stress_snapshot_now — read-only on the field)
cmd = ctx.socket(zmq.PUB)
cmd.connect("tcp://127.0.0.1:5557")
print("warming up SUBs ...")
time.sleep(2.5)
# Collect 30 telemetry frames so we can see distribution, not just one
frames = []
t_end = time.time() + 4.0
while time.time() < t_end and len(frames) < 50:
try:
msg = tel.recv_string(zmq.NOBLOCK)
frames.append(json.loads(msg))
except zmq.Again:
time.sleep(0.05)
print(f"collected {len(frames)} telemetry frames")
if frames:
keys = ["asymmetry","coherence","vel_mean","vel_max","vel_var",
"vorticity_mean","stress_xx","stress_yy","stress_xy"]
print(f"\n{'metric':<16} {'mean':>12} {'min':>12} {'max':>12} {'sign':>6}")
print("-"*64)
for k in keys:
vals = [f[k] for f in frames if k in f]
if not vals: continue
m = np.mean(vals); mn=min(vals); mx=max(vals)
sign = "+/-" if mn*mx < 0 else ("+" if m>0 else "-")
print(f"{k:<16} {m:>12.6f} {mn:>12.6f} {mx:>12.6f} {sign:>6}")
# Now request ONE stress snapshot — read-only command, just triggers a dump
print("\nrequesting one stress snapshot ...")
cmd.send_string(json.dumps({"cmd": "stress_snapshot_now"}))
# Wait up to 5s for snapshot
t_end = time.time() + 5.0
snap = None
while time.time() < t_end:
try:
raw = ssub.recv(zmq.NOBLOCK)
snap = raw
print(f"got stress snapshot: {len(raw)} bytes")
break
except zmq.Again:
time.sleep(0.05)
if snap is None:
print("no stress snapshot received (snapshots may be disabled or delayed)")
sys.exit(0)
# Try to interpret as raw float32 grid. v5 publishes stress fields at NX*NY
# = 1024*1024 = 1048576 floats per channel. Could be 3 channels (xx, yy, xy)
# packed together, possibly with a header.
arr = np.frombuffer(snap, dtype=np.float32)
print(f"snapshot has {arr.size} float32s, expected 3*1024*1024={3*1024*1024}")
# Try unpacking
n_pix = 1024*1024
if arr.size >= 3*n_pix:
# Skip any header by hunting for the right offset
for off in [0, 1, 2, 3, 4, 8, 16]:
if arr.size - off >= 3*n_pix:
sxx = arr[off:off+n_pix].reshape(1024,1024)
syy = arr[off+n_pix:off+2*n_pix].reshape(1024,1024)
sxy = arr[off+2*n_pix:off+3*n_pix].reshape(1024,1024)
# Sanity
if abs(sxx.mean()) < 0.1 and abs(syy.mean()) < 0.1:
print(f"interpreted at offset {off}")
break
else:
print("could not find valid offset")
sys.exit(0)
else:
print("snapshot smaller than expected — maybe single channel")
if arr.size >= n_pix:
sxx = arr[:n_pix].reshape(1024,1024)
syy = None; sxy = None
else:
sys.exit(0)
# OK now actually look at the spatial structure
print(f"\n=== stress_xx field ===")
print(f" shape={sxx.shape} mean={sxx.mean():+.6f} std={sxx.std():.6f}")
print(f" min={sxx.min():+.6f} max={sxx.max():+.6f}")
# Left half vs right half (around X=512)
print(f" left half (x<512) mean: {sxx[:,:512].mean():+.6f}")
print(f" right half (x>=512) mean: {sxx[:,512:].mean():+.6f}")
print(f" top half (y<512) mean: {sxx[:512,:].mean():+.6f}")
print(f" bot half (y>=512) mean: {sxx[512:,:].mean():+.6f}")
# Around the buy site (x=400) vs sell site (x=624), 96-cell window
buy_box = sxx[464:560, 352:448] # 96x96 around (400, 512)
sell_box = sxx[464:560, 576:672] # 96x96 around (624, 512)
ctr_box = sxx[464:560, 464:560] # 96x96 around (512, 512)
print(f"\n stress_xx around buy-site (x=400,y=512) 96x96 box mean: {buy_box.mean():+.6f} std: {buy_box.std():.6f}")
print(f" stress_xx around sell-site (x=624,y=512) 96x96 box mean: {sell_box.mean():+.6f} std: {sell_box.std():.6f}")
print(f" stress_xx around centre (x=512,y=512) 96x96 box mean: {ctr_box.mean():+.6f} std: {ctr_box.std():.6f}")
if syy is not None:
print(f"\n=== stress_yy field ===")
print(f" shape={syy.shape} mean={syy.mean():+.6f} std={syy.std():.6f}")
print(f" min={syy.min():+.6f} max={syy.max():+.6f}")
print(f" left half (x<512) mean: {syy[:,:512].mean():+.6f}")
print(f" right half (x>=512) mean: {syy[:,512:].mean():+.6f}")
buy_box = syy[464:560, 352:448]
sell_box = syy[464:560, 576:672]
print(f" stress_yy around buy-site mean: {buy_box.mean():+.6f}")
print(f" stress_yy around sell-site mean: {sell_box.mean():+.6f}")
if sxy is not None:
print(f"\n=== stress_xy field ===")
print(f" mean={sxy.mean():+.6f} std={sxy.std():.6f}")
# Verdict
print(f"\n=== VERDICT ===")
print(f"global stress_xx mean: {sxx.mean():+.6f}")
print(f"global stress_yy mean: {syy.mean() if syy is not None else float('nan'):+.6f}")
if syy is not None:
diff_lr = sxx[:,:512].mean() - sxx[:,512:].mean()
print(f"stress_xx left-vs-right asymmetry: {diff_lr:+.6f}")
if abs(diff_lr) > 3 * sxx.std() / np.sqrt(512*1024):
print(f" -> SIGNIFICANT left-right asymmetry detected")
else:
print(f" -> no significant left-right asymmetry (sample noise)")