Buckets:
| """cwm3/eval/kin_v4_build_stats.py — diagnostics of the hands_on / hand-event label v4 (pre-registered docs/decisions.md 2026-09-16T22:18:02Z) | |
| against v2 and v3, from the TARGET FILES (results/targets_kin_causal_routes_{v2,v3,v4}; cells present in v4 only) and the v4 fit log. | |
| Sections (all numbers computed here; nothing hand-typed): | |
| 1. per regime x split: rows, row_ok, wheel-ellipse defined share (v4) vs wheel-point-reference defined share (v3), S0 finite v2/v3/v4, | |
| hands_on share v2/v3/v4 (nanmean of S_c[:,:,0] over the 25 future steps), EM hand share v4, and the decomposition of the anchor signed | |
| distance d0 (row_ok rows with finite d0): deep interior d0 < -0.04 | inside near the rim -0.04 <= d0 < 0 | rim band 0 <= d0 < 0.04 | | |
| dead band 0.04 <= d0 <= 0.08 | off d0 > 0.08 — i.e. how much of the ON state comes from a wrist projecting INSIDE the wheel ellipse | |
| (lap / hub behind the wheel in 2-D) rather than from the rim band; | |
| 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) for hand_return / hand_leave / | |
| head_turn_onset, v2 / v3 / v4, and the share of v3 events with a v4 event of the same type within +-1 s (and vice versa); | |
| 3. ellipse geometry sanity per device from route_refs.npz fits__<uid>: refits, passed, pass share, share of the device's pose frames / | |
| manual moving frames with a defined ellipse, median [IQR] of a, b, b/a, centre, coverage over the passed fits; FLAG when the median a or b | |
| of the passed fits is < 0.05 or > 0.6, or any passed fit has a semi-axis outside that range (the RANSAC candidate range is [0.05, 0.6], | |
| so the second condition can only fire through the LO refit); | |
| 4. the owner-observation test on the 68 v3 hand_return validation items (results/review_tool/items_relabel_v3.json, block hand_return): | |
| (i) excursion of the v3 OFF run preceding the event from the v3 POINT reference (max of the v3 min-wrist distance over the OFF run; | |
| recomputed from the v3 route_refs wheel reference + pose), its duration, median and share < 0.25 (= rim sliding), and | |
| (ii) whether a v4 hand_return exists within +-1 s of t_event (unique-event times, same regime's test file) and whether the v4 state is | |
| ON throughout the page window [-2 s, +1 s], the range of the v4 signed distance d over that window and the share of window anchors with a | |
| wrist inside the ellipse (d < 0); cross-tabulated against the owner's labels in results/review_tool/labels_latest.json | |
| (V3_* ids: ret_dt=<dt> -> 'return', always_on, never_on, unsure, or unlabelled); | |
| 5. the 34 pass-3 engaged hand_return chatter events (results/review_tool/items.json, task B, engaged): share with a v4 hand_return within +-1 s. | |
| Writes results/tables/kin_v4_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; frozen v3 script imported, not copied) | |
| import build_kin_causal as BK # noqa: E402 | |
| import build_kin_causal_v3 as V3 # noqa: E402 | |
| import build_kin_causal_v4 as V4 # noqa: E402 | |
| V2 = ROOT / "results" / "targets_kin_causal_routes_v2"; V3D = ROOT / "results" / "targets_kin_causal_routes_v3"; V4D = ROOT / "results" / "targets_kin_causal_routes_v4" | |
| ITEMS_V3 = ROOT / "results" / "review_tool" / "items_relabel_v3.json"; LABELS = ROOT / "results" / "review_tool" / "labels_latest.json"; ITEMS_B = ROOT / "results" / "review_tool" / "items.json" | |
| TABLES = ROOT / "results" / "tables" | |
| EVENTS = K3.EVENTS; H = K3.H | |
| AX_LO, AX_HI = 0.05, 0.6; EXC_TH = 0.25; WIN = (-2.0, 1.0) | |
| def pose_path(u): | |
| p = V4.POSE_PREF / f"{u}.npz" | |
| return p if p.exists() else BK.POSE_R / f"{u}.npz" | |
| def fmt(x, p=3): | |
| return "—" if x is None or (isinstance(x, float) and not np.isfinite(x)) else f"{x:.{p}f}" | |
| def nearest_dt(frames_uid, frames_k, ui, kf): | |
| f = frames_k[frames_uid == ui].astype(np.int64) | |
| if not len(f): return None, False | |
| d = f - kf; j = np.abs(d).argmin(); return float(d[j] / 10.0), bool(abs(d[j]) <= 10) | |
| def v3_off_run_excursion(u, t_event, rr3): | |
| """Recompute the v3 series of route u (v3 point reference from route_refs + pose): returns dict(excursion, off_run_s, d_at_event, state_at_event) | |
| for the OFF run that precedes the ON run containing the event frame (stamp = first ON frame; t_event grid step covers frames ke-1, ke).""" | |
| z = np.load(pose_path(u)); t = z["t"].astype(np.float64); F = np.isfinite(z["box"][:, 0]) & np.isfinite(z["kpts"][:, 0, 2].astype(np.float32)) | |
| raw = BK.raw_from_kpts(z["kpts"], F); wref = rr3[f"wheel_ref__{u}"]; d = V3.wheel_distance(raw["q"], wref); d[~F] = np.nan | |
| k = np.round(t * 10).astype(np.int64); k0 = int(k.min()); M = int(k.max()) - k0 + 1; DD = np.full(M, np.nan, np.float32); DD[k - k0] = d | |
| s_raw, s_held = V3.hyst_hands(DD); ke = int(round(t_event * 10)) - k0 | |
| out = dict(excursion=None, off_run_s=None, d_at_event=float(DD[ke]) if 0 <= ke < M else None, found=False) | |
| if not (0 <= ke < M): return out | |
| kon = ke if (np.isfinite(s_raw[ke]) and s_raw[ke] == 1) else (ke - 1 if ke >= 1 and np.isfinite(s_raw[ke - 1]) and s_raw[ke - 1] == 1 else None) | |
| if kon is None: return out | |
| while kon - 1 >= 0 and s_held[kon - 1] == 1: kon -= 1 # start of the ON run (held across short gaps) | |
| j = kon - 1 | |
| if j < 0 or not (s_held[j] == 0): return out | |
| i = j | |
| while i - 1 >= 0 and s_held[i - 1] == 0: i -= 1 # start of the OFF run | |
| seg = DD[i:j + 1] | |
| out.update(excursion=float(np.nanmax(seg)) if np.isfinite(seg).any() else None, off_run_s=float((j - i + 1) / 10.0), found=True, k_on=int(kon + k0), k_off_start=int(i + k0)) | |
| return out | |
| 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 (V4D / f"kin_causal_{r}_{s}.npz").exists()] | |
| 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) | |
| uid, t = z4["uid"], z4["t"]; assert np.array_equal(uid, z2["uid"]) and np.array_equal(uid, z3["uid"]) and np.allclose(t, z2["t"]); N = len(t); ok = z4["row_ok"]; assert np.array_equal(ok, z2["row_ok"]) | |
| S2, S3, S4 = z2["S_c"][:, :, 0], z3["S_c"][:, :, 0], z4["S_c"][:, :, 0]; S02, S03, S04 = z2["S0_c"][:, 0], z3["S0_c"][:, 0], z4["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"]) | |
| ev_by_cell[(reg, sp)] = (u3, u4, np.unique(uid), z4) | |
| D0 = z4["dwheel0_c"] | |
| c = dict(N=int(N), n_row_ok=int(ok.sum()), row_ok_share=float(ok.mean()), | |
| wheel_ellipse_defined_share_row_ok_v4=float(z4["wheel_ref_defined"][ok].mean()), wheel_ref_defined_share_row_ok_v3=float(z3["wheel_ref_defined"][ok].mean()), | |
| S0_finite_share_row_ok_v2=float(np.isfinite(S02[ok]).mean()), S0_finite_share_row_ok_v3=float(np.isfinite(S03[ok]).mean()), S0_finite_share_row_ok_v4=float(np.isfinite(S04[ok]).mean()), | |
| hands_on_share_v2=float(np.nanmean(S2)), hands_on_share_v3=float(np.nanmean(S3)), hands_on_share_v4=float(np.nanmean(S4)), | |
| hands_on_at_anchor_v4=float(np.nanmean(S04)), EM_hand_share_v4=float(z4["EM_c"][:, :, 0].mean()), EM_hand_share_v3=float(z3["EM_c"][:, :, 0].mean()), | |
| dwheel0_quantiles_row_ok_v4={f"q{p}": float(np.nanpercentile(D0[ok], p)) for p in (5, 25, 50, 75, 95)}, | |
| d0_decomposition_row_ok_v4=(lambda dd: dict(n=int(len(dd)), deep_inside_lt_m004=float((dd < -0.04).mean()), inside_m004_0=float(((dd >= -0.04) & (dd < 0)).mean()), rim_band_0_004=float(((dd >= 0) & (dd < 0.04)).mean()), | |
| dead_band_004_008=float(((dd >= 0.04) & (dd <= 0.08)).mean()), off_gt_008=float((dd > 0.08).mean())))(D0[ok][np.isfinite(D0[ok])]), | |
| head_identical_v4_v2=bool(np.array_equal(z4["E_c"][:, :, 2], z2["E_c"][:, :, 2]) and np.array_equal(z4["S_c"][:, :, 1], z2["S_c"][:, :, 1], equal_nan=True)), events={}) | |
| for name in EVENTS: | |
| c["events"][name] = dict(n_v2=int(len(u2[name][0])), n_v3=int(len(u3[name][0])), n_v4=int(len(u4[name][0])), hours_v2=h2[name], hours_v3=h3[name], hours_v4=h4[name], | |
| per_hour_v2=len(u2[name][0]) / h2[name] if h2[name] else None, per_hour_v3=len(u3[name][0]) / h3[name] if h3[name] else None, per_hour_v4=len(u4[name][0]) / h4[name] if h4[name] else None, | |
| v3_with_v4_within_1s=K3.match_share(u3[name], u4[name]), v4_with_v3_within_1s=K3.match_share(u4[name], u3[name]), v4_with_v2_within_1s=K3.match_share(u4[name], u2[name])) | |
| res[f"{reg}_{sp}"] = c; print(f"{reg}/{sp} done {time.time()-t0:.0f}s", flush=True) | |
| # ---- 3. ellipse geometry per device (fit log) | |
| rr4 = np.load(V4D / "route_refs.npz", allow_pickle=True); cols = {c_: i for i, c_ in enumerate([str(x) for x in rr4["fit_cols"]])} | |
| by_dev = {} | |
| for u in [str(x) for x in rr4["uids"]]: | |
| by_dev.setdefault(u.split("__")[1], []).append(u) | |
| dev_rows = []; all_passed = [] | |
| for dev, us in sorted(by_dev.items()): | |
| fits = [rr4[f"fits__{u}"] for u in us]; fits = np.concatenate([f for f in fits if len(f)], 0) if any(len(f) for f in fits) else np.zeros((0, len(cols))) | |
| n_fr = sum(len(rr4[f"d4__{u}"]) for u in us); n_def = sum(int(np.isfinite(rr4[f"ell__{u}"]).all(1).sum()) for u in us); n_man = sum(int(rr4[f"n_manual_moving__{u}"]) for u in us) | |
| # defined share over manual moving frames needs the regime flag per frame -> approximate with the fit log instead: share of refits passed; exact share over ALL frames from ell__ | |
| passed = fits[fits[:, cols["passed"]] > 0.5] if len(fits) else fits | |
| rec = dict(device=dev, n_routes=len(us), n_frames=int(n_fr), n_manual_moving=int(n_man), n_refits=int(len(fits)), n_passed=int(len(passed)), pass_share=float(len(passed) / len(fits)) if len(fits) else None, | |
| defined_share_frames=float(n_def / n_fr) if n_fr else None) | |
| if len(passed): | |
| a, b, cx, cy, cov = (passed[:, cols[k]] for k in ("a", "b", "cx", "cy", "coverage_deg")); ratio = b / a | |
| q = lambda v: [float(np.percentile(v, p)) for p in (25, 50, 75)] | |
| rec.update(a_q=q(a), b_q=q(b), ratio_q=q(ratio), cx_q=q(cx), cy_q=q(cy), cov_q=q(cov), n_inlier_med=float(np.median(passed[:, cols["n_inlier"]])), | |
| any_axis_out_of_range=bool(((a < AX_LO) | (a > AX_HI) | (b < AX_LO) | (b > AX_HI)).any())) | |
| rec["flag"] = bool(rec["a_q"][1] < AX_LO or rec["a_q"][1] > AX_HI or rec["b_q"][1] < AX_LO or rec["b_q"][1] > AX_HI or rec["any_axis_out_of_range"]) | |
| all_passed.append(passed) | |
| else: | |
| rec.update(a_q=None, b_q=None, ratio_q=None, cx_q=None, cy_q=None, cov_q=None, n_inlier_med=None, any_axis_out_of_range=None, flag=None) | |
| dev_rows.append(rec) | |
| AP = np.concatenate(all_passed, 0) if all_passed else np.zeros((0, len(cols))) | |
| geo = dict(n_devices=len(dev_rows), n_devices_any_passed=int(sum(1 for r in dev_rows if r["n_passed"])), n_devices_flagged=int(sum(1 for r in dev_rows if r["flag"])), n_devices_no_refit=int(sum(1 for r in dev_rows if r["n_refits"] == 0)), | |
| n_refits=int(sum(r["n_refits"] for r in dev_rows)), n_passed=int(len(AP)), | |
| passed_quantiles={k: {f"q{p}": float(np.percentile(AP[:, cols[k]], p)) for p in (5, 25, 50, 75, 95)} for k in ("a", "b", "cx", "cy", "coverage_deg", "n_inlier")} if len(AP) else None, | |
| passed_ratio_quantiles={f"q{p}": float(np.percentile(AP[:, cols["b"]] / AP[:, cols["a"]], p)) for p in (5, 25, 50, 75, 95)} if len(AP) else None, | |
| devices=dev_rows) | |
| print(f"geometry: devices {geo['n_devices']}, with a passed fit {geo['n_devices_any_passed']}, flagged {geo['n_devices_flagged']} ({time.time()-t0:.0f}s)", flush=True) | |
| # ---- 4. owner-observation test on the 68 v3 hand_return validation items | |
| obs = None | |
| if ITEMS_V3.exists() and (V3D / "route_refs.npz").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 {} | |
| rr3 = np.load(V3D / "route_refs.npz", allow_pickle=True) | |
| 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)) | |
| u3, u4, uids_sorted, z4 = ev_by_cell[(reg, "test")]; uidx = {x: i for i, x in enumerate(uids_sorted)}; ui = uidx.get(u, -1) | |
| dt4, hit4 = nearest_dt(u4["hand_return"][0], u4["hand_return"][1], ui, kf); dt3, hit3 = nearest_dt(u3["hand_return"][0], u3["hand_return"][1], ui, kf) | |
| # v4 state over the page window: rows of this route with t in [te-2, te+1] -> S0 (causal state at the anchor) | |
| rws = np.flatnonzero((z4["uid"] == u) & (z4["t"] >= te + WIN[0] - 1e-6) & (z4["t"] <= te + WIN[1] + 1e-6)); s0w = z4["S0_c"][rws, 0]; dw = z4["dwheel0_c"][rws] | |
| v4_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")) | |
| d_min = float(np.nanmin(dw)) if np.isfinite(dw).any() else None; d_max = float(np.nanmax(dw)) if np.isfinite(dw).any() else None | |
| inside_share = float((dw[np.isfinite(dw)] < 0).mean()) if np.isfinite(dw).any() else None | |
| exc = v3_off_run_excursion(u, te, rr3) | |
| lab = (labels.get(it["id"]) or {}).get("label", "") | |
| cls = "unlabelled" if not lab else ("return" if lab.startswith("ret_dt=") else lab) | |
| rows.append(dict(id=it["id"], regime=reg, uid=u, t_event=te, owner_label=lab, owner_class=cls, v3_return_within_1s=hit3, v4_return_within_1s=hit4, nearest_v4_return_dt_s=dt4, | |
| v4_state_window=v4_state, v4_d_at_anchor=float(z4["dwheel0_c"][m["row"]]) if "row" in m else None, v4_d_window_min=d_min, v4_d_window_max=d_max, v4_inside_share_window=inside_share, | |
| v3_off_excursion=exc["excursion"], v3_off_run_s=exc["off_run_s"], v3_off_found=exc["found"])) | |
| ex = np.array([r["v3_off_excursion"] for r in rows if r["v3_off_excursion"] is not None], float) | |
| 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), v4_return_within_1s=int(sum(r["v4_return_within_1s"] for r in rs)), v4_always_on_window=int(sum(r["v4_state_window"] == "always_on" for r in rs)), | |
| v3_excursion_lt_025=int(sum(1 for r in rs if r["v3_off_excursion"] is not None and r["v3_off_excursion"] < EXC_TH)), | |
| v4_d_max_window_le_008=int(sum(1 for r in rs if r["v4_d_window_max"] is not None and r["v4_d_window_max"] <= 0.08)), | |
| v4_inside_share_window_mean=float(np.mean([r["v4_inside_share_window"] for r in rs if r["v4_inside_share_window"] is not None])) if any(r["v4_inside_share_window"] is not None for r in rs) else None) | |
| return out | |
| obs = dict(n=len(rows), n_v3_return_within_1s=int(sum(r["v3_return_within_1s"] for r in rows)), n_v4_return_within_1s=int(sum(r["v4_return_within_1s"] for r in rows)), | |
| n_v4_always_on_window=int(sum(r["v4_state_window"] == "always_on" for r in rows)), n_excursion_found=int(len(ex)), | |
| excursion_median=float(np.median(ex)) if len(ex) else None, excursion_q25=float(np.percentile(ex, 25)) if len(ex) else None, excursion_q75=float(np.percentile(ex, 75)) if len(ex) else None, | |
| share_excursion_lt_025=float((ex < EXC_TH).mean()) if len(ex) else None, off_run_median_s=float(np.median([r["v3_off_run_s"] for r in rows if r["v3_off_run_s"] is not None])) if len(ex) else None, | |
| n_labelled=int(sum(r["owner_class"] != "unlabelled" for r in rows)), | |
| by_regime={reg: dict(n=len([r 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)), | |
| share_excursion_lt_025=float(np.mean([r["v3_off_excursion"] < EXC_TH for r in rows if r["regime"] == reg and r["v3_off_excursion"] is not None])) if any(r["regime"] == reg and r["v3_off_excursion"] is not None for r in rows) else None, | |
| xtab=xtab([r for r in rows if r["regime"] == reg])) for reg in ("manual", "engaged")}, | |
| xtab_all=xtab(rows), excursion_vs_v4={"exc<0.25": dict(n=int(sum(1 for r in rows if r["v3_off_excursion"] is not None and r["v3_off_excursion"] < EXC_TH)), v4_return=int(sum(1 for r in rows if r["v3_off_excursion"] is not None and r["v3_off_excursion"] < EXC_TH and r["v4_return_within_1s"]))), | |
| "exc>=0.25": dict(n=int(sum(1 for r in rows if r["v3_off_excursion"] is not None and r["v3_off_excursion"] >= EXC_TH)), v4_return=int(sum(1 for r in rows if r["v3_off_excursion"] is not None and r["v3_off_excursion"] >= EXC_TH and r["v4_return_within_1s"])))}, | |
| items=rows) | |
| print(f"68-item test: v4 return within 1 s {obs['n_v4_return_within_1s']}/{obs['n']}; excursion median {fmt(obs['excursion_median'])}, share < 0.25 {fmt(obs['share_excursion_lt_025'])}; labelled {obs['n_labelled']} ({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"]) | |
| u3, u4, uids_sorted, z4 = ev_by_cell[("engaged", "test")]; uidx = {x: i for i, x in enumerate(uids_sorted)} | |
| 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) | |
| dt4, hit4 = nearest_dt(u4["hand_return"][0], u4["hand_return"][1], ui, kf); dt3, hit3 = nearest_dt(u3["hand_return"][0], 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, nearest_v4_return_dt_s=dt4)) | |
| rev = dict(n=len(rws), n_v3_return_within_1s=int(sum(r["v3_return_within_1s"] for r in rws)), n_v4_return_within_1s=int(sum(r["v4_return_within_1s"] for r in rws)), items=rws) | |
| print(f"34 pass-3 engaged chatter events: v4 hand_return within +-1 s {rev['n_v4_return_within_1s']}/{rev['n']} (v3: {rev['n_v3_return_within_1s']}/{rev['n']})", flush=True) | |
| # ---- build provenance numbers | |
| bs = json.load(open(V4D / "build_stats_latest.json")) if (V4D / "build_stats_latest.json").exists() else {} | |
| 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")} | |
| prov["split_wall_s"] = {k: v.get("wall_s") for k, v in bs.get("splits", {}).items()} | |
| tf = sorted(V4D.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 — build statistics and v4 diagnostics ({stamp})", "", | |
| f"Source: `eval/kin_v4_build_stats.py` on `{V2.relative_to(ROOT)}`, `{V3D.relative_to(ROOT)}`, `{V4D.relative_to(ROOT)}` (target files; cells present in v4 only) and `{V4D.relative_to(ROOT)}/route_refs.npz` (fit log). " | |
| "v4 = pre-registered hands_on / hand-event label v4 (docs/decisions.md 2026-09-16T22:18:02Z): per-device causal RANSAC + direct-LS ELLIPSE fit of the rim locus from both wrists over manual moving frames " | |
| "(gate >= 200 inliers spanning >= 90 deg of arc, else undefined), hands_on = hysteresis on the min-wrist SIGNED distance to the filled ellipse (ON < 0.04, OFF > 0.08, reset after > 1 s gap), " | |
| ">= 1 s persistence before and after a hand event, causal S0; head_turn_onset and all input channels unchanged (asserted bit-identical to v2 in the build).", "", | |
| f"Build provenance: 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 of the build; " | |
| 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'):,}; per-split wall {prov.get('split_wall_s')}; " | |
| f"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: 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; 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 v4 | point ref defined v3 | S0 finite v2 | S0 finite v3 | S0 finite v4 | hands_on share v2 | hands_on share v3 | hands_on share v4 | EM hand share v4 | d0 median v4 [q5, q95] | head identical to v2 |", "|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|"] | |
| for key, c in res.items(): | |
| reg, sp = key.split("_"); dq = c["dwheel0_quantiles_row_ok_v4"] | |
| L.append(f"| {reg} | {sp} | {c['N']:,} | {c['n_row_ok']:,} ({100*c['row_ok_share']:.2f} %) | {100*c['wheel_ellipse_defined_share_row_ok_v4']:.2f} % | {100*c['wheel_ref_defined_share_row_ok_v3']:.2f} % | {100*c['S0_finite_share_row_ok_v2']:.2f} % | {100*c['S0_finite_share_row_ok_v3']:.2f} % | {100*c['S0_finite_share_row_ok_v4']:.2f} % | " | |
| f"{c['hands_on_share_v2']:.3f} | {c['hands_on_share_v3']:.3f} | {c['hands_on_share_v4']:.3f} | {c['EM_hand_share_v4']:.4f} | {dq['q50']:+.3f} [{dq['q5']:+.3f}, {dq['q95']:+.3f}] | {c['head_identical_v4_v2']} |") | |
| L += ["", "Decomposition of the v4 anchor signed distance d0 over row_ok rows with a defined ellipse (ON state = d0 < 0.04 at entry; a wrist INSIDE the ellipse, d0 < 0, is ON by the pre-registered 'filled region' convention):", "", | |
| "| regime | split | n | deep inside d0 < -0.04 | inside -0.04 <= d0 < 0 | rim band 0 <= d0 < 0.04 | dead band 0.04..0.08 | off d0 > 0.08 |", "|---|---|---|---|---|---|---|---|"] | |
| for key, c in res.items(): | |
| reg, sp = key.split("_"); dd = c["d0_decomposition_row_ok_v4"] | |
| L.append(f"| {reg} | {sp} | {dd['n']:,} | {100*dd['deep_inside_lt_m004']:.1f} % | {100*dd['inside_m004_0']:.1f} % | {100*dd['rim_band_0_004']:.1f} % | {100*dd['dead_band_004_008']:.1f} % | {100*dd['off_gt_008']:.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 | hours v4 | per hour v2 | per hour v3 | per hour v4 | v3 events with v4 within 1 s | v4 events with v3 within 1 s | v4 events with v2 within 1 s |", "|---|---|---|---|---|---|---|---|---|---|---|---|---|"] | |
| for key, c in res.items(): | |
| reg, sp = key.split("_") | |
| for name in EVENTS: | |
| e = c["events"][name] | |
| L.append(f"| {reg} | {sp} | {name} | {e['n_v2']:,} | {e['n_v3']:,} | {e['n_v4']:,} | {e['hours_v4']:.1f} | {fmt(e['per_hour_v2'], 2)} | {fmt(e['per_hour_v3'], 2)} | {fmt(e['per_hour_v4'], 2)} | {fmt(e['v3_with_v4_within_1s'])} | {fmt(e['v4_with_v3_within_1s'])} | {fmt(e['v4_with_v2_within_1s'])} |") | |
| L += ["", "## 3. Ellipse geometry sanity per device (fit log; passed fits only for the geometry columns)", "", | |
| f"Devices {geo['n_devices']}; with at least one passed fit {geo['n_devices_any_passed']}; without any refit (< 200 manual points) {geo['n_devices_no_refit']}; FLAGGED (median semi-axis of the passed fits < {AX_LO} or > {AX_HI}, or any passed fit outside that range) **{geo['n_devices_flagged']}**. " | |
| f"Refits {geo['n_refits']:,}, passed {geo['n_passed']:,}.", ""] | |
| if geo["passed_quantiles"]: | |
| pq = geo["passed_quantiles"]; rq = geo["passed_ratio_quantiles"] | |
| L += ["| quantity (passed fits, all devices) | q5 | q25 | q50 | q75 | q95 |", "|---|---|---|---|---|---|"] | |
| for k, lab in (("a", "semi-axis a"), ("b", "semi-axis b"), ("cx", "centre x"), ("cy", "centre y"), ("coverage_deg", "arc coverage (deg)"), ("n_inlier", "inliers")): | |
| L.append(f"| {lab} | " + " | ".join(f"{pq[k][f'q{p}']:.3f}" for p in (5, 25, 50, 75, 95)) + " |") | |
| L.append("| aspect ratio b / a | " + " | ".join(f"{rq[f'q{p}']:.3f}" for p in (5, 25, 50, 75, 95)) + " |") | |
| L += ["", "Units: crop-normalised-equivalent (x = x_norm x 1.6, y = y_norm); a wheel spanning the full frame width would have a = 0.8.", "", | |
| "| device | routes | pose frames | manual moving frames | refits | passed | pass share | ellipse defined (share of frames) | a median [q25, q75] | b median [q25, q75] | b/a median | centre (x, y) median | coverage deg median | inliers median | flag |", "|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|"] | |
| for r in dev_rows: | |
| if r["n_passed"]: | |
| L.append(f"| {r['device']} | {r['n_routes']} | {r['n_frames']:,} | {r['n_manual_moving']:,} | {r['n_refits']} | {r['n_passed']} | {r['pass_share']:.2f} | {100*r['defined_share_frames']:.1f} % | {r['a_q'][1]:.3f} [{r['a_q'][0]:.3f}, {r['a_q'][2]:.3f}] | {r['b_q'][1]:.3f} [{r['b_q'][0]:.3f}, {r['b_q'][2]:.3f}] | {r['ratio_q'][1]:.2f} | ({r['cx_q'][1]:.3f}, {r['cy_q'][1]:.3f}) | {r['cov_q'][1]:.0f} | {r['n_inlier_med']:.0f} | {'FLAG' if r['flag'] else ''} |") | |
| else: | |
| L.append(f"| {r['device']} | {r['n_routes']} | {r['n_frames']:,} | {r['n_manual_moving']:,} | {r['n_refits']} | 0 | {fmt(r['pass_share'], 2)} | {100*(r['defined_share_frames'] or 0):.1f} % | — | — | — | — | — | — | {'no fit' if r['n_refits'] else 'no refit'} |") | |
| if obs is not None: | |
| L += ["", "## 4. Owner-observation test: the 68 v3 hand_return validation items (relabel_v3.html; owner labels so far in labels_latest.json)", "", | |
| f"Mechanism hypothesis (pre-registration): a hand sliding along the rim leaves the v3 POINT reference by > 0.12 and comes back; v3 then stamps a 'return' although a hand was on the wheel throughout.", "", | |
| f"(i) Excursion of the v3 OFF run preceding the event from the point reference (max of the v3 min-wrist distance; OFF run found for {obs['n_excursion_found']} / {obs['n']} items): median **{fmt(obs['excursion_median'])}** [q25 {fmt(obs['excursion_q25'])}, q75 {fmt(obs['excursion_q75'])}], " | |
| f"share < {EXC_TH} (rim sliding) **{fmt(obs['share_excursion_lt_025'])}**; OFF-run duration median {fmt(obs['off_run_median_s'], 1)} s. " | |
| f"(ii) v4 hand_return within +-1 s of t_event: **{obs['n_v4_return_within_1s']} / {obs['n']}** (v3 sanity: {obs['n_v3_return_within_1s']} / {obs['n']}); v4 state ON throughout the page window [-2 s, +1 s]: {obs['n_v4_always_on_window']} / {obs['n']}.", "", | |
| "| regime | n | v4 return within 1 s | share v3 excursion < 0.25 |", "|---|---|---|---|"] | |
| for reg, b in obs["by_regime"].items(): L.append(f"| {reg} | {b['n']} | {b['v4_return_within_1s']} | {fmt(b['share_excursion_lt_025'])} |") | |
| L += ["", f"Cross-tab against the owner's labels ({obs['n_labelled']} / {obs['n']} labelled at generation time):", "", | |
| "| regime | owner label class | n | v4 return within 1 s | v4 ON throughout window | v4 d never > 0.08 in window | mean share of window with a wrist inside the ellipse | v3 excursion < 0.25 |", "|---|---|---|---|---|---|---|---|"] | |
| for reg, b in obs["by_regime"].items(): | |
| for cls, x in b["xtab"].items(): L.append(f"| {reg} | {cls} | {x['n']} | {x['v4_return_within_1s']} | {x['v4_always_on_window']} | {x['v4_d_max_window_le_008']} | {fmt(x['v4_inside_share_window_mean'], 2)} | {x['v3_excursion_lt_025']} |") | |
| for cls, x in obs["xtab_all"].items(): L.append(f"| both | {cls} | {x['n']} | {x['v4_return_within_1s']} | {x['v4_always_on_window']} | {x['v4_d_max_window_le_008']} | {fmt(x['v4_inside_share_window_mean'], 2)} | {x['v3_excursion_lt_025']} |") | |
| ev_ = obs["excursion_vs_v4"] | |
| L += ["", f"Excursion vs v4 event: excursion < 0.25 -> v4 return in {ev_['exc<0.25']['v4_return']} / {ev_['exc<0.25']['n']}; excursion >= 0.25 -> v4 return in {ev_['exc>=0.25']['v4_return']} / {ev_['exc>=0.25']['n']}.", "", | |
| "| item | regime | uid | t_event | owner label | v3 OFF excursion | v3 OFF run (s) | v4 return within 1 s | nearest v4 return (s) | v4 state in window | v4 d0 | v4 d in window [min, max] | share inside |", "|---|---|---|---|---|---|---|---|---|---|---|---|---|"] | |
| for r in obs["items"]: | |
| L.append(f"| {r['id']} | {r['regime']} | {r['uid']} | {r['t_event']:.1f} | {r['owner_label'] or '—'} | {fmt(r['v3_off_excursion'])} | {fmt(r['v3_off_run_s'], 1)} | {r['v4_return_within_1s']} | {'—' if r['nearest_v4_return_dt_s'] is None else f'{r['nearest_v4_return_dt_s']:+.1f}'} | {r['v4_state_window']} | {fmt(r['v4_d_at_anchor'])} | [{fmt(r['v4_d_window_min'])}, {fmt(r['v4_d_window_max'])}] | {fmt(r['v4_inside_share_window'], 2)} |") | |
| if rev is not None: | |
| L += ["", "## 5. The 34 pass-3 engaged hand_return chatter events (pass 4: 0 / 34 real returns)", "", | |
| f"v4 hand_return within +-1 s of t_event = t_anchor + 0.2 (engaged/test, unique-event times): **{rev['n_v4_return_within_1s']} / {rev['n']}** (v3: {rev['n_v3_return_within_1s']} / {rev['n']}).", "", | |
| "| item | uid | t_event | v3 return within 1 s | v4 return within 1 s | nearest v4 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']} | {'—' if r['nearest_v4_return_dt_s'] is None else f'{r['nearest_v4_return_dt_s']:+.1f}'} |") | |
| L += ["", f"Build provenance: `{V4D.relative_to(ROOT)}/build_stats_latest.json`, `run_build_kin_causal_routes_v4_*.json`, `logs/build_all.log` / `.time`."] | |
| (TABLES / "kin_v4_build_stats.md").write_text("\n".join(L) + "\n") | |
| json.dump(dict(generated_utc=stamp, cells=res, geometry=geo, owner_observation_68=obs, reviewed_34=rev, provenance=prov), open(TABLES / "kin_v4_build_stats.json", "w"), indent=1) | |
| write_run_json(TABLES, "kin_v4_build_stats", [Path(__file__), Path(K3.__file__), Path(V4.__file__)], [V4D / f"kin_causal_{r}_{s}.npz" for r, s in cells] + [V3D / f"kin_causal_{r}_{s}.npz" for r, s in cells] + [V2 / f"kin_causal_{r}_{s}.npz" for r, s in cells] | |
| + [V4D / "route_refs.npz", V3D / "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], geometry={k: v for k, v in geo.items() if k != "devices"}, 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_v4_build_stats.md'} ({time.time()-t0:.0f}s)", flush=True) | |
| if __name__ == "__main__": | |
| main() | |
Xet Storage Details
- Size:
- 33.8 kB
- Xet hash:
- 1bf45bd23e946008328ee7ca6739e5506a790006bc32a21d2c3891ca529b0f2a
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.