import importlib.util import base64 import json import os from pathlib import Path import pickle import subprocess import sys import tempfile import unittest from functools import partial from unittest.mock import patch import numpy as np _script_path = Path(__file__).resolve().parents[4] / "scripts" / "model_compiler.py" _script_spec = importlib.util.spec_from_file_location("starpilot_test_model_compiler", _script_path) assert _script_spec is not None and _script_spec.loader is not None model_compiler = importlib.util.module_from_spec(_script_spec) _script_spec.loader.exec_module(model_compiler) def varint(value): result = bytearray() while value > 127: result.append((value & 127) | 128) value >>= 7 return bytes(result) + bytes([value]) def field(number, value): if isinstance(value, int): return varint(number << 3) + varint(value) if isinstance(value, str): value = value.encode() return varint((number << 3) | 2) + varint(len(value)) + value def value_info(name, shape): dimensions = b"".join(field(1, field(1, dimension)) for dimension in shape) return field(1, name) + field(2, field(1, field(1, 1) + field(2, dimensions))) def stateful_onnx(): inputs = {"new_img": (2, 6, 2, 2), "state": (1, 2), "desire": (1, 2)} add = field(1, "state") + field(1, "desire") + field(2, "next_state") + field(4, "Add") identity = field(1, "next_state") + field(2, "outputs") + field(4, "Identity") graph = field(1, add) + field(1, identity) + field(2, "stateful-fixture") graph += b"".join(field(11, value_info(name, shape)) for name, shape in inputs.items()) graph += field(12, value_info("outputs", (1, 2))) + field(12, value_info("next_state", (1, 2))) metadata = field(1, "output_slices") + field(2, base64.b64encode(pickle.dumps({"fixture": slice(0, 2)}))) return field(1, 8) + field(7, graph) + field(8, field(2, 13)) + field(14, metadata) class CompileTest(unittest.TestCase): def test_legacy_split_and_supercombo_history_survive_arena_round_trip(self): from tinygrad import Context, TinyJit from openpilot.starpilot.models.compile import (compile_jit, make_random_images, make_run_split_policy, make_run_supercombo, make_split_input_queues, make_supercombo_input_queues, FAST_POLICY_INPUTS) image_shapes = {"input_imgs": (1, 12, 2, 2), "big_input_imgs": (1, 12, 2, 2)} policy_shapes = {"features_buffer": (1, 3, 2), "desire": (1, 2, 2), "traffic_convention": (1, 2)} def vision(inputs): values = [inputs[name].cast("float32").mean().reshape(1, 1) for name in image_shapes] return {"outputs": values[0].cat(values[1], dim=1)} def policy(inputs): return {"outputs": inputs["features_buffer"].sum(axis=1) + inputs["desire"].sum(axis=1) + inputs["traffic_convention"]} def supercombo(inputs): return {"outputs": policy(inputs)["outputs"] + vision(inputs)["outputs"]} with Context(DEV="CPU:LLVM", IMAGE=0, FLOAT16=0): for kind in ("split", "supercombo"): with self.subTest(kind=kind): if kind == "split": metadata = {"vision": {"input_shapes": image_shapes, "output_slices": {"hidden_state": slice(0, 2)}}, "policy": {"input_shapes": policy_shapes}} run = make_run_split_policy(vision, {"policy": policy}, metadata, ["policy"], 2) queues = partial(make_split_input_queues, image_shapes, policy_shapes, 2) else: metadata = {"model": {"input_shapes": image_shapes | policy_shapes}} run = make_run_supercombo(supercombo, metadata, 2) queues = partial(make_supercombo_input_queues, image_shapes | policy_shapes, 2) compile_jit(TinyJit(run, prune=True), partial(make_random_images, ["warped"], (2, 6, 2, 2)), FAST_POLICY_INPUTS, queues, validation_runs=5) def test_mapped_stateful_image_queue_survives_jit_reload_and_reset(self): from types import SimpleNamespace from tinygrad import Context, Device, Tensor, TinyJit from tinygrad.dtype import dtypes from openpilot.starpilot.models.compile import compile_jit, make_run_stateful_supercombo, make_stateful_input_queues metadata = {"input_shapes": {"new_img": (2, 6, 4, 8), "action_t": (1, 2), "history": (1, 2)}, "state_pairs": {"history": "next_history"}, "input_dtypes": {"history": "float32"}, "mapped_image_input": True} class Model: graph_inputs = {"new_img": SimpleNamespace(dtype=dtypes.uint8), "action_t": SimpleNamespace(dtype=dtypes.float32)} def __call__(self, inputs): state = inputs["history"] + inputs["new_img"].float().mean() + inputs["action_t"] return {"outputs": state, "next_history": state} with Context(DEV="CPU:LLVM", IMAGE=0, FLOAT16=0): queues, _ = make_stateful_input_queues(metadata, Device.DEFAULT) image_buffer = queues["mapped_gpu_image"].uop.base.buffer self.assertTrue(image_buffer.options.cpu_access) self.assertIsNotNone(image_buffer.get_storage().host) run = make_run_stateful_supercombo(Model(), metadata) with self.assertRaisesRegex(ValueError, "Mapped stateful image queue is missing"): run(Tensor(np.zeros((2, 6, 4, 8), dtype=np.uint8)), queues["packed_npy_inputs"], history=queues["history"]) compile_jit(TinyJit(run, prune=True), lambda rng: {"warped": Tensor(rng.integers(0, 256, (2, 6, 4, 8), dtype=np.uint8), device="NPY").realize()}, ("packed_npy_inputs", "mapped_gpu_image", "history"), partial(make_stateful_input_queues, metadata), validation_runs=3) def test_legacy_stateful_queue_layout_and_values_remain_unchanged(self): from tinygrad import Context, Device from openpilot.starpilot.models.compile import make_stateful_input_queues metadata = {"input_shapes": {"new_img": (2, 6, 4, 8), "action_t": (1, 2), "history": (1, 2)}, "state_pairs": {"history": "next_history"}, "input_dtypes": {"history": "float32"}} with Context(DEV="CPU:LLVM"): queues, host = make_stateful_input_queues(metadata, Device.DEFAULT) self.assertEqual(tuple(queues), ("img_q", "big_img_q", "tfm", "big_tfm", "packed_npy_inputs", "history")) self.assertEqual(tuple(host), ("tfm", "big_tfm", "action_t")) expected_shapes = {"img_q": (1, 6, 4, 8), "big_img_q": (1, 6, 4, 8), "tfm": (3, 3), "big_tfm": (3, 3), "packed_npy_inputs": (2,), "history": (1, 2)} for name, value in queues.items(): with self.subTest(queue=name): self.assertEqual(value.shape, expected_shapes[name]) self.assertFalse(value.uop.base.buffer.options.cpu_access) np.testing.assert_array_equal(value.numpy(), np.zeros(expected_shapes[name])) for value in host.values(): np.testing.assert_array_equal(value, np.zeros(value.shape)) def test_out_of_band_pickle_without_default_device_buffers(self): import io import struct from tinygrad import Context, Tensor from tinygrad_repo.examples.openpilot.helpers import dump_pickle, load_pickle with Context(DEV="CPU:LLVM"), tempfile.TemporaryDirectory() as temporary: path = Path(temporary) / "fixture.pkl" dump_pickle({"metadata": (1, "warp")}, path) self.assertEqual(load_pickle(path, out_of_band=True), {"metadata": (1, "warp")}) values = np.arange(12, dtype=np.float32).reshape(2, 6) dump_pickle({"other_device": Tensor(values, device="NPY").realize()}, path) np.testing.assert_array_equal(load_pickle(path, out_of_band=True)["other_device"].numpy(), values) dump_pickle({"default_device": Tensor(values).realize()}, path) np.testing.assert_array_equal(load_pickle(path, out_of_band=True)["default_device"].numpy(), values) opcodes = io.BytesIO() marker = object() class ReferencingPickler(pickle.Pickler): def persistent_id(self, value): return (1, "invalid", 0) if value is marker else None pickler = ReferencingPickler(opcodes) pickler.dump(marker) path.write_bytes(struct.pack("