| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| """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 |
|
|
|
|
| |
|
|
|
|
| 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) |
| 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 |
| |
| |
| assert torch.allclose(got, expected, atol=1e-5, rtol=1e-5) |
|
|
|
|
| def test_apply_rotary_real_identity_at_zero_angle(): |
| |
| 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) |
|
|
|
|
| |
|
|
|
|
| def _vision_config() -> Qwen3VLVisionConfig: |
| return Qwen3VLVisionConfig(hidden_size=32, num_heads=4) |
|
|
|
|
| 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) |
| export._assert_vision_rotary_matches_analytic(vision, cfg) |
|
|
|
|
| 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 |
| 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() |
| with pytest.raises(RuntimeError, match="not found"): |
| export._assert_vision_rotary_matches_analytic(vision, _vision_config()) |
|
|