114 lines
5.4 KiB
Python
114 lines
5.4 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
# snapshot_uniqueness_test.py
|
||
|
|
# Jason's claim: perturbations are indelible and unique — no two snapshots the same.
|
||
|
|
# This tests the DIFFERENCE that matters:
|
||
|
|
# (A) trivial: every frame differs from every other (chaos / non-repetition) -> NOT memory
|
||
|
|
# (B) real: the DISTANCE between snapshots is STRUCTURED by history/time,
|
||
|
|
# not just uniform noise -> memory-like
|
||
|
|
#
|
||
|
|
# Read-only on checkpoints. Computes pairwise field distances and asks:
|
||
|
|
# - Are all snapshots unique? (yes/no, and by how much)
|
||
|
|
# - Is the distance between two snapshots a FUNCTION of how far apart in cycles they are?
|
||
|
|
# If distance grows with time-gap then plateaus -> the field "forgets" at a timescale (memory horizon).
|
||
|
|
# If distance is flat/random vs gap -> unique but memoryless (just non-repeating).
|
||
|
|
# - Do NEARBY-in-time snapshots stay more similar than FAR ones? (persistence of state)
|
||
|
|
|
||
|
|
import sys, io, glob, os, struct
|
||
|
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||
|
|
import numpy as np
|
||
|
|
|
||
|
|
NX=NY=1024; Q=9; HDR=64
|
||
|
|
DIR = "/mnt/d/Resonance_Engine/pattern_vocab/exp_instance"
|
||
|
|
if not os.path.isdir(DIR):
|
||
|
|
DIR = r"D:\Resonance_Engine\pattern_vocab\exp_instance"
|
||
|
|
|
||
|
|
def read_header(fp):
|
||
|
|
with open(fp,"rb") as f:
|
||
|
|
h=f.read(HDR)
|
||
|
|
if h[:4]!=b"KHRG": return None
|
||
|
|
cyc=struct.unpack_from("<I",h,8)[0]
|
||
|
|
omega=struct.unpack_from("<f",h,24)[0]
|
||
|
|
khra=struct.unpack_from("<f",h,28)[0]
|
||
|
|
gixx=struct.unpack_from("<f",h,32)[0]
|
||
|
|
return dict(cycle=cyc,omega=omega,khra=khra,gixx=gixx,path=fp)
|
||
|
|
|
||
|
|
def load_rho_downsampled(fp, ds=16):
|
||
|
|
# load f_data, compute density per cell, downsample by block-mean to (NX/ds)^2
|
||
|
|
with open(fp,"rb") as f:
|
||
|
|
f.read(HDR)
|
||
|
|
arr=np.frombuffer(f.read(NX*NY*Q*4),dtype=np.float32).reshape(NY,NX,Q)
|
||
|
|
rho=arr.sum(axis=2) # (NY,NX)
|
||
|
|
n=NX//ds
|
||
|
|
rho_d=rho.reshape(n,ds,n,ds).mean(axis=(1,3)) # (n,n)
|
||
|
|
return rho_d
|
||
|
|
|
||
|
|
files=sorted(glob.glob(os.path.join(DIR,"*.bin")))
|
||
|
|
print(f"Found {len(files)} checkpoints in {DIR}", flush=True)
|
||
|
|
|
||
|
|
# canonical-only, sorted by cycle
|
||
|
|
meta=[read_header(fp) for fp in files]
|
||
|
|
meta=[m for m in meta if m and abs(m['omega']-1.97)<0.01 and abs(m['khra']-0.03)<0.001 and abs(m['gixx']-0.008)<0.001]
|
||
|
|
meta.sort(key=lambda m:m['cycle'])
|
||
|
|
print(f"Canonical checkpoints: {len(meta)}, cycles {meta[0]['cycle']}..{meta[-1]['cycle']}", flush=True)
|
||
|
|
|
||
|
|
# sample up to ~40 evenly to keep pairwise cost sane
|
||
|
|
K=min(40,len(meta))
|
||
|
|
idx=np.linspace(0,len(meta)-1,K).astype(int)
|
||
|
|
sel=[meta[i] for i in idx]
|
||
|
|
cyc=np.array([m['cycle'] for m in sel])
|
||
|
|
|
||
|
|
print(f"Loading {K} downsampled density fields...", flush=True)
|
||
|
|
fields=np.array([load_rho_downsampled(m['path']).ravel() for m in sel]) # (K, n*n)
|
||
|
|
|
||
|
|
# --- 1. UNIQUENESS: is any pair identical? ---
|
||
|
|
# normalize each field (remove mean drift so we test PATTERN not the mass-leak amplitude)
|
||
|
|
fn = fields - fields.mean(axis=1, keepdims=True)
|
||
|
|
fn = fn / (fn.std(axis=1, keepdims=True)+1e-12)
|
||
|
|
|
||
|
|
D=np.zeros((K,K))
|
||
|
|
for i in range(K):
|
||
|
|
for j in range(K):
|
||
|
|
D[i,j]=np.sqrt(np.mean((fn[i]-fn[j])**2))
|
||
|
|
offdiag=D[np.triu_indices(K,1)]
|
||
|
|
print("\n=== UNIQUENESS ===", flush=True)
|
||
|
|
print(f" min pairwise distance (excluding self): {offdiag.min():.4f}", flush=True)
|
||
|
|
print(f" are any two snapshots identical? {'NO — all unique' if offdiag.min()>1e-6 else 'YES some identical'}", flush=True)
|
||
|
|
print(f" distance range: {offdiag.min():.3f} .. {offdiag.max():.3f} mean {offdiag.mean():.3f}", flush=True)
|
||
|
|
|
||
|
|
# --- 2. IS DISTANCE A FUNCTION OF TIME-GAP? (the memory-vs-chaos discriminator) ---
|
||
|
|
gaps=[]; dists=[]
|
||
|
|
for i in range(K):
|
||
|
|
for j in range(i+1,K):
|
||
|
|
gaps.append(abs(cyc[i]-cyc[j])); dists.append(D[i,j])
|
||
|
|
gaps=np.array(gaps); dists=np.array(dists)
|
||
|
|
# correlation of distance with time-gap
|
||
|
|
r=np.corrcoef(gaps,dists)[0,1]
|
||
|
|
print("\n=== STRUCTURE OF DISTANCE vs TIME-GAP ===", flush=True)
|
||
|
|
print(f" correlation(time_gap, field_distance) = {r:+.3f}", flush=True)
|
||
|
|
print(" (near 0 = distance unrelated to time = unique-but-memoryless chaos;", flush=True)
|
||
|
|
print(" strongly >0 = closer-in-time stays more similar = STATE PERSISTS = memory-like)", flush=True)
|
||
|
|
|
||
|
|
# binned: mean distance at small gap vs large gap
|
||
|
|
order=np.argsort(gaps)
|
||
|
|
q=len(gaps)//4
|
||
|
|
near=dists[order[:q]].mean(); far=dists[order[-q:]].mean()
|
||
|
|
print(f" mean distance, NEAREST-in-time quartile: {near:.3f}", flush=True)
|
||
|
|
print(f" mean distance, FARTHEST-in-time quartile: {far:.3f}", flush=True)
|
||
|
|
print(f" ratio far/near: {far/max(near,1e-9):.2f}x", flush=True)
|
||
|
|
|
||
|
|
print("\n=== VERDICT ===", flush=True)
|
||
|
|
unique = offdiag.min()>1e-6
|
||
|
|
persists = (r>0.3) and (far/max(near,1e-9) > 1.15)
|
||
|
|
if unique and persists:
|
||
|
|
print(" UNIQUE *and* STRUCTURED: every snapshot differs, AND closer-in-time snapshots", flush=True)
|
||
|
|
print(" are more similar than distant ones. The field's state PERSISTS and drifts —", flush=True)
|
||
|
|
print(" this is memory-like: where it is now depends on where it was. Jason's read is supported.", flush=True)
|
||
|
|
elif unique and not persists:
|
||
|
|
print(" UNIQUE but NOT time-structured: every snapshot differs, but distance is ~unrelated", flush=True)
|
||
|
|
print(" to time-gap. That is non-repetition (chaos), NOT memory. Uniqueness alone != memory.", flush=True)
|
||
|
|
else:
|
||
|
|
print(" Some snapshots near-identical — investigate before concluding.", flush=True)
|
||
|
|
|
||
|
|
np.savez("snapshot_distance.npz", D=D, cyc=cyc, gaps=gaps, dists=dists)
|
||
|
|
print("\nWrote snapshot_distance.npz. DONE.", flush=True)
|