Buckets:
| """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.