Download code/tt_frnet/tests/test_reference_rewrites_host.py from changh95/frnet-p150: direct link, hf CLI and curl.
- Browser
- Download file 6.91 kB
-
https://huggingface.co/changh95/frnet-p150/resolve/main/code/tt_frnet/tests/test_reference_rewrites_host.py
- Command line
-
hf download hf://changh95/frnet-p150/code/tt_frnet/tests/test_reference_rewrites_host.py
-
curl -L -o test_reference_rewrites_host.py https://huggingface.co/changh95/frnet-p150/resolve/main/code/tt_frnet/tests/test_reference_rewrites_host.py
6.91 kB
| # SPDX-License-Identifier: Apache-2.0 | |
| """The TT device dataflow (``reference/rewrites.py``) equals the literal graph (``reference/model.py``) on the CPU | |
| (torch + the weights; no device, no onnxruntime): | |
| - the whole network on the staged PandaSet sample (QT128, the faster variant; OT128 on the same frame in | |
| ``test_reference_cpu.py``) and on a small synthetic frame with tight capacities: every module tap PCC >= 0.9999999 | |
| and ``pred_probs`` within 1e-4, argmax identical -- the plan's sorted / padded points, zero-sentinel gathers, | |
| last-voxel-wins pixel tables, log-step segmented max, K-split convs and point layers and interpolation-matrix | |
| up-sampling change nothing but the summation order; | |
| - padding is harmless: two capacities give the same outputs on the real rows (to the last bits of BLAS blocking); | |
| - the log-step segmented max equals ``scatter_reduce(amax)`` for any rounds >= the frame's need, and fails with one | |
| round fewer when a segment is longer; | |
| - the align-corners interpolation matrices equal ``F.interpolate``; the emulation modes round as documented. | |
| TT_VISIBLE_DEVICES=none python -m pytest -q code/tt_frnet/tests/test_reference_rewrites_host.py | |
| """ | |
| from __future__ import annotations | |
| import numpy as np | |
| import pytest | |
| torch = pytest.importorskip("torch") | |
| pytest.importorskip("onnx") | |
| import torch.nn.functional as F # noqa: E402 | |
| from tt_frnet.host.frame import FRNetPreprocessor # noqa: E402 | |
| from tt_frnet.host.preprocess import autoware_preprocess, build_plan, segmax_rounds # noqa: E402 | |
| from tt_frnet.reference.config import FRNetConfig # noqa: E402 | |
| from tt_frnet.reference.model import FRNetTorch, scatter_max # noqa: E402 | |
| from tt_frnet.reference.rewrites import (Emulation, FRNetPlanTorch, plan_taps_to_literal, round_mode, # noqa: E402 | |
| segmax_logstep) | |
| from tt_frnet.reference.weights import MODULE_TAPS, load_weights # noqa: E402 | |
| from tt_frnet.tests import _data # noqa: E402 | |
| from tt_frnet.ttaw.golden import TapRegistry # noqa: E402 | |
| from tt_frnet.ttaw.metrics import pcc # noqa: E402 | |
| from tt_frnet.ttaw.ops.upsample import interp_matrix # noqa: E402 | |
| THREADS = 4 | |
| PATTERNS = list(MODULE_TAPS) + ["backbone.segmax*", "decoder.up*"] | |
| def qt128(): | |
| torch.set_num_threads(THREADS) | |
| w = _data.weights_dir() | |
| return load_weights(w, "qt128"), FRNetConfig.from_model_dir(w, "qt128") | |
| def _compare(weights, cfg, inputs, n_cap=None, m_cap=None): | |
| plan = build_plan(inputs, cfg, pre_norm=(weights.pre_norm_scale, weights.pre_norm_shift), n_cap=n_cap, | |
| m_cap=m_cap) | |
| tl, tr = TapRegistry(include=PATTERNS), TapRegistry(include=PATTERNS) | |
| lit = FRNetTorch(weights)(inputs.points, inputs.coors, inputs.voxel_coors, inputs.inverse_map, taps=tl) | |
| out = FRNetPlanTorch(weights)(plan, taps=tr) | |
| worst = min((pcc(plan_taps_to_literal(n, tr[n], plan), tl[n]), n) for n in tl.names()) | |
| return plan, lit, out, worst, tr | |
| def test_plan_equals_literal_on_the_sample(qt128): | |
| weights, cfg = qt128 | |
| _data.need_pandaset() | |
| prep = FRNetPreprocessor(cfg, plan=False)(_data.SAMPLE, _data.SAMPLE_CALIB) | |
| plan, lit, out, worst, _ = _compare(weights, cfg, prep.inputs) | |
| assert plan.n_cap == cfg.n_cap == 120000 and plan.m_cap == 60000 | |
| assert worst[0] >= 0.9999999, worst | |
| got = plan.unsort(out) | |
| assert np.abs(got - lit).max() < 1e-4 | |
| np.testing.assert_array_equal(got.argmax(1), lit.argmax(1)) | |
| assert np.allclose(out[plan.n:].sum(1), 1.0, atol=1e-5) # padded rows: finite, ignored | |
| def test_plan_on_a_small_frame_and_padding_is_harmless(qt128): | |
| weights, cfg = qt128 | |
| rng = np.random.default_rng(7) | |
| n = 6000 | |
| az = rng.uniform(-np.pi, np.pi, n) | |
| el = np.radians(rng.uniform(-50, 50, n)) | |
| r = rng.uniform(2, 60, n) | |
| pts = np.c_[r * np.cos(el) * np.cos(az), r * np.cos(el) * np.sin(az), r * np.sin(el), | |
| rng.uniform(0, 255, n)].astype(np.float32) | |
| pts[:500, :3] = pts[0, :3] + rng.normal(0, 0.01, (500, 3)) # one dense cell: a long segment | |
| inputs = autoware_preprocess(pts, cfg, crop_bounds=(0.0,) * 6) | |
| p1, lit, o1, worst, t1 = _compare(weights, cfg, inputs, n_cap=inputs.num_points + 1, m_cap=inputs.num_voxels + 1) | |
| assert p1.max_segment_length >= 400 and segmax_rounds(p1.max_segment_length) >= 9 | |
| assert worst[0] >= 0.9999999, worst | |
| assert np.abs(p1.unsort(o1) - lit).max() < 1e-4 | |
| p2 = build_plan(inputs, cfg, pre_norm=(weights.pre_norm_scale, weights.pre_norm_shift), | |
| n_cap=inputs.num_points + 4096, m_cap=inputs.num_voxels + 777) | |
| o2 = FRNetPlanTorch(weights)(p2) | |
| # padded rows never reach a real row: only BLAS blocking of the taller matrices may move the last bits | |
| np.testing.assert_allclose(p2.unsort(o2), p1.unsort(o1), rtol=0, atol=1e-6) | |
| def test_segmax_logstep_equals_scatter_reduce(qt128): | |
| _, cfg = qt128 | |
| rng = np.random.default_rng(3) | |
| pts = np.c_[rng.normal(0, 15, (3000, 3)), rng.uniform(0, 255, 3000)].astype(np.float32) | |
| pts[:300, :3] = pts[0, :3] | |
| inputs = autoware_preprocess(pts, cfg, crop_bounds=(0.0,) * 6) | |
| plan = build_plan(inputs, cfg, n_cap=inputs.num_points + 64, m_cap=inputs.num_voxels + 9) | |
| x = torch.randn(plan.n_cap, 5) | |
| longest = int(np.argmax(np.diff(plan.seg_offsets))) | |
| x[int(plan.seg_offsets[longest])] = 100.0 # the longest segment's max sits on its FIRST row | |
| ref = scatter_max(x[: plan.n], torch.from_numpy(plan.seg_id[: plan.n]), plan.m) | |
| need = segmax_rounds(plan.max_segment_length) | |
| for rounds in (need, need + 2): | |
| assert torch.equal(segmax_logstep(x, plan, rounds)[: plan.m], ref) | |
| assert not torch.equal(segmax_logstep(x, plan, need - 1)[: plan.m], ref) | |
| def test_interp_matrices_equal_align_corners_interpolate(hw, s): | |
| H, W = hw | |
| x = torch.randn(1, 3, H // s, W // s, dtype=torch.float64) | |
| ah = torch.from_numpy(interp_matrix(H // s, H, mode="linear", align_corners=True)) | |
| aw = torch.from_numpy(interp_matrix(W // s, W, mode="linear", align_corners=True)) | |
| y = torch.einsum("hi,bcij,wj->bchw", ah, x, aw) | |
| ref = F.interpolate(x, size=(H, W), mode="bilinear", align_corners=True) | |
| assert torch.allclose(y, ref, atol=1e-12) | |
| def test_rounding_modes(): | |
| x = torch.tensor([1.0, 1.0 + 2 ** -8, 1.0 + 2 ** -7 + 2 ** -9, -3.14159265, 1e-30]) | |
| assert torch.equal(round_mode(x, "fp32"), x) | |
| assert torch.equal(round_mode(x, "bf16"), x.to(torch.bfloat16).float()) | |
| t = round_mode(x, "tf32") | |
| assert (t.view(torch.int32) & 0x1FFF).eq(0).all() and (t.abs() <= x.abs()).all() | |
| e = Emulation.parse("conv_act=bf16,point_act=tf32") | |
| assert e.label() == "conv_act=bf16,point_act=tf32" and not e.exact and Emulation().exact | |
| with pytest.raises(ValueError): | |
| Emulation.parse("point_act=fp16") | |