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

213 lines
7.6 KiB
Python

"""inject_strength_sweep.py - find what perturbation finally moves global
telemetry. Kernel clamps: strength in [-1.0, 1.0], sigma in [1.0, 256.0].
Keep contacts intact (LEFT_X=400, RIGHT_X=624). Pin strength at clamp
max (1.0) and frequency at every 5s. Sweep sigma 48 -> 96 -> 160 -> 256
to vary the spatial footprint while staying within kernel limits.
Geometry: LEFT_X=400, RIGHT_X=624, CENTER_Y=512 (unchanged).
Per state: declare ACTIVE, fire bipolar dipole every 5s for 3 min,
collect all telemetry, ask Fractonaut at end, dump z-scored summary.
Safety: try/finally restores Khra=0.030/Gixx=0.008 and inj_state=UNKNOWN.
Does NOT modify Khra/Gixx amplitudes during the run.
"""
import zmq, json, time, urllib.request, sys, statistics
from datetime import datetime
from pathlib import Path
CMD_PORT = 5557
TEL_PORT = 5556
FRACTO_API = "http://127.0.0.1:28822"
LEFT_X, RIGHT_X, CENTER_Y = 400, 624, 512
STRENGTH = 1.0 # clamp max
DWELL_S = 180 # 3 min per state
INJ_INTERVAL_S = 5 # fire every 5s
SIGMAS = [48, 96, 160, 256] # baseline -> kernel max
BASELINE_STD = {
"asymmetry": 3.0,
"coherence": 0.005,
"vel_mean": 0.005,
"vel_max": 0.008,
"vel_var": 0.0004,
"vorticity_mean": 0.012,
"stress_xx": 0.00015,
"stress_yy": 0.00015,
"stress_xy": 0.00008,
}
BASELINE_MEAN = {
"asymmetry": 116.67,
"coherence": 0.6047,
"vel_mean": 0.2106,
"vel_max": 0.2854,
"vel_var": 0.002429,
"vorticity_mean": 0.0265,
"stress_xx": -0.000795,
"stress_yy": 0.000731,
"stress_xy": -0.000263,
}
OUT_DIR = Path(f"/mnt/d/Resonance_Engine/traj/inject_sweep_{datetime.now().strftime('%Y%m%dT%H%M%S')}")
OUT_DIR.mkdir(parents=True, exist_ok=True)
OUT_FILE = OUT_DIR / "sweep.jsonl"
LOG_FILE = OUT_DIR / "sweep.log"
ctx = zmq.Context()
cmd = ctx.socket(zmq.PUB)
cmd.connect(f"tcp://127.0.0.1:{CMD_PORT}")
tel = ctx.socket(zmq.SUB)
tel.connect(f"tcp://127.0.0.1:{TEL_PORT}")
tel.setsockopt_string(zmq.SUBSCRIBE, "")
time.sleep(1.0)
def log(m):
line = f"[{datetime.now().strftime('%H:%M:%S')}] {m}"
print(line, flush=True)
with open(LOG_FILE, "a") as f:
f.write(line + "\n")
def send(d):
cmd.send_string(json.dumps(d)); time.sleep(0.05)
def set_inj(state):
body = json.dumps({"state": state}).encode()
req = urllib.request.Request(f"{FRACTO_API}/set_injection_state",
data=body, headers={"Content-Type":"application/json"}, method="POST")
try:
with urllib.request.urlopen(req, timeout=5) as r:
return json.loads(r.read())
except Exception as e:
log(f" WARN set_inj({state}) failed: {e}")
def ask(question, timeout=180):
body = json.dumps({"question": question}).encode()
req = urllib.request.Request(f"{FRACTO_API}/ask",
data=body, headers={"Content-Type":"application/json"}, method="POST")
t0 = time.time()
try:
with urllib.request.urlopen(req, timeout=timeout) as r:
return json.loads(r.read()).get("response",""), time.time()-t0
except Exception as e:
return f"(error: {e})", time.time()-t0
def drain():
out = []
while True:
try:
out.append(json.loads(tel.recv_string(zmq.NOBLOCK)))
except zmq.Again:
return out
def summarize(frames):
if not frames: return {}
keys = list(BASELINE_MEAN.keys())
out = {}
for k in keys:
vals = [f[k] for f in frames if k in f]
if vals:
mu = statistics.mean(vals)
sd = statistics.stdev(vals) if len(vals) > 1 else 0.0
bmean = BASELINE_MEAN[k]
bstd = BASELINE_STD[k]
z_mean = (mu - bmean) / bstd if bstd else 0.0
out[k] = {
"mean": mu,
"stdev": sd,
"min": min(vals),
"max": max(vals),
"n": len(vals),
"z_vs_baseline_mean": z_mean,
"stdev_ratio_vs_baseline": (sd / bstd) if bstd else 0.0,
}
return out
def run_state(sigma):
name = f"SIGMA_{sigma}_STR_{STRENGTH}"
log(f"=== {name} dwell={DWELL_S}s every {INJ_INTERVAL_S}s ===")
set_inj("ACTIVE")
drain()
t_start = time.time()
deadline = t_start + DWELL_S
next_inj = t_start
collected = []
n_injections = 0
while time.time() < deadline:
if time.time() >= next_inj:
send({"cmd":"inject_density","x":LEFT_X,"y":CENTER_Y,"sigma":sigma,"strength":+STRENGTH})
send({"cmd":"inject_density","x":RIGHT_X,"y":CENTER_Y,"sigma":sigma,"strength":-STRENGTH})
n_injections += 1
if n_injections <= 3 or n_injections % 12 == 0:
log(f" dipole #{n_injections} at t+{int(time.time()-t_start)}s sigma={sigma} str=+/-{STRENGTH}")
next_inj = time.time() + INJ_INTERVAL_S
collected.extend(drain())
time.sleep(1)
summary = summarize(collected)
log(f" collected {len(collected)} frames; {n_injections} dipole injections")
flagged = []
for k, s in summary.items():
z = s["z_vs_baseline_mean"]
sr = s["stdev_ratio_vs_baseline"]
flag = ""
if abs(z) >= 2 or sr >= 2:
flag = " !!"
elif abs(z) >= 1 or sr >= 1.5:
flag = " !"
log(f" {k:<16} mean={s['mean']:>+12.6f} z={z:>+7.2f} std_ratio={sr:>5.2f}{flag}")
if flag:
flagged.append((k, z, sr))
q = (f"INJECTION STATE is ACTIVE. Bipolar dipoles fired at "
f"(x={LEFT_X},y={CENTER_Y}) and (x={RIGHT_X},y={CENTER_Y}) "
f"every {INJ_INTERVAL_S}s for {DWELL_S}s at sigma={sigma}, "
f"strength=+/-{STRENGTH}. Describe what you observe in 3-5 "
f"sentences. Only call out flagged channels. If nothing is "
f"flagged, say the substrate did not respond and stop.")
resp, took = ask(q)
log(f" fracto ({took:.1f}s): {resp[:300]}{'...' if len(resp)>300 else ''}")
rec = {
"ts": datetime.now().isoformat(),
"sigma": sigma, "strength": STRENGTH,
"interval_s": INJ_INTERVAL_S, "dwell_s": DWELL_S,
"n_injections": n_injections,
"n_frames": len(collected),
"summary": summary,
"flagged": flagged,
"fracto": resp,
"fracto_took_s": took,
}
with open(OUT_FILE, "a") as fh:
fh.write(json.dumps(rec) + "\n")
return rec
def main():
log(f"=== inject_strength_sweep ===")
log(f"out: {OUT_DIR}")
try:
with urllib.request.urlopen(f"{FRACTO_API}/status", timeout=5) as r:
st = json.loads(r.read())
log(f"fracto OK: model={st['model']} cycle={st['cycle']}")
except Exception as e:
log(f"FATAL: Fractonaut unreachable: {e}")
sys.exit(1)
results = []
try:
for s in SIGMAS:
results.append(run_state(s))
finally:
log("=== RESTORE baseline ===")
send({"cmd":"set_khra_amp","amp":0.030})
send({"cmd":"set_gixx_amp","amp":0.008})
set_inj("UNKNOWN")
log(" (finally) baseline restored")
log("")
log("=== SUMMARY ===")
log(f"{'sigma':>6} {'asym_z':>8} {'coh_z':>8} {'velvar_z':>10} {'sxx_z':>8} {'sxy_z':>8} {'flagged':>8}")
for r in results:
s = r["summary"]
sg = lambda k: s.get(k,{}).get("z_vs_baseline_mean", 0)
log(f"{r['sigma']:>6} {sg('asymmetry'):>+8.2f} {sg('coherence'):>+8.2f} {sg('vel_var'):>+10.2f} {sg('stress_xx'):>+8.2f} {sg('stress_xy'):>+8.2f} {len(r['flagged']):>8}")
log(f"file: {OUT_FILE}")
sys.exit(0)
if __name__ == "__main__":
main()