123 lines
6.3 KiB
Python
123 lines
6.3 KiB
Python
#!/usr/bin/env python3
|
|
# baseline_breathing_test.py
|
|
# Question (Jason): left completely alone, does the organism keep changing, and is that
|
|
# change genuine self-evolution (the state DRIFTS over time) or just churn-in-place
|
|
# (jitter around a fixed center = noise, not evolution)?
|
|
#
|
|
# NO injection. NO intervention. Pure observation of the free-running field.
|
|
# This establishes THE BASELINE that any later on/off perturbation test measures against.
|
|
#
|
|
# Two data sources, both read-only:
|
|
# A) telemetry.jsonl -> per-channel natural movement (the noise floor of each scalar)
|
|
# B) live 5561 coarse field -> spatial frame-to-frame change + drift-vs-churn
|
|
#
|
|
# Drift-vs-churn discriminator:
|
|
# - churn (noise): frame(t) vs frame(t+k) distance is FLAT in k -> it wanders around a
|
|
# fixed center, never gets further away. Memoryless jitter.
|
|
# - drift (self-evolving): distance GROWS with k -> the center itself is moving, the
|
|
# field at t+k is systematically further from t than t+1 is. State evolves.
|
|
|
|
import sys, io, json, time, struct, os
|
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
|
import numpy as np
|
|
|
|
# ---------- PART A: telemetry per-channel natural movement ----------
|
|
TEL = None
|
|
for p in ["/mnt/d/resonance-engine/telemetry.jsonl", r"D:\resonance-engine\telemetry.jsonl"]:
|
|
if os.path.exists(p): TEL=p; break
|
|
|
|
CHANS = ['coherence','asymmetry','vel_mean','vel_max','vel_var','vorticity_mean',
|
|
'stress_xx','stress_yy','stress_xy']
|
|
|
|
print("="*64); print("BASELINE BREATHING TEST — organism left alone"); print("="*64)
|
|
|
|
if TEL:
|
|
rows=[]
|
|
with open(TEL) as f:
|
|
for line in f:
|
|
try: d=json.loads(line)
|
|
except: continue
|
|
if abs(d.get('omega',0)-1.97)<0.01:
|
|
rows.append([d.get(k,np.nan) for k in CHANS]+[d.get('cycle',0)])
|
|
A=np.array(rows,dtype=np.float64)
|
|
print(f"\n[A] telemetry.jsonl: {len(A)} canonical rows, cycles {int(A[:,-1].min())}..{int(A[:,-1].max())}", flush=True)
|
|
print(" per-channel natural movement (this IS the baseline noise floor):", flush=True)
|
|
print(f" {'channel':<16}{'mean':>12}{'std':>12}{'CV%':>10}{'range':>14}", flush=True)
|
|
for i,ch in enumerate(CHANS):
|
|
col=A[:,i]; col=col[~np.isnan(col)]
|
|
if len(col)<2: continue
|
|
mu,sd=col.mean(),col.std()
|
|
cv=100*sd/abs(mu) if abs(mu)>1e-12 else float('nan')
|
|
print(f" {ch:<16}{mu:>12.5f}{sd:>12.5f}{cv:>9.2f}%{col.max()-col.min():>14.5f}", flush=True)
|
|
print(" -> channels with high CV move a lot on their own; low CV sit still.", flush=True)
|
|
print(" -> ANY on/off perturbation must exceed THIS movement to be detectable.", flush=True)
|
|
else:
|
|
print("\n[A] telemetry.jsonl not found — skipping scalar baseline.", flush=True)
|
|
|
|
# ---------- PART B: live coarse field, drift vs churn ----------
|
|
TILES,CH=32,6; NVALS=TILES*TILES*CH; HDR=16; FB=HDR+NVALS*4
|
|
PORT="tcp://localhost:5561"; NCAP=600
|
|
try:
|
|
import zmq
|
|
except ImportError:
|
|
print("\n[B] pyzmq missing; skip live part"); sys.exit(0)
|
|
|
|
print(f"\n[B] Capturing {NCAP} live frames from {PORT} (read-only)...", flush=True)
|
|
ctx=zmq.Context(); s=ctx.socket(zmq.SUB); s.setsockopt(zmq.RCVTIMEO,5000); s.setsockopt_string(zmq.SUBSCRIBE,"")
|
|
s.connect(PORT)
|
|
F=[]; C=[]
|
|
while len(F)<NCAP:
|
|
try: buf=s.recv()
|
|
except zmq.Again: print(f" timeout at {len(F)} frames"); break
|
|
if len(buf)!=FB or buf[:4]!=b"KGCF": continue
|
|
C.append(struct.unpack_from("<I",buf,4)[0])
|
|
F.append(np.frombuffer(buf,dtype=np.float32,count=NVALS,offset=HDR).copy())
|
|
s.close(); ctx.term()
|
|
F=np.array(F); C=np.array(C)
|
|
if len(F)<50: print("too few frames"); sys.exit(1)
|
|
print(f" got {len(F)} frames, cycles {C[0]}..{C[-1]}", flush=True)
|
|
|
|
# z-score per channel so no channel dominates
|
|
Fr=F.reshape(len(F),TILES*TILES,CH).astype(np.float64)
|
|
mu=Fr.mean(axis=(0,1),keepdims=True); sd=Fr.std(axis=(0,1),keepdims=True); sd[sd<1e-9]=1
|
|
Z=(Fr-mu)/sd
|
|
Zflat=Z.reshape(len(F),-1)
|
|
|
|
# frame-to-frame change (is it even moving spatially?)
|
|
step=np.sqrt(np.mean(np.diff(Zflat,axis=0)**2,axis=1))
|
|
print(f"\n spatial frame-to-frame change (per ~10 cyc): mean={step.mean():.4f} std={step.std():.4f}", flush=True)
|
|
print(f" -> {'MOVING (field changes every frame)' if step.mean()>1e-3 else 'STATIC (barely changes)'}", flush=True)
|
|
|
|
# DRIFT vs CHURN: distance between frames as a function of separation k
|
|
print("\n=== DRIFT vs CHURN (does the state get FURTHER over time, or wander in place?) ===", flush=True)
|
|
ks=[1,2,5,10,20,50,100,200]
|
|
print(f" {'gap(frames)':>12}{'gap(cyc)':>10}{'mean_dist':>12}", flush=True)
|
|
dvals=[]
|
|
for k in ks:
|
|
if k>=len(Zflat): continue
|
|
dd=np.sqrt(np.mean((Zflat[k:]-Zflat[:-k])**2,axis=1))
|
|
dvals.append((k,dd.mean()))
|
|
print(f" {k:>12}{k*10:>10}{dd.mean():>12.4f}", flush=True)
|
|
d1=dvals[0][1]; dlast=dvals[-1][1]
|
|
grow=dlast/max(d1,1e-9)
|
|
# saturation: does distance keep climbing or plateau?
|
|
print(f"\n distance at gap={dvals[0][0]}: {d1:.4f} at gap={dvals[-1][0]}: {dlast:.4f} growth {grow:.2f}x", flush=True)
|
|
|
|
print("\n=== READ (description only, no 'memory' verdict) ===", flush=True)
|
|
if grow>1.3:
|
|
print(" Distance GROWS with time separation, then likely plateaus.", flush=True)
|
|
print(" => The field DRIFTS: state at t+k is systematically further from t than t+1 is.", flush=True)
|
|
print(" This is self-evolution, not jitter-in-place. It is genuinely going somewhere.", flush=True)
|
|
print(" The plateau distance = the 'diameter' of its wandering; the gap where it", flush=True)
|
|
print(" saturates = the timescale over which it forgets where it was. THAT number is", flush=True)
|
|
print(" the natural memory-horizon of the undisturbed organism. Note it.", flush=True)
|
|
else:
|
|
print(" Distance is roughly FLAT with time separation.", flush=True)
|
|
print(" => The field CHURNS in place: t+1 and t+200 are about equally far from t.", flush=True)
|
|
print(" It moves constantly but does not go anywhere — jitter around a fixed center.", flush=True)
|
|
print(" That is baseline noise, not self-evolution. A perturbation would have to push", flush=True)
|
|
print(" the center itself to leave a trace above this churn.", flush=True)
|
|
|
|
np.savez("baseline_breathing.npz", step=step, ks=[k for k,_ in dvals], dist=[v for _,v in dvals], cyc=C)
|
|
print("\nWrote baseline_breathing.npz. DONE.", flush=True)
|