#!/usr/bin/env python3 # SPDX-License-Identifier: Apache-2.0 """Goldens of the port from the fp32 CPU reference (``tt_ptv3.reference``; research venv, CPU only, no device). OMP_NUM_THREADS=2 PYTHONPATH=code tools/research-venv/bin/python code/scripts/make_goldens.py [--frames ...] All goldens use the **spconv-faithful** neighbour builder (13 queried taps + symmetric writes; SPEC 4.1, R11): the earlier research golden ``research/ptv3/ref_outputs/scene10_00795_full.npz`` (26-tap emulation) is superseded. Large (never shipped) -> ``research/ptv3/goldens/`` (``--out``): - ``..npz``: the research golden layout (``point_feat_0..4``, ``pred_labels``, ``pred_probs``, the 9 ``det_*`` outputs, ``boxes`` [M, 11], ``point_label15``, ``point_prob``, ``stage_counts``) for every frame and both neighbour modes (``alias-first`` = the port default, PLAN.md D3; ``exact``); - ``.alias-first.taps.npz``: per-module taps (``reference/model.py TAP_NAMES``; ``taps.`` prefix) for the tap frames (the shipped sample, T4 scene_10/00795, nuScenes scene-0103 k20) -- the device PCC gates' references; - ``INDEX.json``: per golden the input sha256, the host-table digests, counts, flags, box count, file sha256. Small -> ``..json``: counts, flags, host-table digests, per-class voxel / point histograms and the boxes (records) -- what the host / e2e tests check without the large files (``tests/_data.small_golden``). Only the shipped sample's go into the bundle (``code/tt_ptv3/tests/goldens/``); the PandaSet sample's go to the git-ignored ``staging_samples_pandaset/goldens/`` (not shipped until the user approves PandaSet samples, DATASETS.md N5), the T4 and nuScenes frames' to ``research/ptv3/goldens/summaries/`` (never shipped). Frames: the shipped sample ``test_pcd`` (Autoware's test.pcd, Apache-2.0), the PandaSet sample (PandaSet 019 f40), the 4 T4 sample-dataset frames (SPEC 7; licence UNVERIFIED, never shipped) and nuScenes scene-0103 k20 (CC BY-NC-SA; never shipped; below the node's 16k-voxel floor: the reference runs with the floor disabled, ``node_rejects_frame`` recorded). The 16 public frames already have research goldens from the same reference (research/ptv3/public_data/goldens; ``test_reference_cpu.py`` checks they are reproduced). """ from __future__ import annotations import argparse import hashlib import json import sys import time from pathlib import Path import numpy as np HERE = Path(__file__).resolve().parent sys.path.insert(0, str(HERE.parent)) WS = Path("/home/ubuntu/experiments/tt-models") T4 = WS / "assets" / "ptv3" / "t4_lidar_detection_sample" / "lidar_detection_sample_dataset" FRAMES = { "test_pcd": HERE.parent / "tt_ptv3" / "samples" / "test_pcd.npz", "ps019_f40": WS / "research" / "ptv3" / "public_data" / "ship_sample" / "pandaset_019_f40.pcd5.npy", "t4_scene10_00795": T4 / "scene_10/data/LIDAR_CONCAT/00795.pcd.bin", "t4_scene6_00860": T4 / "scene_6/data/LIDAR_CONCAT/00860.pcd.bin", "t4_scene7_00270": T4 / "scene_7/data/LIDAR_CONCAT/00270.pcd.bin", "t4_scene7_00720": T4 / "scene_7/data/LIDAR_CONCAT/00720.pcd.bin", "ns0103_k20": WS / "research" / "ptv3" / "public_data" / "inputs" / "ns0103_k20.pcd5.npy", } TAP_FRAMES = ("ps019_f40", "t4_scene10_00795", "ns0103_k20") # taps kept in the large goldens (the CPE / attention sub-taps and the frame-independent key position embedding are # recomputed by the tests when needed) SKIP_TAPS = (".cpe", ".attn", "det.key_pos_embed", "det.bev_refine", "det.stem", "det.heat", "seg.labels") OLD_T4_GOLDEN = WS / "research" / "ptv3" / "ref_outputs" / "scene10_00795_full.npz" def load(path: Path) -> np.ndarray: if path.suffix == ".npy": return np.load(path) if path.suffix == ".npz": from tt_ptv3 import io as tio return np.asarray(tio.load_points(str(path)).points, np.float32) return np.fromfile(path, dtype=np.float32).reshape(-1, 5) def sha256_file(p: Path) -> str: h = hashlib.sha256() with open(p, "rb") as f: for b in iter(lambda: f.read(1 << 20), b""): h.update(b) return h.hexdigest() def tap_wanted(name: str) -> bool: return not any(s in name if s.startswith(".") else name.startswith(s) for s in SKIP_TAPS) def hist(labels: np.ndarray, names) -> dict: lab = np.asarray(labels) h = np.bincount(lab[lab != 255].astype(np.int64), minlength=len(names))[:len(names)] out = {names[i]: int(h[i]) for i in range(len(names)) if h[i]} out["invalid"] = int((lab == 255).sum()) return out def main(argv=None) -> int: ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) ap.add_argument("--weights", default=str(WS / "assets" / "ptv3" / "hf_ptv3")) ap.add_argument("--out", default=str(WS / "research" / "ptv3" / "goldens")) ap.add_argument("--frames", default=",".join(FRAMES)) ap.add_argument("--small-out", help="directory of the small goldens (default: per frame, see the docstring)") ap.add_argument("--modes", default="alias-first,exact") a = ap.parse_args(argv) from tt_ptv3.host.preprocess import table_digests from tt_ptv3.reference.config import DET_CLASSES, SEG_CLASSES from tt_ptv3.reference.pipeline import Reference, set_threads from tt_ptv3.ttaw.metrics import pcc set_threads(2) out = Path(a.out) out.mkdir(parents=True, exist_ok=True) def small_dir(fid: str) -> Path: if a.small_out: d = Path(a.small_out) elif fid == "test_pcd": d = HERE.parent / "tt_ptv3" / "tests" / "goldens" # shipped (Apache-2.0 sample) elif fid.startswith("ps"): d = HERE.parents[1] / "staging_samples_pandaset" / "goldens" # git-ignored until PandaSet may ship else: d = out / "summaries" # workspace only (T4, nuScenes) d.mkdir(parents=True, exist_ok=True) return d ref = Reference(a.weights) index_path = out / "INDEX.json" index = json.loads(index_path.read_text()) if index_path.is_file() else {} index["reference"] = ("bundles/ptv3-p150 code/scripts/make_goldens.py (tt_ptv3.reference: fp32 torch, BN folded " "as the port folds, spconv-faithful neighbour tables; torch 2 threads)") index.setdefault("goldens", {}) for fid in a.frames.split(","): pts = load(FRAMES[fid]) for mode in a.modes.split(","): t0 = time.time() taps: dict = {} if (fid in TAP_FRAMES and mode == "alias-first") else None res = ref.run(pts, neighbour_mode=mode, taps=taps, tap_filter=tap_wanted) g = res.golden() stem = f"{fid}.{mode}" np.savez_compressed(out / f"{stem}.npz", **g) if taps is not None: np.savez_compressed(out / f"{stem}.taps.npz", **{f"taps.{k}": v for k, v in taps.items()}) fr = res.frame summary = { "frame": fid, "neighbour_mode": mode, "input": str(FRAMES[fid].relative_to(WS)) if FRAMES[fid].is_relative_to(WS) else FRAMES[fid].name, "input_sha256": sha256_file(FRAMES[fid]), **fr.describe(), "node_rejects_frame": fr.rejected, "seg_voxel_hist": hist(res.pred_labels, SEG_CLASSES), "seg_point_hist": hist(res.seg.labels, SEG_CLASSES), "boxes": [dict(r, class_name=DET_CLASSES[r["label"]]) for r in res.boxes.to_records()], "host_table_sha256": table_digests(fr), "timing_s": {k: round(v, 2) for k, v in res.timing_s.items()}, } if fid == "t4_scene10_00795" and mode == "exact" and OLD_T4_GOLDEN.is_file(): with np.load(OLD_T4_GOLDEN) as z: # R11: the faithful tables vs the old 26-tap golden summary["vs_26tap_golden"] = { "point_feat_pcc": [round(pcc(res.point_feat[i], z[f"enc_out_point_feat_{i}"]), 6) for i in range(5)], "seg_label_agreement": round(float(np.mean(res.pred_labels == z["seg_out_pred_labels"])), 6), "spec_expected": {"point_feat_pcc_1_4": [0.99970, 0.99831, 0.99713, 0.99403], "labels": 0.9921}} (small_dir(fid) / f"{stem}.json").write_text(json.dumps(summary, indent=1)) index["goldens"][stem] = {k: summary[k] for k in ("frame", "neighbour_mode", "input", "input_sha256", "stage_counts", "node_rejects_frame")} index["goldens"][stem].update(boxes=len(summary["boxes"]), golden=f"{stem}.npz", golden_sha256=sha256_file(out / f"{stem}.npz"), taps=f"{stem}.taps.npz" if taps is not None else None) index_path.write_text(json.dumps(index, indent=1)) print(f"{stem}: counts {fr.counts} rejects={fr.rejected} boxes {len(res.boxes)} " f"taps {len(taps) if taps is not None else 0} {time.time() - t0:.1f}s" + (f" vs26tap {summary['vs_26tap_golden']}" if "vs_26tap_golden" in summary else ""), flush=True) return 0 if __name__ == "__main__": sys.exit(main())