213 lines
7.6 KiB
Python
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()
|