Skip to content

Compat matrix: current-release rows

This page records rows of the TorchLens compat matrix that were measured against releases newer than the pinned CI environment (python 3.10, torch 2.13.0, transformers 5.18.0). Each row is a capture plus validation run, executed by a test in tests/real_model/test_compat_matrix_rows.py, or a named export check. A row says exactly what ran, on which versions and which kind of host; a row that did not run says so and never shows as passing.

The broader support statement (torch 2.1 to 2.12+ for eager capture, the torch version table, the limitations catalog) lives in limitations.md and the torch capability rows of tl.report.doctor().

Rows measured 2026-11-03

Row Test torch transformers python Host Result
Router-logit outputs (output_router_logits=True) test_router_logit_outputs_capture_exactly_and_validate[mixtral, qwen2_moe, olmoe] 2.14.1+cpu 5.19.0 3.11.15 Linux x86_64 CPU worker pass (3 of 3)
DTensor tensor parallelism, two-process CPU gloo mesh test_dtensor_tensor_parallel_on_a_two_process_cpu_mesh_refuses_typed 2.14.1+cpu 5.19.0 3.11.15 Linux x86_64 CPU worker pass: typed refusal on both ranks (torch-native sharding)
flash_attention_4 test_flash_attention_4_capture_and_validate 2.14.1+cu126 5.19.0 3.11.2 Linux x86_64, one NVIDIA L40S (compute capability 8.9), flash-attn-4 4.0.0b37 pass under torch.no_grad(): exact output, each attention call one autogradfunction boundary op (exit flash_attn.cute), forward validation passes, witnessed trace verified; with autograd enabled the call's second output (log-sum-exp) validates through the kernel table's declared per-output dependence (q and k only)
flash_attention_4 test_flash_attention_4_capture_and_validate, test_flash_attention_4_boundary_owns_every_dispatch 2.13.0+cu126 5.18.0 3.10.19 Linux x86_64, one NVIDIA L40S (compute capability 8.9), flash-attn-4 4.0.0b37 pass (the pinned CI versions on a GPU): same shape as the row above; with autograd enabled the log-sum-exp output validates through the kernel table's declared per-output dependence. transformers 5.18.0's flash-attention wrapper tests a Python value first and a tensor bool in its elif, which exposed a TorchLens conditional-record gap (the elif arm carried no terminal bool, failing invariant 15); fixed in this release, with a CPU regression test
flash_attention_2 test_flash_attention_2_captures_exactly, test_flash_attention_2_validates, test_flash_attention_2_witness_sees_no_gap 2.8.0+cu126 5.18.0 3.10.19 Linux x86_64, one NVIDIA L40S (compute capability 8.9), flash-attn 2.8.3.post2 capture exact and natively recorded (the kernel is a torch custom op: four sibling ops per attention call, no boundary op, no warning), witnessed trace verified; forward validation passes through the kernel table's declared per-output dependence for the custom op (log-sum-exp and dropout mask read q and k, the rng state none of q, k, v; the attention output's every edge tested). The flash-attn project ships wheels for torch 2.4 to 2.8 only, so the row could not run on torch 2.13 or 2.14
Router-logit outputs as above 2.14.1+cu126 5.19.0 3.11.2 Linux x86_64 GPU host (CPU tensors) pass (3 of 3)
DTensor tensor parallelism, two-process CPU gloo mesh as above 2.14.1+cu126 5.19.0 3.11.2 Linux x86_64 GPU host, CUDA hidden from the ranks pass
Router-logit outputs as above 2.14.1 5.19.0 3.11.14 macOS arm64 (Apple silicon) pass (3 of 3)
DTensor tensor parallelism, two-process CPU gloo mesh as above 2.14.1 5.19.0 3.11.14 macOS arm64 (Apple silicon) pass: typed refusal on both ranks (torch-native sharding)
DTensor tensor parallelism loaded by transformers (tp_plan="auto") test_transformers_tp_plan_load_on_a_two_process_cpu_mesh_refuses_typed 2.14.1+cpu 5.19.0 3.11.15 Linux x86_64 CPU worker pass: typed refusal on both ranks; the sharded logits match the unsharded load

Router-logit outputs

Three small mixture-of-experts causal LMs (Mixtral, Qwen2-MoE and OLMoE architectures; two layers, four experts, top-2 routing, constructed weights, no download) run with output_router_logits=True under both the eager and the sdpa attention implementations. Each trace returns the exact model output: the logits and every per-layer router-logit tensor are bit-equal to the direct call, each router-logit tensor is an output op of the trace, and forward replay validation (tl.validate(..., scope="forward")) passes.

DTensor tensor parallelism

Two processes form a CPU gloo device mesh. A two-linear MLP is sharded over it through torch.distributed.tensor.parallel.parallelize_module (column-wise then row-wise), and the sharded forward runs (the parameters are DTensors). On every rank, capture refuses at entry with the typed tl.errors.DistributedCaptureUnsupportedError, whose findings name the dtensor and tensor_parallel kinds. This refusal is the supported behaviour today: capturing below the DTensor dispatch layer would record rank-local shims instead of the real ops. A capture design for sharded modules is planned but not built; until it is, capture a rank-local dense copy of the module.

A model loaded by transformers with distributed_config=transformers.DistributedConfig(tp_plan="auto") is the row's second half. A tiny random-weight Llama checkpoint, pinned by revision and digest in the test artifact registry, loads sharded on each rank (the attention projections are DTensors), its logits match the same checkpoint loaded unsharded, and capture refuses with the same typed error and kinds. Transformers applies a tp_plan only when the rank layout is in the process environment (RANK, WORLD_SIZE, LOCAL_RANK); without it the checkpoint loads unsharded and there is no tensor parallelism to test. Transformers refuses a tp_plan on any machine with an MPS device, so this half does not run on macOS. On macOS, gloo needs the loopback interface for a local mesh (GLOO_SOCKET_IFNAME=lo0; the test sets it when unset), else the shard scatter waits out its timeout.

flash_attention_4

attn_implementation="flash_attention_4" needs a CUDA device that the FlashAttention 4 package supports (compute capability 8.0 and newer; transformers refuses it on CPU). FlashAttention 4 (flash_attn.cute) allocates its output with torch.empty and fills it from a CuTe kernel reached through TVM FFI, below the torch function layer TorchLens wraps and without the numpy or dlpack export the dynamic foreign-kernel detector sees, so a plain capture would record the allocation in place of the attention. TorchLens therefore records every autograd.Function of flash_attn.cute.interface (FlashAttnFunc, FlashAttnVarlenFunc) as one autogradfunction boundary op with transform_kind="autograd.Function" and transform_config["exits"] == ["flash_attn.cute"]: its parents are the projected q, k and v, its output the attention output, and it replays by calling the Function's apply again (the treatment numpy and dlpack round trips already get). The op is disclosed once per Function class per capture by the foreign_kernel_boundary warning. On the measured GPU the trace returns the exact logits, and with the completeness witness armed for every capture (torchlens.backends.torch.wrappers.wrap_torch(completeness_witness=True)) the trace is verified (trace.provenance.verified is True, no CaptureGapWarning). Under torch.no_grad() the kernel returns the attention output alone and tl.validate(..., scope="forward") passes with the dispatch census balanced (nothing exempted). With autograd enabled the kernel also returns its log-sum-exp, so each call is two sibling ops sharing one func_call_id (index 0 the bfloat16 attention output, index 1 the float32 log-sum-exp that nothing consumes); the log-sum-exp depends on q and k only. The known-kernel table declares that per-output dependence (ruling: "(b) Declare per-output dependence per kernel in a table."), so the perturbation check excuses the log-sum-exp's v edge by declaration and nothing else: the attention output's q, k and v edges and the log-sum-exp's q and k edges stay perturbation-tested, an undeclared kernel, output or argument position is strict, and test_flash_attention_4_grad_enabled_validates asserts the grad-enabled capture validates. The same model on sdpa or eager attention, same GPU and dtype (bfloat16), captures and validates op by op. tl.report.doctor() reports whether the package is installed as HAS_FLASH_ATTENTION_4_INTERFACE (read from the package's search path; nothing is imported). The row's test asserts the boundary ops, the exact output and the passing validation under torch.no_grad() and with autograd enabled; a companion test pins the verified marking under the armed witness.

flash_attention_2

attn_implementation="flash_attention_2" needs a CUDA device and the flash-attn package, whose prebuilt wheels pin a torch release (2.4 to 2.8 as of flash-attn 2.8.3.post2; there is no wheel for torch 2.13 or 2.14, and the PyPI distribution is a source build). The kernel entry flash_attn.flash_attn_interface._flash_attn_forward is a torch custom op, so the torch function layer TorchLens wraps sees it directly: no boundary op, no foreign_kernel_boundary disclosure and no CaptureGapWarning, with the completeness witness armed as well. Each attention call is recorded as four sibling ops sharing one func_call_id (index 0 the bfloat16 attention output that feeds the next op; the float32 log-sum-exp, the dropout mask and the int64 rng state, which nothing consumes), each with the projected q, k and v as parents. On the measured GPU the trace returns the exact logits under torch.no_grad() and with autograd enabled. Forward validation passes (test_flash_attention_2_validates): the unconsumed siblings do not read every parent the call carries (the log-sum-exp and the dropout mask read q and k, the rng state none of q, k and v), and the kernel table declares that per-output dependence for the custom op flash_attn::_flash_attn_forward (ruling: "(b) Declare per-output dependence per kernel in a table."), so the perturbation check excuses exactly those edges. The attention output has no row and keeps every edge tested; another operator under the same name, a parent at any other position or a keyword stays strict. The dated gap flash-attention-2-impl in the real-model suite is closed. With dropout (dropout_p > 0, return_attn_probs=True, train mode) every output replays bit for bit (test_flash_attention_2_dropout_replays_exactly): replay restores the captured CUDA generator state, so the kernel redraws the same dropout mask, and the mask's padding past the sequence lengths, which the kernel leaves undefined, is zeroed where the recorded call returns, in the capture and in the replay alike.

Model Explorer with adapter 0.2.x

ai-edge-model-explorer-adapter 0.2.0 (August 2026) replaced the adapter's Python extension with a C ABI shared library. Every ai-edge-model-explorer release the model-explorer extra admits (>=0.1.33,<0.2) depends on the 0.2 adapter line: 0.1.33 needs ~=0.2.0 and 0.1.34 needs ~=0.2.1. TorchLens writes Model Explorer JSON graph collections, which the app loads through its built-in JSON path; the adapter's converters handle other formats and are not on TorchLens' export path.

Measured 2026-11-03 on a Linux x86_64 CPU worker in the pinned CI environment (python 3.10, torch 2.13.0, transformers 5.18.0):

Model Explorer Adapter Model Explorer test files ResNet-18 export GPT-2 small export Served read-back
0.1.33 (installed without dependencies, as the nightly contract job does) not installed 61 passed pass pass not run
0.1.33 0.2.0 61 passed pass pass pass
0.1.33 0.2.1 61 passed pass pass not run
0.1.34 0.2.1 61 passed pass pass pass

Each export check traces the model (ResNet-18 with random weights on a 1x3x224x224 input; GPT-2 small from its default config with random weights on 8 token ids), writes the file with tl.export.model_explorer, passes tl.export.validate_model_explorer_payload, rebuilds every graph through the vendor's graph_builder dataclasses (no duplicate node ids). The served read-back starts tl.export.model_explorer_serve on the file, checks the health and index routes, and reads the graph back over the server's file route equal to the file. ResNet-18 exports 151 nodes and 158 edges in the execution graph (about 564 KB); GPT-2 small exports 567 nodes and 638 edges (about 1.39 MB).