From ac0cb8761f48f6cefeeecc5d83efb364bf9ea024 Mon Sep 17 00:00:00 2001 From: Jacob Szwejbka Date: Tue, 27 Oct 2026 09:08:49 -0700 Subject: [PATCH 01/12] [RELEASE ONLY CHANGES] Prepare ExecuTorch 1.6 release dependencies --- .ci/docker/ci_commit_pins/pytorch.txt | 2 +- .ci/scripts/test-rocm-aoti.sh | 4 +- .ci/scripts/test-rocm-voxtral.sh | 8 ++-- .ci/scripts/test_model_e2e.sh | 2 +- .ci/scripts/test_wheel_package_qnn.sh | 4 +- .ci/scripts/tests/test_cu134_dependencies.py | 46 +++++++++---------- .github/workflows/windows-msvc.yml | 2 +- .../models/moshi/mimi/install_requirements.sh | 2 +- install_requirements.py | 42 ++++++----------- setup.py | 14 ++---- torch_pin.py | 3 +- 11 files changed, 52 insertions(+), 77 deletions(-) diff --git a/.ci/docker/ci_commit_pins/pytorch.txt b/.ci/docker/ci_commit_pins/pytorch.txt index 257c075fe46..a6def14a90f 100644 --- a/.ci/docker/ci_commit_pins/pytorch.txt +++ b/.ci/docker/ci_commit_pins/pytorch.txt @@ -1 +1 @@ -e91899ca45f99811049ac7d539820f92a9acddde +30c000d9a4b266157c9515acd195083898a00787 diff --git a/.ci/scripts/test-rocm-aoti.sh b/.ci/scripts/test-rocm-aoti.sh index 44ad516d197..03b123dd4be 100644 --- a/.ci/scripts/test-rocm-aoti.sh +++ b/.ci/scripts/test-rocm-aoti.sh @@ -9,8 +9,8 @@ set -euo pipefail ROCM_VERSION="${ROCM_VERSION:-7.2}" ROCM_PATH="${ROCM_PATH:-/opt/rocm}" -PYTORCH_ROCM_INDEX="${PYTORCH_ROCM_INDEX:-https://download.pytorch.org/whl/test/rocm${ROCM_VERSION}}" -TORCHAO_ROCM_WHEEL_BASE="${TORCHAO_ROCM_WHEEL_BASE:-https://download.pytorch.org/whl/nightly/rocm${ROCM_VERSION}}" +PYTORCH_ROCM_INDEX="${PYTORCH_ROCM_INDEX:-https://download.pytorch.org/whl/rocm${ROCM_VERSION}}" +TORCHAO_ROCM_WHEEL_BASE="${TORCHAO_ROCM_WHEEL_BASE:-https://download.pytorch.org/whl/rocm${ROCM_VERSION}}" ROCM_CI_TMP_ROOT="${RUNNER_TEMP:-/tmp}" mkdir -p "${ROCM_CI_TMP_ROOT}" 2>/dev/null || ROCM_CI_TMP_ROOT=/tmp ROCM_CI_TMPDIR="$(mktemp -d "${ROCM_CI_TMP_ROOT}/executorch-rocm-ci.XXXXXX")" diff --git a/.ci/scripts/test-rocm-voxtral.sh b/.ci/scripts/test-rocm-voxtral.sh index a47e50a4971..5f5da762746 100644 --- a/.ci/scripts/test-rocm-voxtral.sh +++ b/.ci/scripts/test-rocm-voxtral.sh @@ -11,8 +11,8 @@ ROCM_VERSION="${ROCM_VERSION:-7.2}" ROCM_PATH="${ROCM_PATH:-/opt/rocm}" EXPECTED_ROCM_ARCH="${EXPECTED_ROCM_ARCH:-gfx950}" EXPECTED_WARP_SIZE="${EXPECTED_WARP_SIZE:-64}" -PYTORCH_ROCM_INDEX="${PYTORCH_ROCM_INDEX:-https://download.pytorch.org/whl/test/rocm${ROCM_VERSION}}" -TORCHAO_ROCM_WHEEL_BASE="${TORCHAO_ROCM_WHEEL_BASE:-https://download.pytorch.org/whl/nightly/rocm${ROCM_VERSION}}" +PYTORCH_ROCM_INDEX="${PYTORCH_ROCM_INDEX:-https://download.pytorch.org/whl/rocm${ROCM_VERSION}}" +TORCHAO_ROCM_WHEEL_BASE="${TORCHAO_ROCM_WHEEL_BASE:-https://download.pytorch.org/whl/rocm${ROCM_VERSION}}" VOXTRAL_CI_TMP_ROOT="${RUNNER_TEMP:-/tmp}" if ! mkdir -p "${VOXTRAL_CI_TMP_ROOT}" 2>/dev/null || [[ ! -w "${VOXTRAL_CI_TMP_ROOT}" ]]; then @@ -73,8 +73,8 @@ conda install -y -c conda-forge ffmpeg 'libstdcxx-ng>=12' python -m pip install 'fsspec[http]<=2025.3.0' python -m pip install datasets huggingface_hub librosa mistral-common safetensors soundfile -python -m pip install torchcodec==0.11.0 \ - --extra-index-url https://download.pytorch.org/whl/test/cpu +python -m pip install torchcodec==0.17.0 \ + --index-url https://download.pytorch.org/whl/cpu python - <<'PY' import os diff --git a/.ci/scripts/test_model_e2e.sh b/.ci/scripts/test_model_e2e.sh index 786cd81f9e2..11f58f96d78 100755 --- a/.ci/scripts/test_model_e2e.sh +++ b/.ci/scripts/test_model_e2e.sh @@ -390,7 +390,7 @@ elif [[ "$MODEL_NAME" == *whisper* ]] || [ "$MODEL_NAME" = "voxtral_realtime" ]; fi fi pip install datasets soundfile - pip install torchcodec==0.11.0 --extra-index-url https://download.pytorch.org/whl/test/cpu + pip install torchcodec==0.17.0 --index-url https://download.pytorch.org/whl/cpu python -c "from datasets import load_dataset;import soundfile as sf;sample = load_dataset('distil-whisper/librispeech_long', 'clean', split='validation')[0]['audio'];sf.write('${MODEL_DIR}/$AUDIO_FILE', sample['array'][:sample['sampling_rate']*30], sample['sampling_rate'])" fi diff --git a/.ci/scripts/test_wheel_package_qnn.sh b/.ci/scripts/test_wheel_package_qnn.sh index 568c90ed14c..65e60ef21ad 100644 --- a/.ci/scripts/test_wheel_package_qnn.sh +++ b/.ci/scripts/test_wheel_package_qnn.sh @@ -166,8 +166,8 @@ PY # ) echo "=== [$LABEL] Install torch==${TORCH_VERSION} ===" - # Install torch based on the pinned PyTorch version, preferring the PyTorch test index - "$PIPBIN" install torch=="${TORCH_VERSION}" --extra-index-url "https://download.pytorch.org/whl/test" + # Install torch based on the pinned PyTorch version. + "$PIPBIN" install --no-cache-dir torch=="${TORCH_VERSION}" --index-url "https://download.pytorch.org/whl/cpu" "$PIPBIN" install wheel # Install torchao based on the pinned commit from third-party/ao submodule diff --git a/.ci/scripts/tests/test_cu134_dependencies.py b/.ci/scripts/tests/test_cu134_dependencies.py index 224d5dcafeb..83f246f07c2 100644 --- a/.ci/scripts/tests/test_cu134_dependencies.py +++ b/.ci/scripts/tests/test_cu134_dependencies.py @@ -66,9 +66,9 @@ def test_all_install_steps_preserve_exact_cu134_selection(self): commands = self.install_commands((13, 4), machine) self.assertEqual(len(commands), 4) expected = { - "torch==2.15.0.dev20260921+cu134", - "torchvision==0.30.0.dev20260922+cu134", - "torchaudio==2.11.0.dev20260922+cu134", + "torch==2.15.0+cu134", + "torchvision==0.29.2+cu134", + "torchaudio==2.11.1+cu134", f"torchao=={self.installer.CU134_TORCHAO_NIGHTLY_VERSION}+{ao_variant}", } for index, command in enumerate(commands): @@ -89,17 +89,13 @@ def test_all_install_steps_preserve_exact_cu134_selection(self): for arg in command ) ) - self.assertIn( - "https://download.pytorch.org/whl/nightly/cu134", command - ) + self.assertIn("https://download.pytorch.org/whl/cu134", command) self.assertNotIn( "https://download.pytorch.org/whl/test/cu134", command ) self.assertNotIn("--no-deps", command) if machine == "aarch64": - self.assertIn( - "https://download.pytorch.org/whl/nightly/cpu", command - ) + self.assertIn("https://download.pytorch.org/whl/cpu", command) def test_other_cuda_trains_keep_existing_pins(self): for cuda in ((12, 6), (13, 0), (13, 2)): @@ -108,12 +104,12 @@ def test_other_cuda_trains_keep_existing_pins(self): core, local, domains, examples = self.install_commands( cuda, machine ) - self.assertIn("torch==2.14.0", core) + self.assertIn("torch==2.15.0", core) self.assertIn( f"torchao=={self.installer.TORCHAO_NIGHTLY_VERSION}", core ) - self.assertIn("torchvision==0.29.0", domains) - self.assertIn("torchaudio==2.11.0", domains) + self.assertIn("torchvision==0.29.2", domains) + self.assertIn("torchaudio==2.11.1", domains) self.assertFalse(any("==" in arg for arg in local)) self.assertFalse(any("==" in arg for arg in examples)) @@ -122,22 +118,22 @@ def test_source_pinned_torch_is_not_replaced(self): with self.subTest(cuda=cuda): core, _, domains, _ = self.install_commands(cuda, nightly=False) self.assertIn("torch", core) - self.assertNotIn("torch==2.15.0.dev20260921+cu134", core) + self.assertNotIn("torch==2.15.0+cu134", core) self.assertIn("torchvision", domains) self.assertIn("torchaudio", domains) def test_no_cuda_keeps_default_pins(self): core, _, domains, _ = self.install_commands(None) - self.assertIn("torch==2.14.0", core) + self.assertIn("torch==2.15.0", core) self.assertIn(f"torchao=={self.installer.TORCHAO_NIGHTLY_VERSION}", core) - self.assertIn("torchvision==0.29.0", domains) - self.assertIn("https://download.pytorch.org/whl/test/cpu", core) + self.assertIn("torchvision==0.29.2", domains) + self.assertIn("https://download.pytorch.org/whl/cpu", core) def test_windows_does_not_select_cu134(self): core, _, domains, _ = self.install_commands((13, 4), system="Windows") - self.assertIn("torch==2.14.0", core) - self.assertIn("torchvision==0.29.0", domains) - self.assertIn("https://download.pytorch.org/whl/test/cpu", core) + self.assertIn("torch==2.15.0", core) + self.assertIn("torchvision==0.29.2", domains) + self.assertIn("https://download.pytorch.org/whl/cpu", core) def test_python315_cpu_domains_use_available_torchaudio(self): for system in ("Linux", "Darwin", "Windows"): @@ -146,9 +142,9 @@ def test_python315_cpu_domains_use_available_torchaudio(self): _, _, domains, _ = self.install_commands( None, machine="arm64", system=system, python_version=version ) - audio = "2.11.1" if version >= (3, 15) else "2.11.0" + audio = "2.11.1" self.assertIn(f"torchaudio=={audio}", domains) - self.assertIn("torchvision==0.29.0", domains) + self.assertIn("torchvision==0.29.2", domains) def test_python315_cuda_domains_use_available_torchaudio(self): for machine in ("x86_64", "aarch64"): @@ -157,8 +153,8 @@ def test_python315_cuda_domains_use_available_torchaudio(self): (13, 2), machine=machine, python_version=(3, 15) ) self.assertIn("torchaudio==2.11.1", domains) - self.assertIn("torchvision==0.29.0", domains) - self.assertIn("https://download.pytorch.org/whl/test/cu132", domains) + self.assertIn("torchvision==0.29.2", domains) + self.assertIn("https://download.pytorch.org/whl/cu132", domains) def test_python315_preserves_cu134_and_source_pins(self): for cuda in ((13, 2), (13, 4)): @@ -168,7 +164,7 @@ def test_python315_preserves_cu134_and_source_pins(self): cuda, nightly=nightly, python_version=(3, 15) ) audio = ( - "torchaudio==2.11.0.dev20260922+cu134" + "torchaudio==2.11.1+cu134" if nightly and cuda == (13, 4) else "torchaudio==2.11.1" if nightly else "torchaudio" ) @@ -305,7 +301,7 @@ def test_cu134_keeps_explicit_torchao_source_build(self): self.assertFalse( any(arg.startswith("torchao==") for arg in command) ) - self.assertIn("torch==2.15.0.dev20260921+cu134", commands[-1]) + self.assertIn("torch==2.15.0+cu134", commands[-1]) source_version = self.installer.TORCHAO_NIGHTLY_VERSION.partition( ".dev" )[0] diff --git a/.github/workflows/windows-msvc.yml b/.github/workflows/windows-msvc.yml index 269f2cf2381..22f57a3e06b 100644 --- a/.github/workflows/windows-msvc.yml +++ b/.github/workflows/windows-msvc.yml @@ -91,7 +91,7 @@ jobs: - name: Install build dependencies shell: pwsh - run: python -m pip install pyyaml torch==2.14.0 --extra-index-url https://download.pytorch.org/whl/test/cpu + run: python -m pip install pyyaml torch==2.15.0 --extra-index-url https://download.pytorch.org/whl/cpu - name: Build ExecuTorch shell: pwsh diff --git a/examples/models/moshi/mimi/install_requirements.sh b/examples/models/moshi/mimi/install_requirements.sh index 93167ead9a1..3097c939886 100755 --- a/examples/models/moshi/mimi/install_requirements.sh +++ b/examples/models/moshi/mimi/install_requirements.sh @@ -15,7 +15,7 @@ if command -v apt-get >/dev/null 2>&1; then sudo apt-get update sudo apt-get install -y --no-install-recommends ffmpeg fi -pip install torchcodec==0.11.0 --extra-index-url https://download.pytorch.org/whl/test/cpu +pip install torchcodec==0.17.0 --index-url https://download.pytorch.org/whl/cpu pip install moshi==0.2.11 pip install bitsandbytes soundfile einops # Run llama2/install requirements for torchao deps diff --git a/install_requirements.py b/install_requirements.py index 285c4bf281f..366e9ab3f28 100644 --- a/install_requirements.py +++ b/install_requirements.py @@ -13,22 +13,17 @@ from install_utils import determine_torch_url, is_intel_mac_os, python_is_compatible -# The pip repository that hosts nightly torch packages. +# The pip repository that hosts released torch packages. # This will be dynamically set based on CUDA availability and CUDA backend enabled/disabled. -TORCH_URL_BASE = "https://download.pytorch.org/whl/test" -TORCHAO_URL_BASE = "https://download.pytorch.org/whl/nightly" -TORCHAO_NIGHTLY_VERSION = "0.19.0.dev20260907" -CU134_TORCHAO_NIGHTLY_VERSION = "0.19.0.dev20260907" -# Newest rocm7.2 TorchAO nightly with compiled kernels (cp310-abi3); later ROCm nightlies -# are py3-none-any only, and none are published after 0.19.0.dev20260831. -ROCM_TORCHAO_NIGHTLY_VERSION = "0.19.0.dev20260805" -# torchvision 0.30.0.dev20260922 requires exactly torch 2.15.0.dev20260921. -# download.pytorch.org keeps about 60 days of nightlies, so this pin expires; 2026-09-22 -# is the newest date whose torch, torchvision and torchaudio still all carry cp310. +TORCH_URL_BASE = "https://download.pytorch.org/whl" +TORCHAO_URL_BASE = "https://download.pytorch.org/whl" +TORCHAO_NIGHTLY_VERSION = "0.19.0" +CU134_TORCHAO_NIGHTLY_VERSION = "0.19.0" +ROCM_TORCHAO_NIGHTLY_VERSION = "0.19.0" CU134_TORCH_PACKAGES = [ - "torch==2.15.0.dev20260921+cu134", - "torchvision==0.30.0.dev20260922+cu134", - "torchaudio==2.11.0.dev20260922+cu134", + "torch==2.15.0+cu134", + "torchvision==0.29.2+cu134", + "torchaudio==2.11.1+cu134", ] @@ -53,16 +48,7 @@ def cu134_requirements(torch_url, include_domains=False): return packages -# Since ExecuTorch often uses main-branch features of pytorch, only the nightly -# pip versions will have the required features. -# -# NOTE: If a newly-fetched version of the executorch repo changes the value of -# NIGHTLY_VERSION, you should re-run this script to install the necessary -# package versions. -# -# NOTE: If you're changing, make the corresponding change in .ci/docker/ci_commit_pins/pytorch.txt -# by picking the hash from the same date in -# https://hud.pytorch.org/hud/pytorch/pytorch/nightly/ @lint-ignore +# Keep the release packages aligned with .ci/docker/ci_commit_pins/pytorch.txt. # # NOTE: If you're changing, make the corresponding supported CUDA versions in # SUPPORTED_CUDA_VERSIONS in install_utils.py if needed. @@ -88,7 +74,7 @@ def install_requirements(use_pytorch_nightly): # torchao's CUDA channel publishes x86_64 only, so asking for a CUDA build makes the pin # unsatisfiable on aarch64. Only that case is special-cased: falling back everywhere would # change which torchao a CPU x86_64 install resolves, and the CUDA build is genuinely wanted - # where it exists. This nightly is what a development checkout is tested against, and the + # where it exists. This release is what the checkout is tested against, and the # wheel's own torchao lower bound is this same version so that installing the package # afterwards leaves this pin in place rather than replacing it. if platform.machine().lower() in ("aarch64", "arm64"): @@ -105,7 +91,7 @@ def install_requirements(use_pytorch_nightly): # Setting use_pytorch_nightly to false to test the pinned PyTorch commit. Note # that we don't need to set any version number there because they have already # been installed on CI before this step, so pip won't reinstall them - ("torch==2.14.0" if use_pytorch_nightly else "torch"), + ("torch==2.15.0" if use_pytorch_nightly else "torch"), f"torchao=={TORCHAO_NIGHTLY_VERSION}", ] @@ -206,9 +192,9 @@ def install_optional_example_requirements(use_pytorch_nightly): ) print("Installing torch domain libraries") - torchaudio_version = "2.11.1" if sys.version_info >= (3, 15) else "2.11.0" + torchaudio_version = "2.11.1" DOMAIN_LIBRARIES = cu134_packages or [ - ("torchvision==0.29.0" if use_pytorch_nightly else "torchvision"), + ("torchvision==0.29.2" if use_pytorch_nightly else "torchvision"), (f"torchaudio=={torchaudio_version}" if use_pytorch_nightly else "torchaudio"), ] # Then install domain libraries diff --git a/setup.py b/setup.py index c3d73287242..ca8edd8077a 100644 --- a/setup.py +++ b/setup.py @@ -1118,7 +1118,7 @@ def _package_relative_depth(library: Path) -> int: def _torchao_requirement() -> str: """The torchao dependency, pinned to the series install_requirements.py installs. - Derived from that module rather than written out, so a nightly bump cannot move the + Derived from that module rather than written out, so a pin bump cannot move the pin without moving this bound with it. A bump into the next series would otherwise silently stop satisfying the lower bound, and installing this package over a development checkout would replace the torchao that was just installed. @@ -1169,21 +1169,15 @@ def _base_dependencies() -> List[str]: # scope. Neither is needed merely to import the backend. "py-cpuinfo", "requests", - "pytorch-tokenizers", + "pytorch-tokenizers>=1.6.0", # Shipped code imports torchao at module scope in many places, so a plain install cannot # lower a model without it. Among others: the XNNPACK utilities the partitioner uses # (backends/xnnpack/utils/utils.py), the Core ML quantizer, and executorch.export itself. # The MLX backend needs it too, though indirectly: it registers a torchao operator that # only exists once torchao has been imported. # - # The lower bound is the nightly install_requirements.py pins, so that installing this - # package over a development checkout leaves that pin in place. A bound at the stable - # release instead would evict it, because a dev release sorts below its own final. - # - # The upper bound is what makes naming a pre-release safe. A specifier that names one - # accepts pre-releases for this requirement, so without the bound pip would resolve a - # pre-release of the next series wherever one is published, such as an index carrying - # nightlies. Keep both bounds in the same series. + # Keep dependency bounds in the release series used by the installer. + "torch>=2.15.0,<2.16", _torchao_requirement(), "pyyaml", "ruamel.yaml", diff --git a/torch_pin.py b/torch_pin.py index 5737d105625..6e8b8a81e82 100644 --- a/torch_pin.py +++ b/torch_pin.py @@ -1,2 +1 @@ -TORCH_VERSION = "2.14.0" -NIGHTLY_VERSION = "dev20261004" +TORCH_VERSION = "2.15.0" From 65434385f720a5ecc7951b538621bfe14ba1aa01 Mon Sep 17 00:00:00 2001 From: Jacob Szwejbka Date: Wed, 28 Oct 2026 14:07:28 -0700 Subject: [PATCH 02/12] [RELEASE ONLY CHANGES] Retain existing TorchAO nightly pins for 1.6 --- .ci/scripts/test-rocm-aoti.sh | 2 +- .ci/scripts/test-rocm-voxtral.sh | 2 +- .ci/scripts/tests/test_cu134_dependencies.py | 6 ++-- install_requirements.py | 29 +++++++++++--------- setup.py | 13 +++++++-- 5 files changed, 32 insertions(+), 20 deletions(-) diff --git a/.ci/scripts/test-rocm-aoti.sh b/.ci/scripts/test-rocm-aoti.sh index 03b123dd4be..bfdf6caca6e 100644 --- a/.ci/scripts/test-rocm-aoti.sh +++ b/.ci/scripts/test-rocm-aoti.sh @@ -10,7 +10,7 @@ set -euo pipefail ROCM_VERSION="${ROCM_VERSION:-7.2}" ROCM_PATH="${ROCM_PATH:-/opt/rocm}" PYTORCH_ROCM_INDEX="${PYTORCH_ROCM_INDEX:-https://download.pytorch.org/whl/rocm${ROCM_VERSION}}" -TORCHAO_ROCM_WHEEL_BASE="${TORCHAO_ROCM_WHEEL_BASE:-https://download.pytorch.org/whl/rocm${ROCM_VERSION}}" +TORCHAO_ROCM_WHEEL_BASE="${TORCHAO_ROCM_WHEEL_BASE:-https://download.pytorch.org/whl/nightly/rocm${ROCM_VERSION}}" ROCM_CI_TMP_ROOT="${RUNNER_TEMP:-/tmp}" mkdir -p "${ROCM_CI_TMP_ROOT}" 2>/dev/null || ROCM_CI_TMP_ROOT=/tmp ROCM_CI_TMPDIR="$(mktemp -d "${ROCM_CI_TMP_ROOT}/executorch-rocm-ci.XXXXXX")" diff --git a/.ci/scripts/test-rocm-voxtral.sh b/.ci/scripts/test-rocm-voxtral.sh index 5f5da762746..41be38226d7 100644 --- a/.ci/scripts/test-rocm-voxtral.sh +++ b/.ci/scripts/test-rocm-voxtral.sh @@ -12,7 +12,7 @@ ROCM_PATH="${ROCM_PATH:-/opt/rocm}" EXPECTED_ROCM_ARCH="${EXPECTED_ROCM_ARCH:-gfx950}" EXPECTED_WARP_SIZE="${EXPECTED_WARP_SIZE:-64}" PYTORCH_ROCM_INDEX="${PYTORCH_ROCM_INDEX:-https://download.pytorch.org/whl/rocm${ROCM_VERSION}}" -TORCHAO_ROCM_WHEEL_BASE="${TORCHAO_ROCM_WHEEL_BASE:-https://download.pytorch.org/whl/rocm${ROCM_VERSION}}" +TORCHAO_ROCM_WHEEL_BASE="${TORCHAO_ROCM_WHEEL_BASE:-https://download.pytorch.org/whl/nightly/rocm${ROCM_VERSION}}" VOXTRAL_CI_TMP_ROOT="${RUNNER_TEMP:-/tmp}" if ! mkdir -p "${VOXTRAL_CI_TMP_ROOT}" 2>/dev/null || [[ ! -w "${VOXTRAL_CI_TMP_ROOT}" ]]; then diff --git a/.ci/scripts/tests/test_cu134_dependencies.py b/.ci/scripts/tests/test_cu134_dependencies.py index 83f246f07c2..0dd5aff9f92 100644 --- a/.ci/scripts/tests/test_cu134_dependencies.py +++ b/.ci/scripts/tests/test_cu134_dependencies.py @@ -90,12 +90,14 @@ def test_all_install_steps_preserve_exact_cu134_selection(self): ) ) self.assertIn("https://download.pytorch.org/whl/cu134", command) + self.assertIn( + f"https://download.pytorch.org/whl/nightly/{ao_variant}", + command, + ) self.assertNotIn( "https://download.pytorch.org/whl/test/cu134", command ) self.assertNotIn("--no-deps", command) - if machine == "aarch64": - self.assertIn("https://download.pytorch.org/whl/cpu", command) def test_other_cuda_trains_keep_existing_pins(self): for cuda in ((12, 6), (13, 0), (13, 2)): diff --git a/install_requirements.py b/install_requirements.py index 366e9ab3f28..1d8570f9f46 100644 --- a/install_requirements.py +++ b/install_requirements.py @@ -16,10 +16,12 @@ # The pip repository that hosts released torch packages. # This will be dynamically set based on CUDA availability and CUDA backend enabled/disabled. TORCH_URL_BASE = "https://download.pytorch.org/whl" -TORCHAO_URL_BASE = "https://download.pytorch.org/whl" -TORCHAO_NIGHTLY_VERSION = "0.19.0" -CU134_TORCHAO_NIGHTLY_VERSION = "0.19.0" -ROCM_TORCHAO_NIGHTLY_VERSION = "0.19.0" +TORCHAO_URL_BASE = "https://download.pytorch.org/whl/nightly" +TORCHAO_NIGHTLY_VERSION = "0.19.0.dev20260907" +CU134_TORCHAO_NIGHTLY_VERSION = "0.19.0.dev20260907" +# Newest rocm7.2 TorchAO nightly with compiled kernels (cp310-abi3); later ROCm nightlies +# are py3-none-any only, and none are published after 0.19.0.dev20260831. +ROCM_TORCHAO_NIGHTLY_VERSION = "0.19.0.dev20260805" CU134_TORCH_PACKAGES = [ "torch==2.15.0+cu134", "torchvision==0.29.2+cu134", @@ -67,14 +69,12 @@ def install_requirements(use_pytorch_nightly): # Determine the appropriate PyTorch URL based on CUDA delegate status torch_url = determine_torch_url(TORCH_URL_BASE) cu134_packages = cu134_requirements(torch_url) - if cu134_packages: - torch_url = determine_torch_url(TORCHAO_URL_BASE) - if not use_pytorch_nightly: - cu134_packages[0] = "torch" + if cu134_packages and not use_pytorch_nightly: + cu134_packages[0] = "torch" # torchao's CUDA channel publishes x86_64 only, so asking for a CUDA build makes the pin # unsatisfiable on aarch64. Only that case is special-cased: falling back everywhere would # change which torchao a CPU x86_64 install resolves, and the CUDA build is genuinely wanted - # where it exists. This release is what the checkout is tested against, and the + # where it exists. This nightly is what a development checkout is tested against, and the # wheel's own torchao lower bound is this same version so that installing the package # afterwards leaves this pin in place rather than replacing it. if platform.machine().lower() in ("aarch64", "arm64"): @@ -183,11 +183,14 @@ def install_optional_example_requirements(use_pytorch_nightly): if use_pytorch_nightly else [] ) - if cu134_packages: - torch_url = determine_torch_url(TORCHAO_URL_BASE) + torchao_url = ( + f"{TORCHAO_URL_BASE}/cpu" + if platform.machine().lower() in ("aarch64", "arm64") + else determine_torch_url(TORCHAO_URL_BASE) + ) torchao_index = ( - ["--extra-index-url", f"{TORCHAO_URL_BASE}/cpu"] - if cu134_packages and platform.machine().lower() in ("aarch64", "arm64") + ["--extra-index-url", torchao_url] + if cu134_packages and not torchao_from_source() else [] ) diff --git a/setup.py b/setup.py index ca8edd8077a..95af786ad82 100644 --- a/setup.py +++ b/setup.py @@ -1118,7 +1118,7 @@ def _package_relative_depth(library: Path) -> int: def _torchao_requirement() -> str: """The torchao dependency, pinned to the series install_requirements.py installs. - Derived from that module rather than written out, so a pin bump cannot move the + Derived from that module rather than written out, so a nightly bump cannot move the pin without moving this bound with it. A bump into the next series would otherwise silently stop satisfying the lower bound, and installing this package over a development checkout would replace the torchao that was just installed. @@ -1170,14 +1170,21 @@ def _base_dependencies() -> List[str]: "py-cpuinfo", "requests", "pytorch-tokenizers>=1.6.0", + "torch>=2.15.0,<2.16", # Shipped code imports torchao at module scope in many places, so a plain install cannot # lower a model without it. Among others: the XNNPACK utilities the partitioner uses # (backends/xnnpack/utils/utils.py), the Core ML quantizer, and executorch.export itself. # The MLX backend needs it too, though indirectly: it registers a torchao operator that # only exists once torchao has been imported. # - # Keep dependency bounds in the release series used by the installer. - "torch>=2.15.0,<2.16", + # The lower bound is the nightly install_requirements.py pins, so that installing this + # package over a development checkout leaves that pin in place. A bound at the stable + # release instead would evict it, because a dev release sorts below its own final. + # + # The upper bound is what makes naming a pre-release safe. A specifier that names one + # accepts pre-releases for this requirement, so without the bound pip would resolve a + # pre-release of the next series wherever one is published, such as an index carrying + # nightlies. Keep both bounds in the same series. _torchao_requirement(), "pyyaml", "ruamel.yaml", From 51543064532345478b5b9b6b66f7f7d891481e23 Mon Sep 17 00:00:00 2001 From: Jacob Szwejbka Date: Thu, 29 Oct 2026 19:59:21 -0700 Subject: [PATCH 03/12] [RELEASE ONLY CHANGES] Use a minimum TorchAO requirement for 1.6 --- .ci/scripts/tests/test_cu134_dependencies.py | 23 ++++++++++---------- install_requirements.py | 22 ++++++------------- setup.py | 10 ++------- 3 files changed, 21 insertions(+), 34 deletions(-) diff --git a/.ci/scripts/tests/test_cu134_dependencies.py b/.ci/scripts/tests/test_cu134_dependencies.py index 0dd5aff9f92..3f5da1f882c 100644 --- a/.ci/scripts/tests/test_cu134_dependencies.py +++ b/.ci/scripts/tests/test_cu134_dependencies.py @@ -69,7 +69,7 @@ def test_all_install_steps_preserve_exact_cu134_selection(self): "torch==2.15.0+cu134", "torchvision==0.29.2+cu134", "torchaudio==2.11.1+cu134", - f"torchao=={self.installer.CU134_TORCHAO_NIGHTLY_VERSION}+{ao_variant}", + f"torchao>={self.installer.CU134_TORCHAO_NIGHTLY_VERSION}", } for index, command in enumerate(commands): required = ( @@ -78,7 +78,7 @@ def test_all_install_steps_preserve_exact_cu134_selection(self): else { requirement for requirement in expected - if requirement.startswith(("torch==", "torchao==")) + if requirement.startswith(("torch==", "torchao>=")) } ) self.assertTrue(required.issubset(command), command) @@ -108,7 +108,7 @@ def test_other_cuda_trains_keep_existing_pins(self): ) self.assertIn("torch==2.15.0", core) self.assertIn( - f"torchao=={self.installer.TORCHAO_NIGHTLY_VERSION}", core + f"torchao>={self.installer.TORCHAO_NIGHTLY_VERSION}", core ) self.assertIn("torchvision==0.29.2", domains) self.assertIn("torchaudio==2.11.1", domains) @@ -127,7 +127,7 @@ def test_source_pinned_torch_is_not_replaced(self): def test_no_cuda_keeps_default_pins(self): core, _, domains, _ = self.install_commands(None) self.assertIn("torch==2.15.0", core) - self.assertIn(f"torchao=={self.installer.TORCHAO_NIGHTLY_VERSION}", core) + self.assertIn(f"torchao>={self.installer.TORCHAO_NIGHTLY_VERSION}", core) self.assertIn("torchvision==0.29.2", domains) self.assertIn("https://download.pytorch.org/whl/cpu", core) @@ -263,7 +263,7 @@ def test_package_install_preserves_source_pinned_torchao(self): self.assertIn(".", commands[-1]) core = commands[0] torchao = Requirement( - next(arg for arg in core if arg.startswith("torchao==")) + next(arg for arg in core if arg.startswith("torchao>=")) ) version = next(iter(torchao.specifier)).version self.assertIn(version, metadata.specifier) @@ -300,9 +300,7 @@ def test_cu134_keeps_explicit_torchao_source_build(self): self.assertIn("third-party/ao", commands[1]) self.assertIn(".", commands[2]) for command in commands: - self.assertFalse( - any(arg.startswith("torchao==") for arg in command) - ) + self.assertFalse(any(arg.startswith("torchao") for arg in command)) self.assertIn("torch==2.15.0+cu134", commands[-1]) source_version = self.installer.TORCHAO_NIGHTLY_VERSION.partition( ".dev" @@ -318,15 +316,14 @@ def test_cu134_keeps_explicit_torchao_source_build(self): f"{source_version}+git{source_commit[:7]}", metadata.specifier ) - def test_wheel_torchao_bound_matches_selected_train(self): + def test_wheel_torchao_minimum_matches_installer(self): for cuda in ((13, 4), (13, 2), None): version = ( self.installer.CU134_TORCHAO_NIGHTLY_VERSION if cuda == (13, 4) else self.installer.TORCHAO_NIGHTLY_VERSION ) - major, minor = (int(part) for part in version.split(".")[:2]) - expected = f"torchao>={version},<{major}.{minor + 1}" + expected = f"torchao>={version}" self.utils.determine_torch_url.cache_clear() with ( patch.object( @@ -338,6 +335,10 @@ def test_wheel_torchao_bound_matches_selected_train(self): patch.object(self.installer.platform, "system", return_value="Linux"), ): self.assertEqual(self.torchao_requirement(), expected) + requirement = Requirement(self.torchao_requirement()) + self.assertNotIn("0.18.0", requirement.specifier) + for newer in ("0.19.0.dev20260908", "0.19.0", "0.20.0", "1.0.0"): + self.assertIn(newer, requirement.specifier) if __name__ == "__main__": diff --git a/install_requirements.py b/install_requirements.py index 1d8570f9f46..123f971edb8 100644 --- a/install_requirements.py +++ b/install_requirements.py @@ -43,10 +43,7 @@ def cu134_requirements(torch_url, include_domains=False): CU134_TORCH_PACKAGES if include_domains else CU134_TORCH_PACKAGES[:1] ) if not torchao_from_source(): - torchao_variant = ( - "cpu" if platform.machine().lower() in ("aarch64", "arm64") else "cu134" - ) - packages.append(f"torchao=={CU134_TORCHAO_NIGHTLY_VERSION}+{torchao_variant}") + packages.append(f"torchao>={CU134_TORCHAO_NIGHTLY_VERSION}") return packages @@ -71,17 +68,12 @@ def install_requirements(use_pytorch_nightly): cu134_packages = cu134_requirements(torch_url) if cu134_packages and not use_pytorch_nightly: cu134_packages[0] = "torch" - # torchao's CUDA channel publishes x86_64 only, so asking for a CUDA build makes the pin - # unsatisfiable on aarch64. Only that case is special-cased: falling back everywhere would - # change which torchao a CPU x86_64 install resolves, and the CUDA build is genuinely wanted - # where it exists. This nightly is what a development checkout is tested against, and the - # wheel's own torchao lower bound is this same version so that installing the package - # afterwards leaves this pin in place rather than replacing it. + # TorchAO's CUDA channel publishes x86_64 only, so use the CPU channel on + # aarch64. The installer and wheel metadata share a minimum version, allowing + # newer TorchAO packages without downgrading a preinstalled compatible build. if platform.machine().lower() in ("aarch64", "arm64"): - # The cpu channel specifically, not the index root. The root carries every variant, and a - # pin without a local segment admits all of them while ordering a local segment highest, - # so the xpu channel's pure python wheel would win on version before pip compares wheel - # tags, silently replacing the compiled aarch64 build. + # The index root carries every variant; a local segment sorts above the + # base version, so an XPU wheel could replace the compiled aarch64 build. torchao_url = f"{TORCHAO_URL_BASE}/cpu" else: torchao_url = determine_torch_url(TORCHAO_URL_BASE) @@ -92,7 +84,7 @@ def install_requirements(use_pytorch_nightly): # that we don't need to set any version number there because they have already # been installed on CI before this step, so pip won't reinstall them ("torch==2.15.0" if use_pytorch_nightly else "torch"), - f"torchao=={TORCHAO_NIGHTLY_VERSION}", + f"torchao>={TORCHAO_NIGHTLY_VERSION}", ] # Install the requirements for core ExecuTorch package. diff --git a/setup.py b/setup.py index 95af786ad82..076b2324bb3 100644 --- a/setup.py +++ b/setup.py @@ -1116,12 +1116,7 @@ def _package_relative_depth(library: Path) -> int: def _torchao_requirement() -> str: - """The torchao dependency, pinned to the series install_requirements.py installs. - - Derived from that module rather than written out, so a nightly bump cannot move the - pin without moving this bound with it. A bump into the next series would otherwise - silently stop satisfying the lower bound, and installing this package over a - development checkout would replace the torchao that was just installed. + """The TorchAO minimum version, shared with install_requirements.py. Loaded by path, the way install_utils is above, because setuptools executes this file without the project directory on sys.path, so a plain import does not resolve. @@ -1142,8 +1137,7 @@ def _torchao_requirement() -> str: and not module.torchao_from_source() ): version = module.CU134_TORCHAO_NIGHTLY_VERSION - major, minor = (int(part) for part in version.split(".")[:2]) - return f"torchao>={version},<{major}.{minor + 1}" + return f"torchao>={version}" def _base_dependencies() -> List[str]: From 4f266e66b08a2dd9289a77c6f168f227e9101b22 Mon Sep 17 00:00:00 2001 From: Jacob Szwejbka Date: Thu, 29 Oct 2026 20:13:24 -0700 Subject: [PATCH 04/12] [RELEASE ONLY CHANGES] Pin latest torch and use released TorchAO minimum --- .ci/docker/ci_commit_pins/pytorch.txt | 2 +- .ci/scripts/test-rocm-voxtral.sh | 3 +- .ci/scripts/tests/test_cu134_dependencies.py | 30 +++++++++++--------- install_requirements.py | 7 ++--- pyproject.toml | 2 ++ setup.py | 23 ++++----------- 6 files changed, 29 insertions(+), 38 deletions(-) diff --git a/.ci/docker/ci_commit_pins/pytorch.txt b/.ci/docker/ci_commit_pins/pytorch.txt index a6def14a90f..1067b14354d 100644 --- a/.ci/docker/ci_commit_pins/pytorch.txt +++ b/.ci/docker/ci_commit_pins/pytorch.txt @@ -1 +1 @@ -30c000d9a4b266157c9515acd195083898a00787 +31a77fc9c0a7c6fff15a1ff58c743f53b1a9d457 diff --git a/.ci/scripts/test-rocm-voxtral.sh b/.ci/scripts/test-rocm-voxtral.sh index 41be38226d7..7db447e1063 100644 --- a/.ci/scripts/test-rocm-voxtral.sh +++ b/.ci/scripts/test-rocm-voxtral.sh @@ -48,8 +48,7 @@ python -m pip install "torch==${TORCH_VERSION}" \ --index-url "${PYTORCH_ROCM_INDEX}" python -m pip install -r requirements-dev.txt \ "${TORCHAO_WHEEL}" -# ExecuTorch requires torchao>=TORCHAO_NIGHTLY_VERSION, which has no ROCm build, so -# install its other dependencies separately to keep the ROCm TorchAO wheel in place. +# Install the other dependencies separately to keep the compiled ROCm TorchAO wheel in place. python -m pip install --editable . --no-build-isolation --no-deps python - <<'PY' >"${VOXTRAL_CI_TMPDIR}/executorch-deps.txt" from importlib.metadata import requires diff --git a/.ci/scripts/tests/test_cu134_dependencies.py b/.ci/scripts/tests/test_cu134_dependencies.py index 3f5da1f882c..f9d07b50525 100644 --- a/.ci/scripts/tests/test_cu134_dependencies.py +++ b/.ci/scripts/tests/test_cu134_dependencies.py @@ -69,7 +69,7 @@ def test_all_install_steps_preserve_exact_cu134_selection(self): "torch==2.15.0+cu134", "torchvision==0.29.2+cu134", "torchaudio==2.11.1+cu134", - f"torchao>={self.installer.CU134_TORCHAO_NIGHTLY_VERSION}", + f"torchao>={self.installer.TORCHAO_MIN_VERSION}", } for index, command in enumerate(commands): required = ( @@ -108,7 +108,7 @@ def test_other_cuda_trains_keep_existing_pins(self): ) self.assertIn("torch==2.15.0", core) self.assertIn( - f"torchao>={self.installer.TORCHAO_NIGHTLY_VERSION}", core + f"torchao>={self.installer.TORCHAO_MIN_VERSION}", core ) self.assertIn("torchvision==0.29.2", domains) self.assertIn("torchaudio==2.11.1", domains) @@ -127,7 +127,7 @@ def test_source_pinned_torch_is_not_replaced(self): def test_no_cuda_keeps_default_pins(self): core, _, domains, _ = self.install_commands(None) self.assertIn("torch==2.15.0", core) - self.assertIn(f"torchao>={self.installer.TORCHAO_NIGHTLY_VERSION}", core) + self.assertIn(f"torchao>={self.installer.TORCHAO_MIN_VERSION}", core) self.assertIn("torchvision==0.29.2", domains) self.assertIn("https://download.pytorch.org/whl/cpu", core) @@ -302,9 +302,7 @@ def test_cu134_keeps_explicit_torchao_source_build(self): for command in commands: self.assertFalse(any(arg.startswith("torchao") for arg in command)) self.assertIn("torch==2.15.0+cu134", commands[-1]) - source_version = self.installer.TORCHAO_NIGHTLY_VERSION.partition( - ".dev" - )[0] + source_version = self.installer.TORCHAO_MIN_VERSION source_commit = subprocess.run( ["git", "rev-parse", "HEAD:third-party/ao"], cwd=ROOT, @@ -318,11 +316,7 @@ def test_cu134_keeps_explicit_torchao_source_build(self): def test_wheel_torchao_minimum_matches_installer(self): for cuda in ((13, 4), (13, 2), None): - version = ( - self.installer.CU134_TORCHAO_NIGHTLY_VERSION - if cuda == (13, 4) - else self.installer.TORCHAO_NIGHTLY_VERSION - ) + version = self.installer.TORCHAO_MIN_VERSION expected = f"torchao>={version}" self.utils.determine_torch_url.cache_clear() with ( @@ -336,9 +330,17 @@ def test_wheel_torchao_minimum_matches_installer(self): ): self.assertEqual(self.torchao_requirement(), expected) requirement = Requirement(self.torchao_requirement()) - self.assertNotIn("0.18.0", requirement.specifier) - for newer in ("0.19.0.dev20260908", "0.19.0", "0.20.0", "1.0.0"): - self.assertIn(newer, requirement.specifier) + self.assertNotIn("0.17.0", requirement.specifier) + for newer in ( + "0.18.0", + "0.19.0.dev20260908", + "0.19.0", + "0.20.0", + "1.0.0", + ): + self.assertTrue( + requirement.specifier.contains(newer, prereleases=True) + ) if __name__ == "__main__": diff --git a/install_requirements.py b/install_requirements.py index 123f971edb8..dc900c37aa5 100644 --- a/install_requirements.py +++ b/install_requirements.py @@ -17,8 +17,7 @@ # This will be dynamically set based on CUDA availability and CUDA backend enabled/disabled. TORCH_URL_BASE = "https://download.pytorch.org/whl" TORCHAO_URL_BASE = "https://download.pytorch.org/whl/nightly" -TORCHAO_NIGHTLY_VERSION = "0.19.0.dev20260907" -CU134_TORCHAO_NIGHTLY_VERSION = "0.19.0.dev20260907" +TORCHAO_MIN_VERSION = "0.18.0" # Newest rocm7.2 TorchAO nightly with compiled kernels (cp310-abi3); later ROCm nightlies # are py3-none-any only, and none are published after 0.19.0.dev20260831. ROCM_TORCHAO_NIGHTLY_VERSION = "0.19.0.dev20260805" @@ -43,7 +42,7 @@ def cu134_requirements(torch_url, include_domains=False): CU134_TORCH_PACKAGES if include_domains else CU134_TORCH_PACKAGES[:1] ) if not torchao_from_source(): - packages.append(f"torchao>={CU134_TORCHAO_NIGHTLY_VERSION}") + packages.append(f"torchao>={TORCHAO_MIN_VERSION}") return packages @@ -84,7 +83,7 @@ def install_requirements(use_pytorch_nightly): # that we don't need to set any version number there because they have already # been installed on CI before this step, so pip won't reinstall them ("torch==2.15.0" if use_pytorch_nightly else "torch"), - f"torchao>={TORCHAO_NIGHTLY_VERSION}", + f"torchao>={TORCHAO_MIN_VERSION}", ] # Install the requirements for core ExecuTorch package. diff --git a/pyproject.toml b/pyproject.toml index 7b865171146..c52200da925 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,6 +63,8 @@ requires-python = ">=3.11,<3.16" # computed in setup.py, so the EXECUTORCH_BUILD_MINIMAL wheel can ship a slimmer # set than the full wheel. See `_base_dependencies()` / `_minimal_dependencies()` # in setup.py. +# The 1.6 full wheel pins torch==2.15.0 and requires pytorch-tokenizers>=1.6.0 +# and torchao>=0.18.0. [project.optional-dependencies] vgf = [ diff --git a/setup.py b/setup.py index 076b2324bb3..ba58168290a 100644 --- a/setup.py +++ b/setup.py @@ -1131,13 +1131,7 @@ def _torchao_requirement() -> str: sys.modules.setdefault("install_utils", install_utils) spec.loader.exec_module(module) - version = module.TORCHAO_NIGHTLY_VERSION - if ( - install_utils.determine_torch_url(module.TORCH_URL_BASE).endswith("/cu134") - and not module.torchao_from_source() - ): - version = module.CU134_TORCHAO_NIGHTLY_VERSION - return f"torchao>={version}" + return f"torchao>={module.TORCHAO_MIN_VERSION}" def _base_dependencies() -> List[str]: @@ -1164,21 +1158,15 @@ def _base_dependencies() -> List[str]: "py-cpuinfo", "requests", "pytorch-tokenizers>=1.6.0", - "torch>=2.15.0,<2.16", + "torch==2.15.0", # Shipped code imports torchao at module scope in many places, so a plain install cannot # lower a model without it. Among others: the XNNPACK utilities the partitioner uses # (backends/xnnpack/utils/utils.py), the Core ML quantizer, and executorch.export itself. # The MLX backend needs it too, though indirectly: it registers a torchao operator that # only exists once torchao has been imported. # - # The lower bound is the nightly install_requirements.py pins, so that installing this - # package over a development checkout leaves that pin in place. A bound at the stable - # release instead would evict it, because a dev release sorts below its own final. - # - # The upper bound is what makes naming a pre-release safe. A specifier that names one - # accepts pre-releases for this requirement, so without the bound pip would resolve a - # pre-release of the next series wherever one is published, such as an index carrying - # nightlies. Keep both bounds in the same series. + # Use the installer's minimum so a compatible preinstalled TorchAO build + # satisfies the wheel metadata, including newer nightly and release versions. _torchao_requirement(), "pyyaml", "ruamel.yaml", @@ -1222,7 +1210,8 @@ def _minimal_dependencies() -> List[str]: Derived as the subset of _base_dependencies() that executorch.exir needs to lower and serialize a .pte, so version pins and markers stay in sync with the - full set. torch is intentionally absent from both (consumers bring their own). + full set. torch is intentionally absent from the minimal wheel (consumers + bring their own). mpmath is intentionally dropped too: it is pulled transitively by sympy, whose "mpmath<1.4" cap resolves to the same 1.3.0 the full wheel pins. Keep the name set below in sync with the `expected` set in .ci/scripts/test_minimal_wheel.sh. From bb943faa517f75f58300635e16f67c11ade52b3e Mon Sep 17 00:00:00 2001 From: Jacob Szwejbka Date: Thu, 29 Oct 2026 20:16:09 -0700 Subject: [PATCH 05/12] [RELEASE ONLY CHANGES] Leave the tokenizer dependency unpinned --- pyproject.toml | 3 +-- setup.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c52200da925..7560cd8aef4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,8 +63,7 @@ requires-python = ">=3.11,<3.16" # computed in setup.py, so the EXECUTORCH_BUILD_MINIMAL wheel can ship a slimmer # set than the full wheel. See `_base_dependencies()` / `_minimal_dependencies()` # in setup.py. -# The 1.6 full wheel pins torch==2.15.0 and requires pytorch-tokenizers>=1.6.0 -# and torchao>=0.18.0. +# The 1.6 full wheel pins torch==2.15.0 and requires torchao>=0.18.0. [project.optional-dependencies] vgf = [ diff --git a/setup.py b/setup.py index ba58168290a..c6a30960e33 100644 --- a/setup.py +++ b/setup.py @@ -1157,7 +1157,7 @@ def _base_dependencies() -> List[str]: # scope. Neither is needed merely to import the backend. "py-cpuinfo", "requests", - "pytorch-tokenizers>=1.6.0", + "pytorch-tokenizers", "torch==2.15.0", # Shipped code imports torchao at module scope in many places, so a plain install cannot # lower a model without it. Among others: the XNNPACK utilities the partitioner uses From 3247b8adf7fcbd7b29840782119cd52dd9303b61 Mon Sep 17 00:00:00 2001 From: Jacob Szwejbka Date: Thu, 29 Oct 2026 20:57:44 -0700 Subject: [PATCH 06/12] [RELEASE ONLY CHANGES] Preserve source-pinned torch in CI installs Respect --use-pt-pinned-commit when generating the installed wheel metadata so pip keeps the torch build used to link our extensions. Ordinary release wheel metadata remains pinned to torch 2.15.0. Assisted by Codex. --- .ci/scripts/tests/test_cu134_dependencies.py | 77 ++++++++++++++++++++ install_executorch.py | 6 +- setup.py | 8 +- 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/.ci/scripts/tests/test_cu134_dependencies.py b/.ci/scripts/tests/test_cu134_dependencies.py index f9d07b50525..cd832e20482 100644 --- a/.ci/scripts/tests/test_cu134_dependencies.py +++ b/.ci/scripts/tests/test_cu134_dependencies.py @@ -5,12 +5,15 @@ # LICENSE file in the root directory of this source tree. import ast +import importlib.metadata import importlib.util import os +import re import subprocess import sys import unittest from pathlib import Path +from typing import List from unittest.mock import patch from packaging.requirements import Requirement @@ -270,6 +273,80 @@ def test_package_install_preserves_source_pinned_torchao(self): self.assertIn("torch", core) self.assertFalse(any(arg.startswith("torch==") for arg in core)) + def test_package_metadata_preserves_source_torch_only_when_requested(self): + with patch.dict(sys.modules, {"install_requirements": self.installer}): + package_installer = load_module("install_executorch") + tree = ast.parse((ROOT / "setup.py").read_text()) + functions = [ + node + for node in tree.body + if isinstance(node, ast.FunctionDef) + and node.name in {"_base_dependencies", "_minimal_dependencies"} + ] + namespace = { + "List": List, + "os": os, + "re": re, + "importlib": importlib, + "_torchao_requirement": self.torchao_requirement, + } + exec( + compile(ast.Module(body=functions, type_ignores=[]), "setup.py", "exec"), + namespace, + ) + source_version = "2.15.0a0+git31a77fc" + for pinned in (False, True): + with self.subTest(pinned=pinned): + argv = ["install_executorch", "--minimal"] + if pinned: + argv.append("--use-pt-pinned-commit") + with ( + patch.dict( + os.environ, + {"_EXECUTORCH_USE_PT_PINNED_COMMIT": "1"}, + clear=True, + ), + patch.object(sys, "argv", argv), + patch.object( + package_installer, "python_is_compatible", return_value=True + ), + patch.object(package_installer, "check_and_update_submodules"), + patch.object(package_installer, "install_requirements"), + patch.object(package_installer.subprocess, "run") as run, + ): + package_installer.main([]) + package_env = run.call_args.kwargs["env"] + self.assertEqual( + os.environ["_EXECUTORCH_USE_PT_PINNED_COMMIT"], "1" + ) + self.assertEqual( + package_env["_EXECUTORCH_USE_PT_PINNED_COMMIT"], + "1" if pinned else "0", + ) + with ( + patch.dict(os.environ, package_env, clear=True), + patch.object( + importlib.metadata, "version", return_value=source_version + ), + ): + deps = namespace["_base_dependencies"]() + torch = Requirement( + next(dep for dep in deps if dep.startswith("torch==")) + ) + self.assertEqual(source_version in torch.specifier, pinned) + self.assertEqual("2.15.0" in torch.specifier, not pinned) + self.assertEqual( + f"torchao>={self.installer.TORCHAO_MIN_VERSION}", + next(dep for dep in deps if dep.startswith("torchao>=")), + ) + self.assertIn("pytorch-tokenizers", deps) + self.assertFalse( + any( + dep.startswith("torch") + for dep in namespace["_minimal_dependencies"]() + ) + ) + def test_cu134_keeps_explicit_torchao_source_build(self): with patch.dict(sys.modules, {"install_requirements": self.installer}): package_installer = load_module("install_executorch") diff --git a/install_executorch.py b/install_executorch.py index 276841364e6..f11f65b9d17 100644 --- a/install_executorch.py +++ b/install_executorch.py @@ -251,7 +251,11 @@ def main(args): ) if args.verbose: cmd.append("-v") - subprocess.run(cmd, check=True) + package_env = os.environ.copy() + package_env["_EXECUTORCH_USE_PT_PINNED_COMMIT"] = ( + "1" if args.use_pt_pinned_commit else "0" + ) + subprocess.run(cmd, check=True, env=package_env) # Step 3: Extra (optional) packages that is only useful for running examples. if not args.minimal: diff --git a/setup.py b/setup.py index c6a30960e33..9a348718f2e 100644 --- a/setup.py +++ b/setup.py @@ -53,6 +53,7 @@ # Import this before distutils so that setuptools can intercept the distuils # imports. +import importlib.metadata import importlib.util import logging import os @@ -1141,6 +1142,11 @@ def _base_dependencies() -> List[str]: dynamic) so the minimal build can ship a slimmer set. Keep in sync with the project's runtime needs. """ + torch_requirement = "torch==2.15.0" + if os.environ.get("_EXECUTORCH_USE_PT_PINNED_COMMIT") == "1": + # CI source builds carry an a0+git version even at the release tag. + # Preserve the build that our extension libraries link against. + torch_requirement = f"torch=={importlib.metadata.version('torch')}" return [ "expecttest", "flatbuffers", @@ -1158,7 +1164,7 @@ def _base_dependencies() -> List[str]: "py-cpuinfo", "requests", "pytorch-tokenizers", - "torch==2.15.0", + torch_requirement, # Shipped code imports torchao at module scope in many places, so a plain install cannot # lower a model without it. Among others: the XNNPACK utilities the partitioner uses # (backends/xnnpack/utils/utils.py), the Core ML quantizer, and executorch.export itself. From dbbc82fb6c74cbd0ae44a662438ee81e034cdc7b Mon Sep 17 00:00:00 2001 From: Jacob Szwejbka Date: Thu, 29 Oct 2026 21:27:09 -0700 Subject: [PATCH 07/12] [RELEASE ONLY CHANGES] Complete GPU dependency setup Honor explicitly disabled CUDA builds when selecting torch, align Windows export toolkits with the released CUDA 13.2 packages, and include the pinned TorchAO CPU extension sources for ROCm. Assisted by Codex. --- .ci/docker/build.sh | 2 +- .../install_cuda_windows_cross_compile.sh | 1 + .ci/scripts/install-rocm-dependencies.sh | 2 +- .ci/scripts/tests/test_cu134_dependencies.py | 34 ++++++++++++++++++- .ci/scripts/tests/test_rocm_dependencies.py | 4 +-- .github/workflows/cuda-windows.yml | 5 +-- .github/workflows/vulkan.yml | 4 +-- install_utils.py | 6 ++++ 8 files changed, 47 insertions(+), 11 deletions(-) diff --git a/.ci/docker/build.sh b/.ci/docker/build.sh index ef963961787..be96ae7eba4 100755 --- a/.ci/docker/build.sh +++ b/.ci/docker/build.sh @@ -88,7 +88,7 @@ case "${IMAGE_NAME}" in LINTRUNNER="" GCC_VERSION=11 CUDA_WINDOWS_CROSS_COMPILE=yes - CUDA_VERSION=13.0 + CUDA_VERSION=13.2 SKIP_PYTORCH=yes ;; executorch-ubuntu-24.04-gcc14) diff --git a/.ci/docker/common/install_cuda_windows_cross_compile.sh b/.ci/docker/common/install_cuda_windows_cross_compile.sh index b5d92c8215d..67a8e1d526b 100644 --- a/.ci/docker/common/install_cuda_windows_cross_compile.sh +++ b/.ci/docker/common/install_cuda_windows_cross_compile.sh @@ -18,6 +18,7 @@ declare -A CUDA_DRIVER_MAP=( ["12.8"]="12.8.1:572.61" ["12.9"]="12.9.1:576.57" ["13.0"]="13.0.2:" + ["13.2"]="13.2.2:" ) install_mingw() { diff --git a/.ci/scripts/install-rocm-dependencies.sh b/.ci/scripts/install-rocm-dependencies.sh index a7ea9b5bf49..4c96b8f94bb 100644 --- a/.ci/scripts/install-rocm-dependencies.sh +++ b/.ci/scripts/install-rocm-dependencies.sh @@ -21,7 +21,7 @@ python -m pip install "torch==${TORCH_VERSION}+rocm${ROCM_VERSION}" \ python -m pip install -r requirements-dev.txt ninja # ROCm 7.14 TorchAO wheels are Python-only. Build the pinned extension against # the installed torch and HIP libraries instead of reusing a ROCm 7.2 binary. -USE_CPP=1 MAX_JOBS="${MAX_JOBS:-8}" \ +USE_CPP=1 USE_CPU_KERNELS=1 MAX_JOBS="${MAX_JOBS:-8}" \ python -m pip install --no-build-isolation --no-deps third-party/ao python - <<'PY' diff --git a/.ci/scripts/tests/test_cu134_dependencies.py b/.ci/scripts/tests/test_cu134_dependencies.py index e88c552473f..ef46bbf7808 100644 --- a/.ci/scripts/tests/test_cu134_dependencies.py +++ b/.ci/scripts/tests/test_cu134_dependencies.py @@ -43,10 +43,11 @@ def install_commands( nightly=True, system="Linux", python_version=(3, 14), + cmake_args="", ): self.utils.determine_torch_url.cache_clear() with ( - patch.dict(os.environ, {}, clear=True), + patch.dict(os.environ, {"CMAKE_ARGS": cmake_args}, clear=True), patch.object( self.utils, "_get_cuda_version", @@ -140,6 +141,37 @@ def test_no_cuda_keeps_default_pins(self): self.assertIn("torchvision==0.29.2", domains) self.assertIn("https://download.pytorch.org/whl/cpu", core) + def test_disabled_cuda_keeps_cpu_dependencies_on_gpu_runners(self): + for cuda in ((12, 6), (13, 2)): + with self.subTest(cuda=cuda): + core, _, domains, _ = self.install_commands( + cuda, + cmake_args="-DEXECUTORCH_BUILD_VULKAN=ON -DEXECUTORCH_BUILD_CUDA=OFF", + ) + self.assertIn("torch==2.15.0", core) + self.assertIn("torchvision==0.29.2", domains) + self.assertIn("https://download.pytorch.org/whl/cpu", core) + self.assertIn("https://download.pytorch.org/whl/nightly/cpu", core) + + def test_disabled_cuda_skips_toolkit_detection(self): + for args in ( + "-DEXECUTORCH_BUILD_CUDA=OFF", + "-D EXECUTORCH_BUILD_CUDA:BOOL=0", + "-DEXECUTORCH_BUILD_CUDA=ON -DEXECUTORCH_BUILD_CUDA=OFF", + ): + self.utils.determine_torch_url.cache_clear() + with ( + self.subTest(args=args), + patch.dict(os.environ, {"CMAKE_ARGS": args}, clear=True), + patch.object(self.utils.platform, "system", return_value="Linux"), + patch.object(self.utils, "_get_cuda_version") as detect, + ): + self.assertEqual( + self.utils.determine_torch_url(self.installer.TORCH_URL_BASE), + "https://download.pytorch.org/whl/cpu", + ) + detect.assert_not_called() + def test_windows_does_not_select_cu134(self): core, _, domains, _ = self.install_commands((13, 4), system="Windows") self.assertIn("torch==2.15.0", core) diff --git a/.ci/scripts/tests/test_rocm_dependencies.py b/.ci/scripts/tests/test_rocm_dependencies.py index 396028175dd..0c4ec3a5036 100644 --- a/.ci/scripts/tests/test_rocm_dependencies.py +++ b/.ci/scripts/tests/test_rocm_dependencies.py @@ -25,7 +25,7 @@ def test_compiled_torchao_uses_the_selected_torch_and_propagates_failures(self): echo VERIFY return "$VERIFY_STATUS" else - echo "PIP $* CPP=${USE_CPP:-unset} JOBS=${MAX_JOBS:-unset}" + echo "PIP $* CPP=${USE_CPP:-unset} CPU=${USE_CPU_KERNELS:-unset} JOBS=${MAX_JOBS:-unset}" if [[ "$*" == *third-party/ao* ]]; then return "$AO_STATUS"; fi if [[ "$*" == *torch==* ]]; then return "$TORCH_STATUS"; fi fi @@ -70,7 +70,7 @@ def test_compiled_torchao_uses_the_selected_torch_and_propagates_failures(self): ) if not torch_status: self.assertIn( - "--no-build-isolation --no-deps third-party/ao CPP=1 JOBS=8", + "--no-build-isolation --no-deps third-party/ao CPP=1 CPU=1 JOBS=8", result.stdout, ) else: diff --git a/.github/workflows/cuda-windows.yml b/.github/workflows/cuda-windows.yml index 624a071c9fd..83f34c4d114 100644 --- a/.github/workflows/cuda-windows.yml +++ b/.github/workflows/cuda-windows.yml @@ -105,7 +105,7 @@ jobs: secrets-env: EXECUTORCH_HF_TOKEN runner: mt-l-x86aavx2-11-41-a10g gpu-arch-type: cuda - gpu-arch-version: "13.0" + gpu-arch-version: "13.2" docker-image: ${{ needs.docker-image.outputs.docker-registry }}/ci-image:executorch-ubuntu-22.04-cuda-windows-${{ needs.docker-image.outputs.ci-docker-hash }} submodules: recursive upload-artifact: ${{ matrix.model_repo }}-${{ matrix.model_name }}-cuda-windows-${{ matrix.quant }} @@ -206,9 +206,6 @@ jobs: timeout: 240 runner: windows.g5.4xlarge.nvidia.gpu gpu-arch-type: cuda - # The Windows runner image carries only the toolkits the CUDA wheels are built for, - # 13.2 and 13.4, so this cannot match the export's 13.0. The exported program loads - # cudart64_13.dll, which every CUDA 13 toolkit provides. gpu-arch-version: "13.2" download-artifact: ${{ matrix.model_repo }}-${{ matrix.model_name }}-cuda-windows-${{ matrix.quant }} ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} diff --git a/.github/workflows/vulkan.yml b/.github/workflows/vulkan.yml index 1fce832be6a..e9bcb82b8ff 100644 --- a/.github/workflows/vulkan.yml +++ b/.github/workflows/vulkan.yml @@ -100,7 +100,7 @@ jobs: # setup-linux.sh's "deps already in the image" assumption does not hold. # CMAKE_ARGS enables Vulkan in the pybindings so the model --test runs and # the pt2e/torchao e2e tests below execute on the GPU (default is OFF). - CMAKE_ARGS="-DEXECUTORCH_BUILD_VULKAN=ON" PYTHON_EXECUTABLE=python ./install_executorch.sh + CMAKE_ARGS="-DEXECUTORCH_BUILD_VULKAN=ON -DEXECUTORCH_BUILD_CUDA=OFF" PYTHON_EXECUTABLE=python ./install_executorch.sh python -m unittest backends/vulkan/test/test_vulkan_dynamic.py @@ -193,7 +193,7 @@ jobs: export PIP_EXTRA_INDEX_URL= pip install filelock typing-extensions "setuptools<82" sympy networkx jinja2 "fsspec[http]<=2025.3.0" numpy pillow export USE_MKL=OFF - CMAKE_ARGS="-DEXECUTORCH_BUILD_VULKAN=ON -DEXECUTORCH_BUILD_KERNELS_LLM_AOT=ON" \ + CMAKE_ARGS="-DEXECUTORCH_BUILD_VULKAN=ON -DEXECUTORCH_BUILD_CUDA=OFF -DEXECUTORCH_BUILD_KERNELS_LLM_AOT=ON" \ PYTHON_EXECUTABLE=python ./install_executorch.sh python -m pip list --format=json > "${RUNNER_ARTIFACT_DIR}/python-packages.json" diff --git a/install_utils.py b/install_utils.py index 4e1397413b3..658b820a2d1 100644 --- a/install_utils.py +++ b/install_utils.py @@ -346,6 +346,12 @@ def determine_torch_url(torch_nightly_url_base): ) return f"{torch_nightly_url_base}/cpu" + if not is_cmake_option_on( + _cmake_args_from_env(), "EXECUTORCH_BUILD_CUDA", default=True + ): + print("CUDA delegate disabled, using CPU-only PyTorch") + return f"{torch_nightly_url_base}/cpu" + print("Attempting to detect CUDA via nvcc...") try: From d7c54f49d427c240977581bbc2aba6ff06ede847 Mon Sep 17 00:00:00 2001 From: Jacob Szwejbka Date: Thu, 29 Oct 2026 21:38:32 -0700 Subject: [PATCH 08/12] [RELEASE ONLY CHANGES] Use the packaged ROCm development SDK Install the development files matching the torch-provided ROCm runtime and point TorchAO and ExecuTorch builds at its expanded SDK root, where the HIP linker library is available. Assisted by Codex. --- .ci/scripts/install-rocm-dependencies.sh | 9 +++++ .ci/scripts/tests/test_rocm_dependencies.py | 39 +++++++++++++++------ 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/.ci/scripts/install-rocm-dependencies.sh b/.ci/scripts/install-rocm-dependencies.sh index 4c96b8f94bb..b28fd695d04 100644 --- a/.ci/scripts/install-rocm-dependencies.sh +++ b/.ci/scripts/install-rocm-dependencies.sh @@ -18,6 +18,15 @@ export LD_LIBRARY_PATH="${ROCM_PYTHON_PREFIX}/lib:${LD_LIBRARY_PATH:-}" TORCH_VERSION="$(python -c 'from torch_pin import TORCH_VERSION; print(TORCH_VERSION)')" python -m pip install "torch==${TORCH_VERSION}+rocm${ROCM_VERSION}" \ --index-url "${PYTORCH_ROCM_INDEX}" +ROCM_SDK_VERSION="$(python -c 'import rocm_sdk; print(rocm_sdk.__version__)')" +python -m pip install "rocm[devel]==${ROCM_SDK_VERSION}" \ + --index-url "${PYTORCH_ROCM_INDEX}" +ROCM_PATH="$(python -m rocm_sdk path --root)" +export ROCM_PATH +export ROCM_HOME="${ROCM_PATH}" +export HIP_PATH="${ROCM_PATH}" +export PATH="${ROCM_PATH}/bin:${PATH}" +export LD_LIBRARY_PATH="${ROCM_PYTHON_PREFIX}/lib:${ROCM_PATH}/lib:${ROCM_PATH}/lib64:${LD_LIBRARY_PATH:-}" python -m pip install -r requirements-dev.txt ninja # ROCm 7.14 TorchAO wheels are Python-only. Build the pinned extension against # the installed torch and HIP libraries instead of reusing a ROCm 7.2 binary. diff --git a/.ci/scripts/tests/test_rocm_dependencies.py b/.ci/scripts/tests/test_rocm_dependencies.py index 0c4ec3a5036..e30d312ef26 100644 --- a/.ci/scripts/tests/test_rocm_dependencies.py +++ b/.ci/scripts/tests/test_rocm_dependencies.py @@ -19,7 +19,11 @@ def test_compiled_torchao_uses_the_selected_torch_and_propagates_failures(self): conda() { echo RUNTIME; } python() { if [[ "$1" == "-c" ]]; then - if [[ "$2" == *torch_pin* ]]; then echo 2.15.0; else echo /env; fi + if [[ "$2" == *torch_pin* ]]; then echo 2.15.0 + elif [[ "$2" == *rocm_sdk* ]]; then echo 7.14.1 + else echo /env; fi + elif [[ "$*" == "-m rocm_sdk path --root" ]]; then + echo /sdk elif [[ "$1" == "-" ]]; then cat >/dev/null echo VERIFY @@ -28,22 +32,26 @@ def test_compiled_torchao_uses_the_selected_torch_and_propagates_failures(self): echo "PIP $* CPP=${USE_CPP:-unset} CPU=${USE_CPU_KERNELS:-unset} JOBS=${MAX_JOBS:-unset}" if [[ "$*" == *third-party/ao* ]]; then return "$AO_STATUS"; fi if [[ "$*" == *torch==* ]]; then return "$TORCH_STATUS"; fi + if [[ "$*" == *'rocm[devel]'* ]]; then return "$SDK_STATUS"; fi fi } """ - for torch_status, ao_status, verify_status in ( - (0, 0, 0), - (1, 0, 0), - (0, 1, 0), - (0, 0, 1), + for torch_status, sdk_status, ao_status, verify_status in ( + (0, 0, 0, 0), + (1, 0, 0, 0), + (0, 1, 0, 0), + (0, 0, 1, 0), + (0, 0, 0, 1), ): - with self.subTest(torch=torch_status, ao=ao_status, verify=verify_status): + with self.subTest( + torch=torch_status, sdk=sdk_status, ao=ao_status, verify=verify_status + ): result = subprocess.run( [ "bash", "-c", stubs - + "source .ci/scripts/install-rocm-dependencies.sh\necho INSTALL_ET", + + "source .ci/scripts/install-rocm-dependencies.sh\necho INSTALL_ET\necho SDK_HOME=$ROCM_HOME HIP_PATH=$HIP_PATH ROCM_PATH=$ROCM_PATH", ], cwd=ROOT, env={ @@ -51,6 +59,7 @@ def test_compiled_torchao_uses_the_selected_torch_and_propagates_failures(self): "ROCM_VERSION": "7.14", "PYTORCH_ROCM_INDEX": "https://download.pytorch.org/whl/rocm7.14", "TORCH_STATUS": str(torch_status), + "SDK_STATUS": str(sdk_status), "AO_STATUS": str(ao_status), "VERIFY_STATUS": str(verify_status), "MAX_JOBS": "8", @@ -60,7 +69,7 @@ def test_compiled_torchao_uses_the_selected_torch_and_propagates_failures(self): ) self.assertEqual( result.returncode, - max(torch_status, ao_status, verify_status), + max(torch_status, sdk_status, ao_status, verify_status), result.stderr, ) self.assertTrue(result.stdout.startswith("RUNTIME\n")) @@ -69,6 +78,11 @@ def test_compiled_torchao_uses_the_selected_torch_and_propagates_failures(self): result.stdout, ) if not torch_status: + self.assertIn( + "rocm[devel]==7.14.1 --index-url https://download.pytorch.org/whl/rocm7.14", + result.stdout, + ) + if not (torch_status or sdk_status): self.assertIn( "--no-build-isolation --no-deps third-party/ao CPP=1 CPU=1 JOBS=8", result.stdout, @@ -76,9 +90,14 @@ def test_compiled_torchao_uses_the_selected_torch_and_propagates_failures(self): else: self.assertNotIn("third-party/ao", result.stdout) self.assertEqual( - "VERIFY" in result.stdout, not (torch_status or ao_status) + "VERIFY" in result.stdout, + not (torch_status or sdk_status or ao_status), ) self.assertEqual("INSTALL_ET" in result.stdout, result.returncode == 0) + if result.returncode == 0: + self.assertIn( + "SDK_HOME=/sdk HIP_PATH=/sdk ROCM_PATH=/sdk", result.stdout + ) if __name__ == "__main__": From c8440c770193c9a98a563cd9ff56117d7374370f Mon Sep 17 00:00:00 2001 From: Jacob Szwejbka Date: Thu, 29 Oct 2026 21:51:26 -0700 Subject: [PATCH 09/12] [RELEASE ONLY CHANGES] Keep the ROCm C++ toolchain consistent Use the conda-forge GCC compiler and matching runtime libraries for ROCm builds so the Red Hat nonshared libstdc++ objects are not mixed with conda shared libraries. Assisted by Codex. --- .ci/scripts/install-rocm-dependencies.sh | 5 ++++- .ci/scripts/tests/test_rocm_dependencies.py | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.ci/scripts/install-rocm-dependencies.sh b/.ci/scripts/install-rocm-dependencies.sh index b28fd695d04..21c68631509 100644 --- a/.ci/scripts/install-rocm-dependencies.sh +++ b/.ci/scripts/install-rocm-dependencies.sh @@ -11,8 +11,11 @@ if ! command -v conda >/dev/null; then echo "The ROCm CI image must provide conda for its C++ runtime libraries" exit 1 fi -conda install -y -c conda-forge 'libstdcxx-ng>=12' +conda install -y --override-channels -c conda-forge \ + gxx_linux-64=16.2.0 libstdcxx-ng=16.2.0 libgcc-ng=16.2.0 ROCM_PYTHON_PREFIX="$(python -c 'import sys; print(sys.prefix)')" +export CC="${ROCM_PYTHON_PREFIX}/bin/x86_64-conda-linux-gnu-cc" +export CXX="${ROCM_PYTHON_PREFIX}/bin/x86_64-conda-linux-gnu-c++" export LD_LIBRARY_PATH="${ROCM_PYTHON_PREFIX}/lib:${LD_LIBRARY_PATH:-}" TORCH_VERSION="$(python -c 'from torch_pin import TORCH_VERSION; print(TORCH_VERSION)')" diff --git a/.ci/scripts/tests/test_rocm_dependencies.py b/.ci/scripts/tests/test_rocm_dependencies.py index e30d312ef26..35ac78532ea 100644 --- a/.ci/scripts/tests/test_rocm_dependencies.py +++ b/.ci/scripts/tests/test_rocm_dependencies.py @@ -16,7 +16,7 @@ class RocmDependenciesTest(unittest.TestCase): def test_compiled_torchao_uses_the_selected_torch_and_propagates_failures(self): stubs = """ -conda() { echo RUNTIME; } +conda() { echo "RUNTIME $*"; } python() { if [[ "$1" == "-c" ]]; then if [[ "$2" == *torch_pin* ]]; then echo 2.15.0 @@ -51,7 +51,7 @@ def test_compiled_torchao_uses_the_selected_torch_and_propagates_failures(self): "bash", "-c", stubs - + "source .ci/scripts/install-rocm-dependencies.sh\necho INSTALL_ET\necho SDK_HOME=$ROCM_HOME HIP_PATH=$HIP_PATH ROCM_PATH=$ROCM_PATH", + + "source .ci/scripts/install-rocm-dependencies.sh\necho INSTALL_ET\necho SDK_HOME=$ROCM_HOME HIP_PATH=$HIP_PATH ROCM_PATH=$ROCM_PATH\necho CC=$CC CXX=$CXX", ], cwd=ROOT, env={ @@ -72,7 +72,12 @@ def test_compiled_torchao_uses_the_selected_torch_and_propagates_failures(self): max(torch_status, sdk_status, ao_status, verify_status), result.stderr, ) - self.assertTrue(result.stdout.startswith("RUNTIME\n")) + self.assertTrue( + result.stdout.startswith( + "RUNTIME install -y --override-channels -c conda-forge " + "gxx_linux-64=16.2.0 libstdcxx-ng=16.2.0 libgcc-ng=16.2.0\n" + ) + ) self.assertIn( "torch==2.15.0+rocm7.14 --index-url https://download.pytorch.org/whl/rocm7.14", result.stdout, @@ -98,6 +103,11 @@ def test_compiled_torchao_uses_the_selected_torch_and_propagates_failures(self): self.assertIn( "SDK_HOME=/sdk HIP_PATH=/sdk ROCM_PATH=/sdk", result.stdout ) + self.assertIn( + "CC=/env/bin/x86_64-conda-linux-gnu-cc " + "CXX=/env/bin/x86_64-conda-linux-gnu-c++", + result.stdout, + ) if __name__ == "__main__": From b376004507498000bc34262018d9dd39fe96b063 Mon Sep 17 00:00:00 2001 From: Jacob Szwejbka Date: Thu, 29 Oct 2026 22:00:31 -0700 Subject: [PATCH 10/12] [RELEASE ONLY CHANGES] Match the ROCm compiler sysroot to its image Require glibc 2.28 development headers so conda cannot select an older sysroot missing timespec_get while using the new C++ compiler. Assisted by Codex. --- .ci/scripts/install-rocm-dependencies.sh | 3 ++- .ci/scripts/tests/test_rocm_dependencies.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.ci/scripts/install-rocm-dependencies.sh b/.ci/scripts/install-rocm-dependencies.sh index 21c68631509..35aab8cef1a 100644 --- a/.ci/scripts/install-rocm-dependencies.sh +++ b/.ci/scripts/install-rocm-dependencies.sh @@ -12,7 +12,8 @@ if ! command -v conda >/dev/null; then exit 1 fi conda install -y --override-channels -c conda-forge \ - gxx_linux-64=16.2.0 libstdcxx-ng=16.2.0 libgcc-ng=16.2.0 + gxx_linux-64=16.2.0 libstdcxx-ng=16.2.0 libgcc-ng=16.2.0 \ + sysroot_linux-64=2.28 ROCM_PYTHON_PREFIX="$(python -c 'import sys; print(sys.prefix)')" export CC="${ROCM_PYTHON_PREFIX}/bin/x86_64-conda-linux-gnu-cc" export CXX="${ROCM_PYTHON_PREFIX}/bin/x86_64-conda-linux-gnu-c++" diff --git a/.ci/scripts/tests/test_rocm_dependencies.py b/.ci/scripts/tests/test_rocm_dependencies.py index 35ac78532ea..f4000eb8112 100644 --- a/.ci/scripts/tests/test_rocm_dependencies.py +++ b/.ci/scripts/tests/test_rocm_dependencies.py @@ -75,7 +75,8 @@ def test_compiled_torchao_uses_the_selected_torch_and_propagates_failures(self): self.assertTrue( result.stdout.startswith( "RUNTIME install -y --override-channels -c conda-forge " - "gxx_linux-64=16.2.0 libstdcxx-ng=16.2.0 libgcc-ng=16.2.0\n" + "gxx_linux-64=16.2.0 libstdcxx-ng=16.2.0 libgcc-ng=16.2.0 " + "sysroot_linux-64=2.28\n" ) ) self.assertIn( From c2da61241de4fe86773b8ac3c06410aa2278245d Mon Sep 17 00:00:00 2001 From: Jacob Szwejbka Date: Thu, 29 Oct 2026 22:29:03 -0700 Subject: [PATCH 11/12] [RELEASE ONLY CHANGES] Preserve the Windows CUDA host compiler Select MSVC through CUDAHOSTCXX after PyTorch overrides the CMake variable during CUDA initialization. Authored with Codex assistance. --- .ci/scripts/wheel/vc_env_helper.bat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/scripts/wheel/vc_env_helper.bat b/.ci/scripts/wheel/vc_env_helper.bat index 37a9921274b..be43e962657 100644 --- a/.ci/scripts/wheel/vc_env_helper.bat +++ b/.ci/scripts/wheel/vc_env_helper.bat @@ -23,10 +23,10 @@ if "%VSDEVCMD_ARGS%" == "" ( @echo on -REM nvcc with clang-cl collides with MSVC's cmath definitions on CUDA 13.2. +REM CUDAHOSTCXX is read after Torch overwrites CMAKE_CUDA_HOST_COMPILER. if /I "%CU_VERSION:~0,2%" == "cu" ( where cl.exe >nul 2>&1 || exit /b 1 - set "CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_CUDA_HOST_COMPILER=cl.exe" + for /f "delims=" %%i in ('where cl.exe') do if not defined CUDAHOSTCXX set "CUDAHOSTCXX=%%i" ) if "%CU_VERSION%" == "xpu" call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" From 9f56344eb5c5190279a059afc7e4f258af27a5ee Mon Sep 17 00:00:00 2001 From: Jacob Szwejbka Date: Thu, 29 Oct 2026 22:30:45 -0700 Subject: [PATCH 12/12] [RELEASE ONLY CHANGES] Supply the FLA graph flag during export PyTorch 2.15 wrap_triton export requires the cumulative-sum USE_GRAPH argument explicitly instead of relying on the eager Triton default. Authored with Codex assistance. --- backends/cuda/triton/kernels/chunk_gated_delta_rule.py | 1 + 1 file changed, 1 insertion(+) diff --git a/backends/cuda/triton/kernels/chunk_gated_delta_rule.py b/backends/cuda/triton/kernels/chunk_gated_delta_rule.py index a7403e22c6a..e98f7528139 100644 --- a/backends/cuda/triton/kernels/chunk_gated_delta_rule.py +++ b/backends/cuda/triton/kernels/chunk_gated_delta_rule.py @@ -128,6 +128,7 @@ def chunk_gated_delta_rule( REVERSE=False, HAS_SCALE=True, IS_VARLEN=False, + USE_GRAPH=False, ) # 2. chunk_scaled_dot_kkt: compute beta * K * K^T with gating