AsianPlayer/TriDrive / code /eval /kin_v3_build_stats.py
AsianPlayer's picture
download
raw
11.5 kB
"""cwm3/eval/kin_v3_build_stats.py — v2 vs v3 comparison of the causal kinematics targets, per regime x split, from the TARGET FILES
(results/targets_kin_causal_routes_v2 vs results/targets_kin_causal_routes_v3; only splits present in v3 are reported).
Per cell: rows N, row_ok, wheel_ref_defined share (v3, among row_ok), hands_on share (nanmean of S_c[:,:,0] over the 25 future steps;
v2 vs v3), S0 hands_on finite share (v2 vs v3) and the coverage loss (row_ok rows with v2 S0 finite but v3 S0 NaN), events per hour
(unique (uid, frame) events on the 0.2 s grid with E_c=1 & EM_c; hours = unique (uid, frame) with EM_c True x 0.2 s / 3600; v2 vs v3),
share of v2 hand events with a v3 event of the same type within +-1 s on the same route and vice versa (unique events),
plus the check of the 34 owner-reviewed engaged hand_return events (results/review_tool/items.json task B; t_event = t_anchor + 0.2):
how many still have a v3 hand_return (unique-event time) within +-1 s in engaged/test.
Writes results/tables/kin_v3_build_stats.md and results/tables/kin_v3_build_stats.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"))
from runmeta import write_run_json # noqa: E402
V2 = ROOT / "results" / "targets_kin_causal_routes_v2"; V3 = ROOT / "results" / "targets_kin_causal_routes_v3"
ITEMS = ROOT / "results" / "review_tool" / "items.json"; TABLES = ROOT / "results" / "tables"
EVENTS = ["hand_return", "hand_leave", "head_turn_onset"]; H = 25; TOL = 1.0 + 1e-6
def unique_events(uid, t, E, EM):
"""-> dict event -> (uid_arr, frame_key_arr) of unique (uid, frame) events; and hours = unique EM frames x 0.2 s / 3600 per event."""
steps = (np.arange(1, H + 1) * 2)[None, :]; kbase = np.round(t * 10).astype(np.int64)[:, None] + steps # frame keys of the 25 future steps
uidx = np.unique(uid, return_inverse=True)[1].astype(np.int64)[:, None]
out = {}; hours = {}
for j, name in enumerate(EVENTS):
m = EM[:, :, j]; ev = E[:, :, j].astype(bool) & m
code_em = np.unique(uidx.repeat(H, 1)[m] * (1 << 32) + kbase[m]); hours[name] = len(code_em) * 0.2 / 3600.0
code_ev = np.unique(uidx.repeat(H, 1)[ev] * (1 << 32) + kbase[ev])
out[name] = (code_ev >> 32, code_ev & ((1 << 32) - 1))
return out, hours
def match_share(a, b, tol_frames=10):
"""share of events in a=(uid_idx, frame) with an event in b (same uid) within tol_frames."""
if len(a[0]) == 0: return None
ca = a[0] * (1 << 32) + a[1]; cb = np.sort(b[0] * (1 << 32) + b[1])
if len(cb) == 0: return 0.0
pos = np.searchsorted(cb, ca); hit = np.zeros(len(ca), bool)
for p in (np.clip(pos - 1, 0, len(cb) - 1), np.clip(pos, 0, len(cb) - 1)):
same = (cb[p] >> 32) == (ca >> 32); hit |= same & (np.abs((cb[p] & ((1 << 32) - 1)).astype(np.int64) - a[1].astype(np.int64)) <= tol_frames)
return float(hit.mean())
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 (V3 / f"kin_causal_{r}_{s}.npz").exists()]
res = {}; ev3_by_cell = {}
for reg, sp in cells:
z2 = np.load(V2 / f"kin_causal_{reg}_{sp}.npz", allow_pickle=True); z3 = np.load(V3 / f"kin_causal_{reg}_{sp}.npz", allow_pickle=True)
uid, t = z3["uid"], z3["t"]; assert np.array_equal(uid, z2["uid"]) and np.allclose(t, z2["t"]); N = len(t); ok = z3["row_ok"]; assert np.array_equal(ok, z2["row_ok"])
S2, S3 = z2["S_c"][:, :, 0], z3["S_c"][:, :, 0]; S02, S03 = z2["S0_c"][:, 0], z3["S0_c"][:, 0]
E2, EM2, E3, EM3 = z2["E_c"], z2["EM_c"], z3["E_c"], z3["EM_c"]
u2, h2 = unique_events(uid, t, E2, EM2); u3, h3 = unique_events(uid, t, E3, EM3); ev3_by_cell[(reg, sp)] = (u3, np.unique(uid))
c = dict(N=int(N), n_row_ok=int(ok.sum()), row_ok_share=float(ok.mean()), wheel_ref_defined_share_row_ok=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()),
coverage_loss_row_ok=float((np.isfinite(S02[ok]) & ~np.isfinite(S03[ok])).mean()),
hands_on_share_v2=float(np.nanmean(S2)), hands_on_share_v3=float(np.nanmean(S3)), hands_on_at_anchor_v2=float(np.nanmean(S02)), hands_on_at_anchor_v3=float(np.nanmean(S03)),
EM_hand_share_v2=float(EM2[:, :, 0].mean()), EM_hand_share_v3=float(EM3[:, :, 0].mean()), events={})
for name in EVENTS:
c["events"][name] = dict(n_unique_v2=int(len(u2[name][0])), n_unique_v3=int(len(u3[name][0])), hours_v2=h2[name], hours_v3=h3[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,
v2_with_v3_within_1s=match_share(u2[name], u3[name]), v3_with_v2_within_1s=match_share(u3[name], u2[name]),
n_E_cells_v2=int(E2[:, :, EVENTS.index(name)].sum()), n_E_cells_v3=int(E3[:, :, EVENTS.index(name)].sum()))
res[f"{reg}_{sp}"] = c; print(f"{reg}/{sp} done {time.time()-t0:.0f}s", flush=True)
# ---- the 34 reviewed engaged hand_return events
rev = None
if ITEMS.exists() and ("engaged", "test") in ev3_by_cell:
items = json.load(open(ITEMS))["items"]
sel = sorted([i for i in items if i["task"] == "B" and i["meta"].get("event") == "hand_return" and i["meta"].get("regime") == "engaged"], key=lambda i: i["id"])
u3, uids_sorted = ev3_by_cell[("engaged", "test")]; ret_uid, ret_frame = u3["hand_return"]
z2 = np.load(V2 / "kin_causal_engaged_test.npz", allow_pickle=True); u2, _ = unique_events(z2["uid"], z2["t"], z2["E_c"], z2["EM_c"]); r2_uid, r2_frame = u2["hand_return"]
uidx = {u: i for i, u in enumerate(uids_sorted)}
rows = []
for it in sel:
m = it["meta"]; te = float(m["t_anchor"]) + 0.2; assert abs(te - float(m["t_event"])) < 1e-6; kf = int(round(te * 10)); ui = uidx.get(m["uid"], -1)
f3 = ret_frame[ret_uid == ui].astype(np.int64); f2 = r2_frame[r2_uid == ui].astype(np.int64)
d3 = np.abs(f3 - kf); d2 = np.abs(f2 - kf)
rows.append(dict(id=it["id"], uid=m["uid"], t_event=te, v2_return_within_1s=bool((d2 <= 10).any()), v3_return_within_1s=bool((d3 <= 10).any()),
nearest_v3_return_dt_s=(float(np.sign((f3 - kf)[d3.argmin()]) * d3.min() / 10) if len(f3) else None)))
rev = dict(n=len(rows), n_v3_return_within_1s=int(sum(r["v3_return_within_1s"] for r in rows)), n_v2_return_within_1s=int(sum(r["v2_return_within_1s"] for r in rows)), items=rows)
print(f"34 reviewed engaged hand_return events: v3 hand_return within +-1 s: {rev['n_v3_return_within_1s']}/{rev['n']} (v2 sanity: {rev['n_v2_return_within_1s']}/{rev['n']})", flush=True)
# ---- markdown
stamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
L = [f"# kin causal targets v2 vs v3 — build statistics ({stamp})", "",
f"Source: `eval/kin_v3_build_stats.py` on `{V2.relative_to(ROOT)}` and `{V3.relative_to(ROOT)}` (target files; cells present in v3 only). "
"v3 = pre-registered hands_on / hand-event label v3 (docs/decisions.md 2026-09-16T16:45:31Z): manual-only causal wheel reference, hysteresis 0.08 / 0.12, "
">= 1 s persistence before and after a transition, causal S0; head_turn_onset and all input channels unchanged (asserted bit-identical in the build).", "",
"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; "
"coverage loss = row_ok rows with v2 S0 defined and v3 S0 undefined (manual-only reference not yet defined, or d undefined / undecided); 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.", "",
"## Coverage and state shares", "",
"| regime | split | rows | row_ok | wheel_ref_defined (v3) | S0 finite v2 | S0 finite v3 | coverage loss | hands_on share v2 | hands_on share v3 | EM hand share v2 | EM hand share v3 |", "|---|---|---|---|---|---|---|---|---|---|---|---|"]
for key, c in res.items():
reg, sp = key.split("_")
L.append(f"| {reg} | {sp} | {c['N']:,} | {c['n_row_ok']:,} ({100*c['row_ok_share']:.2f} %) | {100*c['wheel_ref_defined_share_row_ok']:.2f} % | {100*c['S0_finite_share_row_ok_v2']:.2f} % | {100*c['S0_finite_share_row_ok_v3']:.2f} % | {100*c['coverage_loss_row_ok']:.2f} % | "
f"{c['hands_on_share_v2']:.3f} | {c['hands_on_share_v3']:.3f} | {c['EM_hand_share_v2']:.4f} | {c['EM_hand_share_v3']:.4f} |")
L += ["", "## 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 | hours v2 | hours v3 | per hour v2 | per hour v3 | v2 events with v3 within 1 s | v3 events with v2 within 1 s |", "|---|---|---|---|---|---|---|---|---|---|---|"]
for key, c in res.items():
reg, sp = key.split("_")
for name in EVENTS:
e = c["events"][name]; f = lambda x, p=3: ("—" if x is None else f"{x:.{p}f}")
L.append(f"| {reg} | {sp} | {name} | {e['n_unique_v2']:,} | {e['n_unique_v3']:,} | {e['hours_v2']:.1f} | {e['hours_v3']:.1f} | {f(e['per_hour_v2'],2)} | {f(e['per_hour_v3'],2)} | {f(e['v2_with_v3_within_1s'])} | {f(e['v3_with_v2_within_1s'])} |")
if rev is not None:
L += ["", "## The 34 owner-reviewed engaged hand_return events (pass 4: 0 / 34 real returns)", "",
f"v3 hand_return within +-1 s of t_event = t_anchor + 0.2 (engaged/test, unique-event times): **{rev['n_v3_return_within_1s']} / {rev['n']}** (v2 sanity check: {rev['n_v2_return_within_1s']} / {rev['n']}).", "",
"| item | uid | t_event | v2 return within 1 s | v3 return within 1 s | nearest v3 return (s) |", "|---|---|---|---|---|---|"]
for r in rev["items"]:
L.append(f"| {r['id']} | {r['uid']} | {r['t_event']:.1f} | {r['v2_return_within_1s']} | {r['v3_return_within_1s']} | {'—' if r['nearest_v3_return_dt_s'] is None else f'{r['nearest_v3_return_dt_s']:+.1f}'} |")
L += ["", f"Build provenance: `{V3.relative_to(ROOT)}/build_stats_latest.json`, `run_build_kin_causal_routes_v3_*.json`; wall / RSS per split in `{V3.relative_to(ROOT)}/logs/`."]
(TABLES / "kin_v3_build_stats.md").write_text("\n".join(L) + "\n")
json.dump(dict(generated_utc=stamp, cells=res, reviewed_34=rev), open(TABLES / "kin_v3_build_stats.json", "w"), indent=1)
write_run_json(TABLES, "kin_v3_build_stats", [Path(__file__)], [V3 / f"kin_causal_{r}_{s}.npz" for r, s in cells] + [V2 / f"kin_causal_{r}_{s}.npz" for r, s in cells] + ([ITEMS] if ITEMS.exists() else []), None, t0,
extra=dict(cells=[f"{r}_{s}" for r, s in cells], reviewed_34=None if rev is None else {k: v for k, v in rev.items() if k != "items"}), input_listing=False)
print(f"wrote {TABLES / 'kin_v3_build_stats.md'} ({time.time()-t0:.0f}s)", flush=True)
if __name__ == "__main__":
main()

Xet Storage Details

Size:
11.5 kB
·
Xet hash:
84ecbf51f2120ed2a98b9d2eaa0d77518624ebc6d52a388675029a7d55017bcb

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