Download Isaac-GR00T/tests/scripts/deployment/test_export_vit_rotary.py from Timsty/groot_deployment: direct link, hf CLI and curl.
- Browser
- Download file 5.27 kB
-
https://huggingface.co/Timsty/groot_deployment/resolve/main/Isaac-GR00T/tests/scripts/deployment/test_export_vit_rotary.py
- Command line
-
hf download hf://Timsty/groot_deployment/Isaac-GR00T/tests/scripts/deployment/test_export_vit_rotary.py
-
curl -L -o test_export_vit_rotary.py https://huggingface.co/Timsty/groot_deployment/resolve/main/Isaac-GR00T/tests/scripts/deployment/test_export_vit_rotary.py
5.27 kB
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # SPDX-License-Identifier: Apache-2.0 | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| """CPU-only oracle tests for the ViT ONNX export rotary path. | |
| Covers two silent-failure risks in ``scripts/deployment/export_onnx_n1d7.py`` | |
| whose only existing coverage is a backend-vs-backend cosine fingerprint (blind to | |
| common-mode error): | |
| - ``_apply_rotary_real`` re-implements the vision rotary application with | |
| real-valued ops. An independent closed-form (complex-arithmetic) oracle pins it, | |
| so a rotate-half / sign / precision drift is caught. | |
| - The exporter freezes ``rot_pos_emb``-derived cos/sin built from a non-persistent | |
| ``inv_freq`` buffer. ``_assert_vision_rotary_matches_analytic`` must abort the | |
| export when that buffer drifts from the analytic value. | |
| No checkpoint download or GPU is required. | |
| """ | |
| from __future__ import annotations | |
| import os | |
| import sys | |
| import pytest | |
| import torch | |
| DEPLOY_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../scripts/deployment")) | |
| if DEPLOY_DIR not in sys.path: | |
| sys.path.insert(0, DEPLOY_DIR) | |
| export = pytest.importorskip("export_onnx_n1d7") | |
| qwen3_vl = pytest.importorskip("transformers.models.qwen3_vl.modeling_qwen3_vl") | |
| qwen3_vl_config = pytest.importorskip("transformers.models.qwen3_vl.configuration_qwen3_vl") | |
| Qwen3VLVisionConfig = qwen3_vl_config.Qwen3VLVisionConfig | |
| Qwen3VLVisionRotaryEmbedding = qwen3_vl.Qwen3VLVisionRotaryEmbedding | |
| # --- _apply_rotary_real closed-form parity -------------------------------- | |
| def _complex_oracle_apply_rotary( | |
| x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor | |
| ) -> torch.Tensor: | |
| """Independent rotary application via complex multiplication (float64). | |
| The production path is ``out = x * cos + rotate_half(x) * sin`` with | |
| ``rotate_half(x) = [-x2, x1]``. For the vision embeddings (``emb = | |
| cat(freqs, freqs)``, so the two cos/sin halves are equal) that is exactly the | |
| complex product ``(x1 + i x2) * (cos + i sin)`` re-split into ``[real, imag]`` | |
| -- a genuinely different implementation, so a shared bug cannot hide. | |
| """ | |
| half = x.shape[-1] // 2 | |
| x1 = x[..., :half].double() | |
| x2 = x[..., half:].double() | |
| c = cos[..., :half].double().unsqueeze(1) | |
| s = sin[..., :half].double().unsqueeze(1) | |
| z = torch.complex(x1, x2) * torch.complex(c, s) | |
| return torch.cat([z.real, z.imag], dim=-1) | |
| def test_apply_rotary_real_matches_complex_oracle(): | |
| torch.manual_seed(0) | |
| seq, heads, half = 7, 3, 4 | |
| x = torch.randn(seq, heads, 2 * half) | |
| freqs = torch.randn(seq, half) | |
| emb = torch.cat([freqs, freqs], dim=-1) # how the exporter builds cos/sin | |
| cos, sin = emb.cos(), emb.sin() | |
| got = export._apply_rotary_real(x, cos, sin).double() | |
| expected = _complex_oracle_apply_rotary(x, cos, sin) | |
| assert got.shape == x.shape | |
| # _apply_rotary_real computes in float32 (to match the exported path), while the | |
| # oracle is float64, so tolerate float32-level rounding rather than exact equality. | |
| assert torch.allclose(got, expected, atol=1e-5, rtol=1e-5) | |
| def test_apply_rotary_real_identity_at_zero_angle(): | |
| # cos=1, sin=0 (freqs=0) must be a no-op, whatever the rotate_half layout is. | |
| x = torch.randn(5, 2, 8) | |
| cos = torch.ones(5, 8) | |
| sin = torch.zeros(5, 8) | |
| assert torch.allclose(export._apply_rotary_real(x, cos, sin), x, atol=1e-6) | |
| # --- export-time rotary analytic oracle ----------------------------------- | |
| def _vision_config() -> Qwen3VLVisionConfig: | |
| return Qwen3VLVisionConfig(hidden_size=32, num_heads=4) # head_dim = 8 | |
| def _fake_vision(dim: int) -> torch.nn.Module: | |
| vision = torch.nn.Module() | |
| vision.rotary_pos_emb = Qwen3VLVisionRotaryEmbedding(dim) | |
| return vision | |
| def test_rotary_oracle_passes_for_correct_inv_freq(): | |
| cfg = _vision_config() | |
| dim = (cfg.hidden_size // cfg.num_heads) // 2 | |
| vision = _fake_vision(dim) # fresh module -> analytic inv_freq | |
| export._assert_vision_rotary_matches_analytic(vision, cfg) # must not raise | |
| def test_rotary_oracle_raises_on_corrupt_inv_freq(): | |
| cfg = _vision_config() | |
| dim = (cfg.hidden_size // cfg.num_heads) // 2 | |
| vision = _fake_vision(dim) | |
| rotary = vision.rotary_pos_emb | |
| rotary.inv_freq = rotary.inv_freq + 1.0 # drift from the analytic value | |
| with pytest.raises(RuntimeError, match="diverges from the analytic oracle"): | |
| export._assert_vision_rotary_matches_analytic(vision, cfg) | |
| def test_rotary_oracle_raises_when_layout_missing(): | |
| vision = torch.nn.Module() # no rotary_pos_emb attribute at all | |
| with pytest.raises(RuntimeError, match="not found"): | |
| export._assert_vision_rotary_matches_analytic(vision, _vision_config()) | |