|
| 1 | +from pathlib import Path |
| 2 | + |
| 3 | +import pytest |
| 4 | +import torch |
| 5 | +from torch.export.pt2_archive._package import load_pt2 |
| 6 | + |
| 7 | +from wherobots_export.torch.export import create_example_input_from_shape, save |
| 8 | + |
| 9 | + |
| 10 | +class TinyModel(torch.nn.Module): |
| 11 | + def __init__(self) -> None: |
| 12 | + super().__init__() |
| 13 | + self.conv = torch.nn.Conv2d(3, 4, kernel_size=3, padding=1) |
| 14 | + |
| 15 | + def forward(self, pixels: torch.Tensor) -> torch.Tensor: |
| 16 | + return self.conv(pixels).mean(dim=(2, 3)) |
| 17 | + |
| 18 | + |
| 19 | +class Normalize(torch.nn.Module): |
| 20 | + def forward(self, pixels: torch.Tensor) -> torch.Tensor: |
| 21 | + return (pixels - 0.5) / 0.5 |
| 22 | + |
| 23 | + |
| 24 | +def test_save_creates_nonempty_pt2(tmp_path: Path) -> None: |
| 25 | + output_file = tmp_path / "model.pt2" |
| 26 | + save(model=TinyModel(), output_file=output_file, input_shape=[1, 3, 32, 32], device="cpu") |
| 27 | + assert output_file.exists() |
| 28 | + assert output_file.stat().st_size > 0 |
| 29 | + |
| 30 | + |
| 31 | +def test_save_dynamic_batch_roundtrip(tmp_path: Path) -> None: |
| 32 | + model = TinyModel().eval() |
| 33 | + output_file = tmp_path / "model.pt2" |
| 34 | + save(model=model, output_file=output_file, input_shape=[-1, 3, 32, 32], device="cpu") |
| 35 | + |
| 36 | + contents = load_pt2(str(output_file)) |
| 37 | + assert set(contents.exported_programs) == {"model"} |
| 38 | + |
| 39 | + exported = contents.exported_programs["model"].module() |
| 40 | + pixels = torch.randn(4, 3, 32, 32) # batch differs from the export-time example |
| 41 | + torch.testing.assert_close(exported(pixels), model(pixels)) |
| 42 | + |
| 43 | + |
| 44 | +def test_save_with_transforms_packages_both(tmp_path: Path) -> None: |
| 45 | + output_file = tmp_path / "model.pt2" |
| 46 | + save( |
| 47 | + model=TinyModel(), |
| 48 | + output_file=output_file, |
| 49 | + input_shape=[-1, 3, 32, 32], |
| 50 | + device="cpu", |
| 51 | + transforms=Normalize(), |
| 52 | + ) |
| 53 | + contents = load_pt2(str(output_file)) |
| 54 | + assert set(contents.exported_programs) == {"model", "transforms"} |
| 55 | + |
| 56 | + |
| 57 | +def test_create_example_input_rejects_all_dynamic() -> None: |
| 58 | + with pytest.raises(ValueError): |
| 59 | + create_example_input_from_shape([-1, -1, -1, -1]) |
| 60 | + |
| 61 | + |
| 62 | +def test_create_example_input_fills_dynamic_dims() -> None: |
| 63 | + example = create_example_input_from_shape([-1, 3, -1, -1], shape_default_value=64) |
| 64 | + assert example.shape == (2, 3, 64, 64) |
0 commit comments