AsianPlayer/TriDrive / code /eval /kin_v4b_build_stats.py
AsianPlayer's picture
download
raw
38 kB
"""cwm3/eval/kin_v4b_build_stats.py — diagnostics of the hands_on / hand-event label v4b (pre-registered docs/decisions.md 2026-09-16T23:00:36Z:
v4 with the RIM-BAND distance convention d_rim = min over wrists |signed distance to the fitted ellipse|, interior NOT on; ON < 0.04, OFF > 0.08)
against v2 / v3 / v4, from the TARGET FILES (results/targets_kin_causal_routes_{v2,v3,v4,v4b}; cells present in v4b only) and the route_refs
of v4 / v4b. Imports eval/kin_v4_build_stats.py (K4: nearest_dt, fmt, constants) and eval/kin_v3_build_stats.py (K3: unique_events,
match_share) — frozen scripts imported, not copied. All numbers are computed here; nothing is hand-typed.
Sections:
1. per regime x split: rows, row_ok, ellipse defined share (v4b; asserted identical to v4), S0 finite v2 / v3 / v4 / v4b, hands_on share
v2 / v3 / v4 / v4b (nanmean of S_c[:,:,0] over the 25 future steps), hands_on at the anchor v4b, EM hand share v4b, d_rim quantiles,
head identical to v2, ellipse0_c identical to v4; the d_rim DECOMPOSITION of the ON and OFF anchors (row_ok rows with finite S0 and
d_rim): rim band d_rim < 0.04 | dead band 0.04 <= d_rim <= 0.08 | off d_rim > 0.08 (an ON anchor in the dead band is held by the
hysteresis; ON with d_rim > 0.08 or OFF with d_rim < 0.04 cannot occur); the S0 cross-tabs v4 -> v4b and v3 -> v4b on row_ok rows
where both are finite (the 'v4 ON / v4b OFF' share = anchors that were ON in v4 only because a wrist projected INSIDE the ellipse).
2. events per hour (unique (route, 0.2 s frame) events with E_c = 1 & EM_c; hours = unique EM frames x 0.2 s, per version) for hand_return /
hand_leave / head_turn_onset, v2 / v3 / v4 / v4b, and match shares within +-1 s: v3 events with a v4b event, v4b with v3, v4 with v4b,
v4b with v4, v4b with v2.
3. ellipse identity: per route, ell__<uid> / fits__<uid> / ref__<uid> of the v4b route_refs vs the v4 route_refs (mismatches expected 0);
refit / pass counts from the v4b build stats.
4. the owner-label cross-tab on the 68 v3 hand_return validation items (results/review_tool/items_relabel_v3.json, task V3E, block
hand_return; owner labels results/review_tool/labels_latest.json ids V3_*: ret_dt=<dt> -> 'return' clicked at t_event + dt, always_on,
never_on, unsure, or unlabelled): per item the nearest v4b hand_return (unique-event times, same regime's test cell) relative to t_event
and, for clicked returns, relative to the owner's click; v4b return within +-1 s of t_event (primary, the v4 convention) and of the click;
the v4b state over the page window [-2 s, +1 s] (always_on / never_on / switches / undefined), the d_rim range over the window, the number
of v4b hand events (return + leave) inside the window; v3 / v4 return within +-1 s for reference. PRE-REGISTERED EXPECTATION: the 4
clicked returns regain a v4b event within +-1 s (REQUIRED >= 3 / 4), the 6 always_on items keep none.
5. the 34 pass-3 engaged hand_return chatter events (results/review_tool/items.json task B, engaged; pass 4: 0 / 34 real): v4b hand_return
within +-1 s of t_event = t_anchor + 0.2 (chatter survival x / 34) vs v3 / v4.
Writes results/tables/kin_v4b_build_stats.md and .json (+ run json). Read-only on all inputs."""
import json, sys, time
from pathlib import Path
import numpy as np
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "common")); sys.path.insert(0, str(ROOT / "eval")); sys.path.insert(0, str(ROOT / "data"))
from runmeta import write_run_json # noqa: E402
import kin_v3_build_stats as K3 # noqa: E402 (unique_events, match_share)
import kin_v4_build_stats as K4 # noqa: E402 (nearest_dt, fmt)
import build_kin_causal_v4b as V4B # noqa: E402 (constants; OUT_V4B)
V2 = K4.V2; V3D = K4.V3D; V4D = K4.V4D; V4BD = V4B.OUT_V4B
ITEMS_V3, LABELS, ITEMS_B, TABLES = K4.ITEMS_V3, K4.LABELS, K4.ITEMS_B, K4.TABLES
EVENTS = K3.EVENTS; WIN = K4.WIN; HYST_ON, HYST_OFF = V4B.HYST_ON, V4B.HYST_OFF
fmt, nearest_dt = K4.fmt, K4.nearest_dt
REQUIRED_RETURNS = 3
def decomposition(dd):
dd = dd[np.isfinite(dd)]
return dict(n=int(len(dd)), rim_band_lt_004=float((dd < HYST_ON).mean()) if len(dd) else None, dead_band_004_008=float(((dd >= HYST_ON) & (dd <= HYST_OFF)).mean()) if len(dd) else None,
off_gt_008=float((dd > HYST_OFF).mean()) if len(dd) else None)
def s0_xtab(a, b, ok):
"""cross-tab of two anchor states on row_ok rows where both are finite -> shares of (a,b) in {(1,1),(1,0),(0,1),(0,0)} and n."""
m = ok & np.isfinite(a) & np.isfinite(b); n = int(m.sum())
if not n: return dict(n=0)
A, B = a[m] > 0.5, b[m] > 0.5
return dict(n=n, on_on=float((A & B).mean()), on_off=float((A & ~B).mean()), off_on=float((~A & B).mean()), off_off=float((~A & ~B).mean()), a_on=float(A.mean()), b_on=float(B.mean()))
def window_state(z, u, te):
rws = np.flatnonzero((z["uid"] == u) & (z["t"] >= te + WIN[0] - 1e-6) & (z["t"] <= te + WIN[1] + 1e-6)); s0w = z["S0_c"][rws, 0]; dw = z["dwheel0_c"][rws]
state = "undefined" if not np.isfinite(s0w).any() else ("always_on" if np.nanmin(s0w) == 1 else ("never_on" if np.nanmax(s0w) == 0 else "switches"))
return state, (float(np.nanmin(dw)) if np.isfinite(dw).any() else None), (float(np.nanmax(dw)) if np.isfinite(dw).any() else None), int(len(rws)), int(np.isfinite(s0w).sum())
def events_in_window(ev, ui, kf, lo=-20, hi=10):
f = ev[1][ev[0] == ui].astype(np.int64); return int(((f - kf >= lo) & (f - kf <= hi)).sum())
def main():
t0 = time.time(); TABLES.mkdir(parents=True, exist_ok=True)
cells = [(r, s) for s in ("test", "val", "train") for r in ("manual", "engaged") if (V4BD / f"kin_causal_{r}_{s}.npz").exists()]
assert cells, f"no v4b cells in {V4BD}"
res = {}; ev_by_cell = {}
for reg, sp in cells:
z2 = np.load(V2 / f"kin_causal_{reg}_{sp}.npz", allow_pickle=True); z3 = np.load(V3D / f"kin_causal_{reg}_{sp}.npz", allow_pickle=True)
z4 = np.load(V4D / f"kin_causal_{reg}_{sp}.npz", allow_pickle=True); zb = np.load(V4BD / f"kin_causal_{reg}_{sp}.npz", allow_pickle=True)
uid, t = zb["uid"], zb["t"]; N = len(t); ok = zb["row_ok"]
assert np.array_equal(uid, z2["uid"]) and np.array_equal(uid, z3["uid"]) and np.array_equal(uid, z4["uid"]) and np.allclose(t, z2["t"]) and np.array_equal(ok, z2["row_ok"]) and np.array_equal(ok, z4["row_ok"])
assert str(zb["label_version"]) == "v4b" and float(zb["hyst_on"]) == np.float32(HYST_ON) and float(zb["hyst_off"]) == np.float32(HYST_OFF)
S2, S3, S4, SB = z2["S_c"][:, :, 0], z3["S_c"][:, :, 0], z4["S_c"][:, :, 0], zb["S_c"][:, :, 0]; S02, S03, S04, S0B = z2["S0_c"][:, 0], z3["S0_c"][:, 0], z4["S0_c"][:, 0], zb["S0_c"][:, 0]
u2, h2 = K3.unique_events(uid, t, z2["E_c"], z2["EM_c"]); u3, h3 = K3.unique_events(uid, t, z3["E_c"], z3["EM_c"]); u4, h4 = K3.unique_events(uid, t, z4["E_c"], z4["EM_c"]); ub, hb = K3.unique_events(uid, t, zb["E_c"], zb["EM_c"])
ev_by_cell[(reg, sp)] = dict(u3=u3, u4=u4, ub=ub, uids=np.unique(uid), zb=zb)
D0 = zb["dwheel0_c"]; assert (D0[np.isfinite(D0)] >= 0).all(), "v4b dwheel0_c must be the non-negative rim distance"
on = ok & (S0B == 1); off = ok & (S0B == 0)
c = dict(N=int(N), n_row_ok=int(ok.sum()), row_ok_share=float(ok.mean()),
ellipse_defined_share_row_ok_v4b=float(zb["wheel_ref_defined"][ok].mean()), ellipse_defined_share_row_ok_v4=float(z4["wheel_ref_defined"][ok].mean()), point_ref_defined_share_row_ok_v3=float(z3["wheel_ref_defined"][ok].mean()),
ellipse_identical_to_v4=bool(np.array_equal(zb["ellipse0_c"], z4["ellipse0_c"], equal_nan=True) and np.array_equal(zb["wheel_ref_defined"], z4["wheel_ref_defined"])),
d_defined_identical_to_v4=bool(np.array_equal(np.isfinite(D0), np.isfinite(z4["dwheel0_c"]))),
S0_finite_share_row_ok=dict(v2=float(np.isfinite(S02[ok]).mean()), v3=float(np.isfinite(S03[ok]).mean()), v4=float(np.isfinite(S04[ok]).mean()), v4b=float(np.isfinite(S0B[ok]).mean())),
hands_on_share=dict(v2=float(np.nanmean(S2)), v3=float(np.nanmean(S3)), v4=float(np.nanmean(S4)), v4b=float(np.nanmean(SB))),
hands_on_at_anchor=dict(v2=float(np.nanmean(S02)), v3=float(np.nanmean(S03)), v4=float(np.nanmean(S04)), v4b=float(np.nanmean(S0B))),
EM_hand_share=dict(v3=float(z3["EM_c"][:, :, 0].mean()), v4=float(z4["EM_c"][:, :, 0].mean()), v4b=float(zb["EM_c"][:, :, 0].mean())),
d_rim_quantiles_row_ok=({f"q{p}": float(np.nanpercentile(D0[ok], p)) for p in (5, 25, 50, 75, 95)} if np.isfinite(D0[ok]).any() else None),
d_rim_decomposition=dict(all=decomposition(D0[ok]), on=decomposition(D0[on]), off=decomposition(D0[off]), n_on=int(on.sum()), n_off=int(off.sum())),
s0_xtab_v4_v4b=s0_xtab(S04, S0B, ok), s0_xtab_v3_v4b=s0_xtab(S03, S0B, ok), s0_xtab_v2_v4b=s0_xtab(S02, S0B, ok),
head_identical_v4b_v2=bool(np.array_equal(zb["E_c"][:, :, 2], z2["E_c"][:, :, 2]) and np.array_equal(zb["S_c"][:, :, 1], z2["S_c"][:, :, 1], equal_nan=True) and np.array_equal(zb["EM_c"][:, :, 2], z2["EM_c"][:, :, 2])),
inputs_identical_v2={k: bool(np.array_equal(zb[k], z2[k], equal_nan=True) if zb[k].dtype.kind == "f" else np.array_equal(zb[k], z2[k])) for k in ("K_c", "K0_c", "Kvalid", "Kframe", "K0valid", "row_ok", "route_decoded")},
events={})
assert c["d_rim_decomposition"]["on"]["off_gt_008"] in (None, 0.0) and c["d_rim_decomposition"]["off"]["rim_band_lt_004"] in (None, 0.0), "state inconsistent with d_rim at the anchor"
for name in EVENTS:
c["events"][name] = dict(n=dict(v2=int(len(u2[name][0])), v3=int(len(u3[name][0])), v4=int(len(u4[name][0])), v4b=int(len(ub[name][0]))), hours=dict(v2=h2[name], v3=h3[name], v4=h4[name], v4b=hb[name]),
per_hour={v: (len(u[name][0]) / h[name] if h[name] else None) for v, u, h in (("v2", u2, h2), ("v3", u3, h3), ("v4", u4, h4), ("v4b", ub, hb))},
v3_with_v4b_within_1s=K3.match_share(u3[name], ub[name]), v4b_with_v3_within_1s=K3.match_share(ub[name], u3[name]),
v4_with_v4b_within_1s=K3.match_share(u4[name], ub[name]), v4b_with_v4_within_1s=K3.match_share(ub[name], u4[name]), v4b_with_v2_within_1s=K3.match_share(ub[name], u2[name]))
res[f"{reg}_{sp}"] = c; print(f"{reg}/{sp} done {time.time()-t0:.0f}s hands_on v2/v3/v4/v4b = {c['hands_on_share']['v2']:.3f}/{c['hands_on_share']['v3']:.3f}/{c['hands_on_share']['v4']:.3f}/{c['hands_on_share']['v4b']:.3f}", flush=True)
# ---- 3. ellipse identity vs v4 (route_refs)
rrb = np.load(V4BD / "route_refs.npz", allow_pickle=True); rr4 = np.load(V4D / "route_refs.npz", allow_pickle=True)
ub_uids = [str(x) for x in rrb["uids"]]; u4_uids = set(str(x) for x in rr4["uids"])
mism = dict(ell=[], fits=[], ref=[]); n_cmp = 0; n_fr = 0; n_def = 0; n_d_neg = 0; n_d_fin = 0
for u in ub_uids:
e = rrb[f"ell__{u}"]; d = rrb[f"d4__{u}"]; n_fr += len(e); n_def += int(np.isfinite(e).all(1).sum()); fin = np.isfinite(d); n_d_fin += int(fin.sum()); n_d_neg += int((d[fin] < 0).sum())
if u not in u4_uids: continue
n_cmp += 1
if not np.array_equal(e, rr4[f"ell__{u}"], equal_nan=True): mism["ell"].append(u)
if not np.array_equal(rrb[f"fits__{u}"], rr4[f"fits__{u}"], equal_nan=True): mism["fits"].append(u)
if not np.array_equal(rrb[f"ref__{u}"], rr4[f"ref__{u}"], equal_nan=True): mism["ref"].append(u)
bs = json.load(open(V4BD / "build_stats_latest.json")) if (V4BD / "build_stats_latest.json").exists() else {}
bs4 = json.load(open(V4D / "build_stats_latest.json")) if (V4D / "build_stats_latest.json").exists() else {}
geo = dict(n_routes_v4b=len(ub_uids), n_routes_compared=n_cmp, n_mismatch_ell=len(mism["ell"]), n_mismatch_fits=len(mism["fits"]), n_mismatch_ref=len(mism["ref"]), mismatch_routes=mism,
pose_frames=n_fr, ellipse_defined_frames=n_def, ellipse_defined_frame_share=n_def / n_fr if n_fr else None, d_rim_finite_frames=n_d_fin, d_rim_negative_frames=n_d_neg,
refits=dict(v4b=bs.get("n_refits"), v4=bs4.get("n_refits")), refits_passed=dict(v4b=bs.get("n_refits_passed"), v4=bs4.get("n_refits_passed")),
routes_ellipse_never_defined=dict(v4b=len(bs.get("routes_ellipse_never_defined") or []), v4=len(bs4.get("routes_ellipse_never_defined") or [])),
raw_transitions_10hz=dict(v4b=bs.get("n_10hz_raw_transitions"), v4=bs4.get("n_10hz_raw_transitions")), confirmed_return_10hz=dict(v4b=bs.get("n_10hz_confirmed_return"), v4=bs4.get("n_10hz_confirmed_return")),
confirmed_leave_10hz=dict(v4b=bs.get("n_10hz_confirmed_leave"), v4=bs4.get("n_10hz_confirmed_leave")))
assert n_d_neg == 0, "d4__ must be non-negative in v4b"
print(f"ellipse identity vs v4: {n_cmp} routes compared, mismatches ell {len(mism['ell'])} fits {len(mism['fits'])} ref {len(mism['ref'])} ({time.time()-t0:.0f}s)", flush=True)
# ---- 4. owner-label cross-tab on the 68 v3 hand_return items
obs = None
if ITEMS_V3.exists() and ("engaged", "test") in ev_by_cell and ("manual", "test") in ev_by_cell:
items = [i for i in json.load(open(ITEMS_V3, encoding="utf-8"))["items"] if i.get("task") == "V3E" and i["meta"].get("block") == "hand_return"]
labels = json.load(open(LABELS)) if LABELS.exists() else {}
rows = []
for it in sorted(items, key=lambda i: i["id"]):
m = it["meta"]; reg = m["regime"]; u = m["uid"]; te = float(m["t_event"]); kf = int(round(te * 10))
ev = ev_by_cell[(reg, "test")]; uidx = {x: i for i, x in enumerate(ev["uids"])}; ui = uidx.get(u, -1); zb = ev["zb"]
dtb, hitb = nearest_dt(ev["ub"]["hand_return"][0], ev["ub"]["hand_return"][1], ui, kf); dt4, hit4 = nearest_dt(ev["u4"]["hand_return"][0], ev["u4"]["hand_return"][1], ui, kf); dt3, hit3 = nearest_dt(ev["u3"]["hand_return"][0], ev["u3"]["hand_return"][1], ui, kf)
lab = (labels.get(it["id"]) or {}).get("label", ""); cls = "unlabelled" if not lab else ("return" if lab.startswith("ret_dt=") else lab)
click_dt = float(lab.split("=", 1)[1]) if cls == "return" else None
dtb_click, hitb_click = (nearest_dt(ev["ub"]["hand_return"][0], ev["ub"]["hand_return"][1], ui, int(round((te + click_dt) * 10))) if click_dt is not None else (None, None))
state, dmin, dmax, n_rows, n_fin = window_state(zb, u, te)
n_ev_win = events_in_window(ev["ub"]["hand_return"], ui, kf) + events_in_window(ev["ub"]["hand_leave"], ui, kf)
rows.append(dict(id=it["id"], regime=reg, uid=u, t_event=te, owner_label=lab, owner_class=cls, owner_click_dt=click_dt,
v3_return_within_1s=hit3, v4_return_within_1s=hit4, v4b_return_within_1s=hitb, nearest_v4b_return_dt_s=dtb,
v4b_return_within_1s_of_click=hitb_click, nearest_v4b_return_dt_from_click_s=dtb_click,
v4b_state_window=state, v4b_d_rim_window_min=dmin, v4b_d_rim_window_max=dmax, v4b_window_rows=n_rows, v4b_window_rows_finite=n_fin, v4b_events_in_window=n_ev_win,
v4b_d_rim_at_anchor=float(zb["dwheel0_c"][m["row"]]) if "row" in m else None))
def xtab(sel):
out = {}
for cls in ("return", "always_on", "never_on", "unsure", "unlabelled"):
rs = [r for r in sel if r["owner_class"] == cls]
if rs: out[cls] = dict(n=len(rs), v4b_return_within_1s=int(sum(r["v4b_return_within_1s"] for r in rs)), v4b_return_within_1s_of_click=(int(sum(bool(r["v4b_return_within_1s_of_click"]) for r in rs)) if cls == "return" else None),
v4_return_within_1s=int(sum(r["v4_return_within_1s"] for r in rs)), v3_return_within_1s=int(sum(r["v3_return_within_1s"] for r in rs)),
v4b_always_on_window=int(sum(r["v4b_state_window"] == "always_on" for r in rs)), v4b_never_on_window=int(sum(r["v4b_state_window"] == "never_on" for r in rs)),
v4b_switches_window=int(sum(r["v4b_state_window"] == "switches" for r in rs)), v4b_undefined_window=int(sum(r["v4b_state_window"] == "undefined" for r in rs)),
v4b_any_event_in_window=int(sum(r["v4b_events_in_window"] > 0 for r in rs)))
return out
ret = [r for r in rows if r["owner_class"] == "return"]; aon = [r for r in rows if r["owner_class"] == "always_on"]; non = [r for r in rows if r["owner_class"] == "never_on"]
n_ret_regained = int(sum(r["v4b_return_within_1s"] for r in ret)); n_ret_regained_click = int(sum(bool(r["v4b_return_within_1s_of_click"]) for r in ret))
n_aon_event = int(sum(r["v4b_return_within_1s"] for r in aon)); n_aon_any_event_win = int(sum(r["v4b_events_in_window"] > 0 for r in aon))
obs = dict(n=len(rows), n_labelled=int(sum(r["owner_class"] != "unlabelled" for r in rows)),
n_return_within_1s=dict(v3=int(sum(r["v3_return_within_1s"] for r in rows)), v4=int(sum(r["v4_return_within_1s"] for r in rows)), v4b=int(sum(r["v4b_return_within_1s"] for r in rows))),
n_v4b_state_window={s: int(sum(r["v4b_state_window"] == s for r in rows)) for s in ("always_on", "never_on", "switches", "undefined")},
by_regime={reg: dict(n=len([r for r in rows if r["regime"] == reg]), v4b_return_within_1s=int(sum(r["v4b_return_within_1s"] for r in rows if r["regime"] == reg)),
v4_return_within_1s=int(sum(r["v4_return_within_1s"] for r in rows if r["regime"] == reg)), xtab=xtab([r for r in rows if r["regime"] == reg])) for reg in ("manual", "engaged")},
xtab_all=xtab(rows),
prereg=dict(n_clicked_returns=len(ret), returns_regained_within_1s_of_t_event=n_ret_regained, returns_regained_within_1s_of_click=n_ret_regained_click, required=REQUIRED_RETURNS,
returns_pass=bool(n_ret_regained >= REQUIRED_RETURNS), returns_pass_click=bool(n_ret_regained_click >= REQUIRED_RETURNS),
n_always_on=len(aon), always_on_with_v4b_return_within_1s=n_aon_event, always_on_with_any_v4b_event_in_window=n_aon_any_event_win,
always_on_window_state={s: int(sum(r["v4b_state_window"] == s for r in aon)) for s in ("always_on", "never_on", "switches", "undefined")},
always_on_pass=bool(n_aon_event == 0),
n_never_on=len(non), never_on_with_v4b_return_within_1s=int(sum(r["v4b_return_within_1s"] for r in non)), never_on_with_any_v4b_event_in_window=int(sum(r["v4b_events_in_window"] > 0 for r in non)),
never_on_window_state={s: int(sum(r["v4b_state_window"] == s for r in non)) for s in ("always_on", "never_on", "switches", "undefined")}),
items=rows)
obs["prereg"]["holds"] = bool(obs["prereg"]["returns_pass"] and obs["prereg"]["always_on_pass"])
p = obs["prereg"]
print(f"68-item test: v4b return within 1 s {obs['n_return_within_1s']['v4b']}/{obs['n']} (v4 {obs['n_return_within_1s']['v4']}, v3 {obs['n_return_within_1s']['v3']}); clicked returns regained {p['returns_regained_within_1s_of_t_event']}/{p['n_clicked_returns']} "
f"(of click {p['returns_regained_within_1s_of_click']}/{p['n_clicked_returns']}; required >= {REQUIRED_RETURNS}); always_on with a v4b return {p['always_on_with_v4b_return_within_1s']}/{p['n_always_on']}; "
f"never_on with a v4b return {p['never_on_with_v4b_return_within_1s']}/{p['n_never_on']}; pre-registered expectation holds: {p['holds']} ({time.time()-t0:.0f}s)", flush=True)
# ---- 5. the 34 pass-3 engaged chatter events
rev = None
if ITEMS_B.exists() and ("engaged", "test") in ev_by_cell:
sel = sorted([i for i in json.load(open(ITEMS_B))["items"] if i["task"] == "B" and i["meta"].get("event") == "hand_return" and i["meta"].get("regime") == "engaged"], key=lambda i: i["id"])
ev = ev_by_cell[("engaged", "test")]; uidx = {x: i for i, x in enumerate(ev["uids"])}
rws = []
for it in sel:
m = it["meta"]; te = float(m["t_anchor"]) + 0.2; kf = int(round(te * 10)); ui = uidx.get(m["uid"], -1)
dtb, hitb = nearest_dt(ev["ub"]["hand_return"][0], ev["ub"]["hand_return"][1], ui, kf); dt4, hit4 = nearest_dt(ev["u4"]["hand_return"][0], ev["u4"]["hand_return"][1], ui, kf); dt3, hit3 = nearest_dt(ev["u3"]["hand_return"][0], ev["u3"]["hand_return"][1], ui, kf)
rws.append(dict(id=it["id"], uid=m["uid"], t_event=te, v3_return_within_1s=hit3, v4_return_within_1s=hit4, v4b_return_within_1s=hitb, nearest_v4b_return_dt_s=dtb))
rev = dict(n=len(rws), n_return_within_1s=dict(v3=int(sum(r["v3_return_within_1s"] for r in rws)), v4=int(sum(r["v4_return_within_1s"] for r in rws)), v4b=int(sum(r["v4b_return_within_1s"] for r in rws))), items=rws)
print(f"34 pass-3 engaged chatter events: v4b hand_return within +-1 s {rev['n_return_within_1s']['v4b']}/{rev['n']} (v4 {rev['n_return_within_1s']['v4']}, v3 {rev['n_return_within_1s']['v3']})", flush=True)
# ---- provenance
prov = {k: bs.get(k) for k in ("refit_frames", "refit_s", "n_refits", "n_refits_passed", "n_refits_fail_points", "n_refits_fail_coverage", "n_refits_no_candidate", "fit_wall_s", "ellipse_undefined_frame_frac", "d_undefined_frame_frac",
"n_manual_moving_frames", "n_pose_frames", "routes_ellipse_never_defined", "n_10hz_confirmed_return", "n_10hz_confirmed_leave", "n_10hz_raw_transitions", "label_version", "wheel_ref_rule")}
prov["split_wall_s"] = {k: v.get("wall_s") for k, v in bs.get("splits", {}).items()}
prov["v2_identity_checks_all_true"] = bool(all(all(v.get("v2_identity_checks", {}).values()) for v in bs.get("splits", {}).values())) if bs.get("splits") else None
prov["kinhist_files"] = {k: v.get("kinhist_file") for k, v in bs.get("splits", {}).items()}
tf = sorted(V4BD.glob("logs/build_all*.time"))
if tf:
txt = tf[-1].read_text(); rss = [l for l in txt.splitlines() if "Maximum resident" in l]; wall = [l for l in txt.splitlines() if "Elapsed (wall clock)" in l]
prov["max_rss_kb"] = int(rss[0].split()[-1]) if rss else None; prov["wall_clock"] = wall[0].split("): ")[-1].strip() if wall else None
# ---- markdown
stamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
L = [f"# kin causal targets v2 / v3 / v4 / v4b — build statistics and v4b diagnostics ({stamp})", "",
f"Source: `eval/kin_v4b_build_stats.py` on `{V2.relative_to(ROOT)}`, `{V3D.relative_to(ROOT)}`, `{V4D.relative_to(ROOT)}`, `{V4BD.relative_to(ROOT)}` (target files; cells present in v4b only) and the v4 / v4b `route_refs.npz`. "
"v4b = pre-registered hands_on / hand-event label v4b (docs/decisions.md 2026-09-16T23:00:36Z): IDENTICAL to v4 (per-device causal RANSAC + direct-LS ELLIPSE fit of the rim from both wrists over manual moving frames, gate >= 200 inliers & >= 90 deg arc, "
"undefined -> masked, no fallback; refit cadence 50 frames; hysteresis ON < 0.04 / OFF > 0.08, reset after > 1 s gap; >= 1 s persistence both sides; causal S0; head and all inputs unchanged) EXCEPT the state distance: "
"d_rim = min over wrists of |signed distance to the fitted ellipse| (rim band on both sides of the rim; the interior is NOT on) instead of v4's signed distance to the filled ellipse (interior on).", "",
f"Build provenance (v4b): label_version {prov.get('label_version')}; refit every {prov.get('refit_frames')} new manual moving frames ({prov.get('refit_s')} s); {prov.get('n_refits'):,} refits, {prov.get('n_refits_passed'):,} passed the gate "
f"({prov.get('n_refits_fail_points'):,} failed < 200 inliers, {prov.get('n_refits_fail_coverage'):,} failed the 90-deg coverage, {prov.get('n_refits_no_candidate'):,} no candidate); ellipse fitting {prov.get('fit_wall_s')} s; "
f"ellipse undefined on {100*prov.get('ellipse_undefined_frame_frac', float('nan')):.2f} % of the {prov.get('n_pose_frames'):,} pose frames ({prov.get('n_manual_moving_frames'):,} manual moving); routes with the ellipse never defined: {len(prov.get('routes_ellipse_never_defined') or [])}; "
f"10 Hz raw transitions {prov.get('n_10hz_raw_transitions'):,} -> confirmed return {prov.get('n_10hz_confirmed_return'):,} / leave {prov.get('n_10hz_confirmed_leave'):,} (v4: {geo['raw_transitions_10hz']['v4']:,} -> {geo['confirmed_return_10hz']['v4']:,} / {geo['confirmed_leave_10hz']['v4']:,}); "
f"per-split wall {prov.get('split_wall_s')}; v2 identity checks all true in the build: {prov.get('v2_identity_checks_all_true')}; kinhist {prov.get('kinhist_files')}; max RSS {prov.get('max_rss_kb')} kB, wall clock {prov.get('wall_clock')} (single invocation, all splits).", "",
"Definitions: hands_on share = nanmean of `S_c[:,:,0]` over the 25 future steps of all rows; S0 finite = share of row_ok rows with a defined hands_on state at the anchor; defined = wheel reference (v3: point; v4 / v4b: ellipse) defined at the anchor; "
"events = unique (route, 0.2 s grid frame) with `E_c = 1 & EM_c`; hours = unique (route, frame) with `EM_c` True x 0.2 s (per version); match = share of unique events with an event of the same type on the same route within +-1.0 s in the other version.", "",
"## 1. Coverage and state shares", "",
"| regime | split | rows | row_ok | ellipse defined v4b (= v4) | point ref defined v3 | S0 finite v2 / v3 / v4 / v4b | hands_on share v2 | hands_on share v3 | hands_on share v4 | hands_on share v4b | hands_on at anchor v4b | EM hand share v3 / v4 / v4b | d_rim median [q5, q95] | head = v2 | inputs = v2 | ellipse = v4 |",
"|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|"]
for key, c in res.items():
reg, sp = key.split("_"); dq = c["d_rim_quantiles_row_ok"]; sf = c["S0_finite_share_row_ok"]; hs = c["hands_on_share"]; em = c["EM_hand_share"]
L.append(f"| {reg} | {sp} | {c['N']:,} | {c['n_row_ok']:,} ({100*c['row_ok_share']:.2f} %) | {100*c['ellipse_defined_share_row_ok_v4b']:.2f} % | {100*c['point_ref_defined_share_row_ok_v3']:.2f} % | {100*sf['v2']:.2f} / {100*sf['v3']:.2f} / {100*sf['v4']:.2f} / {100*sf['v4b']:.2f} % | "
f"{hs['v2']:.3f} | {hs['v3']:.3f} | {hs['v4']:.3f} | **{hs['v4b']:.3f}** | {c['hands_on_at_anchor']['v4b']:.3f} | {em['v3']:.4f} / {em['v4']:.4f} / {em['v4b']:.4f} | {dq['q50']:.3f} [{dq['q5']:.3f}, {dq['q95']:.3f}] | {c['head_identical_v4b_v2']} | {all(c['inputs_identical_v2'].values())} | {c['ellipse_identical_to_v4']} |")
L += ["", f"Decomposition of the v4b anchor rim distance d_rim over row_ok rows with a defined state (ON = S0 hands_on 1, OFF = 0): rim band d_rim < {HYST_ON} | dead band {HYST_ON}..{HYST_OFF} (state held by the hysteresis) | off d_rim > {HYST_OFF}. "
"By construction an ON anchor never has d_rim > 0.08 and an OFF anchor never d_rim < 0.04 (asserted).", "",
"| regime | split | ON anchors n | ON: rim band | ON: dead band | OFF anchors n | OFF: dead band | OFF: off | all finite n | all: rim band | all: dead band | all: off |", "|---|---|---|---|---|---|---|---|---|---|---|---|"]
for key, c in res.items():
reg, sp = key.split("_"); d = c["d_rim_decomposition"]; o, f_, a = d["on"], d["off"], d["all"]
L.append(f"| {reg} | {sp} | {o['n']:,} | {fmt(100*o['rim_band_lt_004'] if o['rim_band_lt_004'] is not None else None, 1)} % | {fmt(100*o['dead_band_004_008'] if o['dead_band_004_008'] is not None else None, 1)} % | {f_['n']:,} | "
f"{fmt(100*f_['dead_band_004_008'] if f_['dead_band_004_008'] is not None else None, 1)} % | {fmt(100*f_['off_gt_008'] if f_['off_gt_008'] is not None else None, 1)} % | {a['n']:,} | {100*a['rim_band_lt_004']:.1f} % | {100*a['dead_band_004_008']:.1f} % | {100*a['off_gt_008']:.1f} % |")
L += ["", "Anchor-state cross-tabs on row_ok rows where both versions have a defined S0 (shares of the compared rows; 'v4 ON / v4b OFF' = anchors ON in v4 only because a wrist projected inside the ellipse):", "",
"| regime | split | n (v4, v4b) | v4 ON / v4b ON | v4 ON / v4b OFF | v4 OFF / v4b ON | v4 OFF / v4b OFF | n (v3, v4b) | v3 ON / v4b ON | v3 ON / v4b OFF | v3 OFF / v4b ON | v3 OFF / v4b OFF |", "|---|---|---|---|---|---|---|---|---|---|---|---|"]
for key, c in res.items():
reg, sp = key.split("_"); x4, x3 = c["s0_xtab_v4_v4b"], c["s0_xtab_v3_v4b"]
L.append(f"| {reg} | {sp} | {x4['n']:,} | {100*x4['on_on']:.1f} % | **{100*x4['on_off']:.1f} %** | {100*x4['off_on']:.1f} % | {100*x4['off_off']:.1f} % | {x3['n']:,} | {100*x3['on_on']:.1f} % | {100*x3['on_off']:.1f} % | {100*x3['off_on']:.1f} % | {100*x3['off_off']:.1f} % |")
L += ["", "## 2. Events per hour (unique events on the 0.2 s grid) and cross-version matching within +-1 s", "",
"| regime | split | event | n v2 | n v3 | n v4 | n v4b | hours v4b | per hour v2 | per hour v3 | per hour v4 | per hour v4b | v3 events with v4b within 1 s | v4b with v3 | v4 with v4b | v4b with v4 | v4b with v2 |", "|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|"]
for key, c in res.items():
reg, sp = key.split("_")
for name in EVENTS:
e = c["events"][name]; n = e["n"]; ph = e["per_hour"]
L.append(f"| {reg} | {sp} | {name} | {n['v2']:,} | {n['v3']:,} | {n['v4']:,} | {n['v4b']:,} | {e['hours']['v4b']:.1f} | {fmt(ph['v2'], 2)} | {fmt(ph['v3'], 2)} | {fmt(ph['v4'], 2)} | **{fmt(ph['v4b'], 2)}** | {fmt(e['v3_with_v4b_within_1s'])} | {fmt(e['v4b_with_v3_within_1s'])} | {fmt(e['v4_with_v4b_within_1s'])} | {fmt(e['v4b_with_v4_within_1s'])} | {fmt(e['v4b_with_v2_within_1s'])} |")
L += ["", "## 3. Ellipse identity vs v4 (route_refs) and ellipse coverage", "",
f"Routes in the v4b route_refs {geo['n_routes_v4b']}; compared with v4 {geo['n_routes_compared']}; mismatching routes — ellipse series `ell__` **{geo['n_mismatch_ell']}**, fit log `fits__` **{geo['n_mismatch_fits']}**, channel reference `ref__` **{geo['n_mismatch_ref']}** (expected 0: same pose, same RNG, same gate). "
f"Refits v4b / v4 {geo['refits']['v4b']:,} / {geo['refits']['v4']:,}, passed {geo['refits_passed']['v4b']:,} / {geo['refits_passed']['v4']:,}; routes with the ellipse never defined {geo['routes_ellipse_never_defined']['v4b']} / {geo['routes_ellipse_never_defined']['v4']}. "
f"Ellipse defined on {100*geo['ellipse_defined_frame_share']:.2f} % of the {geo['pose_frames']:,} pose frames; d_rim finite on {geo['d_rim_finite_frames']:,} frames, negative on {geo['d_rim_negative_frames']} (must be 0). "
"Per-device ellipse geometry: unchanged from v4, see `results/tables/kin_v4_build_stats.md` section 3."]
if obs is not None:
p = obs["prereg"]
L += ["", "## 4. Owner-label cross-tab: the 68 v3 hand_return validation items (relabel_v3.html; owner labels in labels_latest.json, ids V3_*)", "",
f"Pre-registered expectation (decisions row 23:00:36Z): the owner's {p['n_clicked_returns']} clicked returns regain a v4b hand_return within +-1 s (REQUIRED >= {REQUIRED_RETURNS} / {p['n_clicked_returns']}), the {p['n_always_on']} always_on items keep none. ",
f"**Result: clicked returns regained {p['returns_regained_within_1s_of_t_event']} / {p['n_clicked_returns']} within +-1 s of t_event ({p['returns_regained_within_1s_of_click']} / {p['n_clicked_returns']} within +-1 s of the owner's click) -> {'PASS' if p['returns_pass'] else 'FAIL'}; "
f"always_on items with a v4b hand_return within +-1 s {p['always_on_with_v4b_return_within_1s']} / {p['n_always_on']} (with any v4b hand event in the [-2, +1] s window {p['always_on_with_any_v4b_event_in_window']} / {p['n_always_on']}; window state {p['always_on_window_state']}) -> {'PASS' if p['always_on_pass'] else 'FAIL'}; "
f"never_on items with a v4b hand_return within +-1 s (false event) {p['never_on_with_v4b_return_within_1s']} / {p['n_never_on']} (any event in window {p['never_on_with_any_v4b_event_in_window']} / {p['n_never_on']}; window state {p['never_on_window_state']}). "
f"Pre-registered expectation holds: {'YES' if p['holds'] else 'NO'}.**", "",
f"All 68 items: v4b hand_return within +-1 s of t_event **{obs['n_return_within_1s']['v4b']} / {obs['n']}** (v4 {obs['n_return_within_1s']['v4']} / {obs['n']}, v3 {obs['n_return_within_1s']['v3']} / {obs['n']}); v4b state over the page window [-2 s, +1 s]: {obs['n_v4b_state_window']}.", "",
"| regime | n | v4b return within 1 s | v4 return within 1 s |", "|---|---|---|---|"]
for reg, b in obs["by_regime"].items(): L.append(f"| {reg} | {b['n']} | {b['v4b_return_within_1s']} | {b['v4_return_within_1s']} |")
L += ["", f"Cross-tab against the owner's labels ({obs['n_labelled']} / {obs['n']} labelled at generation time; 'of click' = within +-1 s of t_event + ret_dt, return class only):", "",
"| regime | owner label class | n | v4b return within 1 s | v4b return within 1 s of click | v4 return within 1 s | v3 return within 1 s | v4b ON throughout window | v4b never ON in window | v4b switches in window | v4b undefined in window | v4b any hand event in window |", "|---|---|---|---|---|---|---|---|---|---|---|---|"]
for reg, b in obs["by_regime"].items():
for cls, x in b["xtab"].items(): L.append(f"| {reg} | {cls} | {x['n']} | {x['v4b_return_within_1s']} | {'—' if x['v4b_return_within_1s_of_click'] is None else x['v4b_return_within_1s_of_click']} | {x['v4_return_within_1s']} | {x['v3_return_within_1s']} | {x['v4b_always_on_window']} | {x['v4b_never_on_window']} | {x['v4b_switches_window']} | {x['v4b_undefined_window']} | {x['v4b_any_event_in_window']} |")
for cls, x in obs["xtab_all"].items(): L.append(f"| both | {cls} | {x['n']} | {x['v4b_return_within_1s']} | {'—' if x['v4b_return_within_1s_of_click'] is None else x['v4b_return_within_1s_of_click']} | {x['v4_return_within_1s']} | {x['v3_return_within_1s']} | {x['v4b_always_on_window']} | {x['v4b_never_on_window']} | {x['v4b_switches_window']} | {x['v4b_undefined_window']} | {x['v4b_any_event_in_window']} |")
L += ["", "| item | regime | uid | t_event | owner label | v3 ret | v4 ret | v4b return within 1 s | nearest v4b return (s) | nearest v4b return from click (s) | v4b state in window | v4b d_rim at anchor | v4b d_rim in window [min, max] | v4b hand events in window |", "|---|---|---|---|---|---|---|---|---|---|---|---|---|---|"]
for r in obs["items"]:
L.append(f"| {r['id']} | {r['regime']} | {r['uid']} | {r['t_event']:.1f} | {r['owner_label'] or '—'} | {r['v3_return_within_1s']} | {r['v4_return_within_1s']} | {'**True**' if r['v4b_return_within_1s'] else 'False'} | {'—' if r['nearest_v4b_return_dt_s'] is None else f'{r['nearest_v4b_return_dt_s']:+.1f}'} | "
f"{'—' if r['nearest_v4b_return_dt_from_click_s'] is None else f'{r['nearest_v4b_return_dt_from_click_s']:+.1f}'} | {r['v4b_state_window']} | {fmt(r['v4b_d_rim_at_anchor'])} | [{fmt(r['v4b_d_rim_window_min'])}, {fmt(r['v4b_d_rim_window_max'])}] | {r['v4b_events_in_window']} |")
if rev is not None:
L += ["", "## 5. The 34 pass-3 engaged hand_return chatter events (pass 4: 0 / 34 real returns) — chatter survival", "",
f"v4b hand_return within +-1 s of t_event = t_anchor + 0.2 (engaged/test, unique-event times): **{rev['n_return_within_1s']['v4b']} / {rev['n']}** (v4 {rev['n_return_within_1s']['v4']} / {rev['n']}, v3 {rev['n_return_within_1s']['v3']} / {rev['n']}).", "",
"| item | uid | t_event | v3 return within 1 s | v4 return within 1 s | v4b return within 1 s | nearest v4b return (s) |", "|---|---|---|---|---|---|---|"]
for r in rev["items"]:
L.append(f"| {r['id']} | {r['uid']} | {r['t_event']:.1f} | {r['v3_return_within_1s']} | {r['v4_return_within_1s']} | {r['v4b_return_within_1s']} | {'—' if r['nearest_v4b_return_dt_s'] is None else f'{r['nearest_v4b_return_dt_s']:+.1f}'} |")
L += ["", f"Build provenance: `{V4BD.relative_to(ROOT)}/build_stats_latest.json`, `run_build_kin_causal_routes_v4b_*.json`, `logs/build_all.log` / `.time`; v4 reference `results/tables/kin_v4_build_stats.md`."]
(TABLES / "kin_v4b_build_stats.md").write_text("\n".join(L) + "\n")
json.dump(dict(generated_utc=stamp, cells=res, ellipse_identity=geo, owner_observation_68=obs, reviewed_34=rev, provenance=prov), open(TABLES / "kin_v4b_build_stats.json", "w"), indent=1)
write_run_json(TABLES, "kin_v4b_build_stats", [Path(__file__), Path(K3.__file__), Path(K4.__file__), Path(V4B.__file__)],
[D / f"kin_causal_{r}_{s}.npz" for D in (V4BD, V4D, V3D, V2) for r, s in cells] + [V4BD / "route_refs.npz", V4D / "route_refs.npz"] + [p for p in (ITEMS_V3, LABELS, ITEMS_B) if p.exists()], None, t0,
extra=dict(cells=[f"{r}_{s}" for r, s in cells], ellipse_identity={k: v for k, v in geo.items() if k != "mismatch_routes"}, owner_observation_68=None if obs is None else {k: v for k, v in obs.items() if k != "items"},
reviewed_34=None if rev is None else {k: v for k, v in rev.items() if k != "items"}, provenance=prov), input_listing=False)
print(f"wrote {TABLES / 'kin_v4b_build_stats.md'} ({time.time()-t0:.0f}s)", flush=True)
if __name__ == "__main__":
main()

Xet Storage Details

Size:
38 kB
·
Xet hash:
87f7a1d56ffd4e2cc221ba1ff22180f5919943147c2d1da7dae6b0dc6f5c3abd

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.