Add the CUDA torch 2.12 extras alongside the 2.11 ones
torch 2.12.0 and 2.12.1 are published on the cu126 and cu130 indexes, so _auto_install.py raised "too new" for anyone already on them. The new cuNNNonlytorch2120 and cuNNNonlytorch2121 leaves follow the 2110 shape: the trio is pinned to the matching +cuNNN local build because xformers 0.0.35 depends on torch without pinning it, so an unpinned trio walks torch up to the newest release the index serves. torchvision exact-pins torch, so 2.12.0 takes 0.27.0 and 2.12.1 takes 0.27.1. torchaudio has no 2.12 release at all; 2.11.0 dropped its exact torch pin, so it stays as the audio member of both trios. There is no cu128 leaf: that index tops out at torch 2.11.0, so a cu128 2.12 extra would be unresolvable. _auto_install.py now rejects CUDA 12.8 on torch 2.12 with a message naming the two flavors that exist instead of printing a command for an extra that does not. Verified in throwaway uv venvs on a real GPU: each candidate trio plus the pinned xformers wheel and bitsandbytes installed, then torch bf16 matmul, sdpa, torchvision nms, torchaudio resample, xformers memory_efficient_attention with its registered op set, the swiglu symbol and a bitsandbytes 4bit forward all ran. cu130 2.12.0 and 2.12.1 pass end to end, with the shipping 2.11.0 trio as the control. The cu126 wheels build sm_50 through sm_90 so they cannot launch kernels on the sm_100 test box; those two cases were checked for extension loading and CPU execution instead. Every new leaf also resolves cleanly through uv pip compile for both linux and windows targets.
This commit is contained in:
parent
d9efdafaa7
commit
fb177fa867
3 changed files with 169 additions and 9 deletions
|
|
@ -381,6 +381,41 @@ cu130onlytorch2110 = [
|
|||
"xformers @ https://download.pytorch.org/whl/cu130/xformers-0.0.35-py39-none-manylinux_2_28_x86_64.whl ; ('linux' in sys_platform) and (platform_machine == 'AMD64' or platform_machine == 'x86_64')",
|
||||
"xformers @ https://download.pytorch.org/whl/cu130/xformers-0.0.35-py39-none-win_amd64.whl ; (sys_platform == 'win32') and (platform_machine == 'AMD64' or platform_machine == 'x86_64')",
|
||||
]
|
||||
# torch 2.12 ships on the cu126 and cu130 indexes only, so there is no cu128 leaf.
|
||||
# torchaudio has no 2.12 release; 2.11.0 carries no torch pin and pairs with 2.12.
|
||||
cu126onlytorch2120 = [
|
||||
# Same +cuNNN pin as cu126onlytorch2110: xformers 0.0.35 depends on torch without
|
||||
# pinning it, so an unpinned trio walks up to the newest release on the index.
|
||||
"torch==2.12.0+cu126",
|
||||
"torchvision==0.27.0+cu126",
|
||||
"torchaudio==2.11.0+cu126",
|
||||
"xformers @ https://download.pytorch.org/whl/cu126/xformers-0.0.35-py39-none-manylinux_2_28_x86_64.whl ; ('linux' in sys_platform) and (platform_machine == 'AMD64' or platform_machine == 'x86_64')",
|
||||
"xformers @ https://download.pytorch.org/whl/cu126/xformers-0.0.35-py39-none-win_amd64.whl ; (sys_platform == 'win32') and (platform_machine == 'AMD64' or platform_machine == 'x86_64')",
|
||||
]
|
||||
cu130onlytorch2120 = [
|
||||
# Same +cuNNN pin as cu126onlytorch2120, on the cu130 index.
|
||||
"torch==2.12.0+cu130",
|
||||
"torchvision==0.27.0+cu130",
|
||||
"torchaudio==2.11.0+cu130",
|
||||
"xformers @ https://download.pytorch.org/whl/cu130/xformers-0.0.35-py39-none-manylinux_2_28_x86_64.whl ; ('linux' in sys_platform) and (platform_machine == 'AMD64' or platform_machine == 'x86_64')",
|
||||
"xformers @ https://download.pytorch.org/whl/cu130/xformers-0.0.35-py39-none-win_amd64.whl ; (sys_platform == 'win32') and (platform_machine == 'AMD64' or platform_machine == 'x86_64')",
|
||||
]
|
||||
cu126onlytorch2121 = [
|
||||
# torchvision exact-pins torch, so the 2.12.1 patch takes 0.27.1.
|
||||
"torch==2.12.1+cu126",
|
||||
"torchvision==0.27.1+cu126",
|
||||
"torchaudio==2.11.0+cu126",
|
||||
"xformers @ https://download.pytorch.org/whl/cu126/xformers-0.0.35-py39-none-manylinux_2_28_x86_64.whl ; ('linux' in sys_platform) and (platform_machine == 'AMD64' or platform_machine == 'x86_64')",
|
||||
"xformers @ https://download.pytorch.org/whl/cu126/xformers-0.0.35-py39-none-win_amd64.whl ; (sys_platform == 'win32') and (platform_machine == 'AMD64' or platform_machine == 'x86_64')",
|
||||
]
|
||||
cu130onlytorch2121 = [
|
||||
# Same +cuNNN pin as cu126onlytorch2121, on the cu130 index.
|
||||
"torch==2.12.1+cu130",
|
||||
"torchvision==0.27.1+cu130",
|
||||
"torchaudio==2.11.0+cu130",
|
||||
"xformers @ https://download.pytorch.org/whl/cu130/xformers-0.0.35-py39-none-manylinux_2_28_x86_64.whl ; ('linux' in sys_platform) and (platform_machine == 'AMD64' or platform_machine == 'x86_64')",
|
||||
"xformers @ https://download.pytorch.org/whl/cu130/xformers-0.0.35-py39-none-win_amd64.whl ; (sys_platform == 'win32') and (platform_machine == 'AMD64' or platform_machine == 'x86_64')",
|
||||
]
|
||||
cu118 = [
|
||||
"unsloth[huggingface]",
|
||||
"bitsandbytes>=0.45.5,!=0.46.0,!=0.48.0",
|
||||
|
|
@ -604,6 +639,26 @@ cu130-torch2110 = [
|
|||
"bitsandbytes>=0.45.5,!=0.46.0,!=0.48.0",
|
||||
"unsloth[cu130onlytorch2110]",
|
||||
]
|
||||
cu126-torch2120 = [
|
||||
"unsloth[huggingface]",
|
||||
"bitsandbytes>=0.45.5,!=0.46.0,!=0.48.0",
|
||||
"unsloth[cu126onlytorch2120]",
|
||||
]
|
||||
cu130-torch2120 = [
|
||||
"unsloth[huggingface]",
|
||||
"bitsandbytes>=0.45.5,!=0.46.0,!=0.48.0",
|
||||
"unsloth[cu130onlytorch2120]",
|
||||
]
|
||||
cu126-torch2121 = [
|
||||
"unsloth[huggingface]",
|
||||
"bitsandbytes>=0.45.5,!=0.46.0,!=0.48.0",
|
||||
"unsloth[cu126onlytorch2121]",
|
||||
]
|
||||
cu130-torch2121 = [
|
||||
"unsloth[huggingface]",
|
||||
"bitsandbytes>=0.45.5,!=0.46.0,!=0.48.0",
|
||||
"unsloth[cu130onlytorch2121]",
|
||||
]
|
||||
kaggle = [
|
||||
"unsloth[huggingface]",
|
||||
]
|
||||
|
|
@ -921,6 +976,26 @@ cu130-ampere-torch2110 = [
|
|||
"bitsandbytes>=0.45.5,!=0.46.0,!=0.48.0",
|
||||
"unsloth[cu130onlytorch2110]",
|
||||
]
|
||||
cu126-ampere-torch2120 = [
|
||||
"unsloth[huggingface]",
|
||||
"bitsandbytes>=0.45.5,!=0.46.0,!=0.48.0",
|
||||
"unsloth[cu126onlytorch2120]",
|
||||
]
|
||||
cu130-ampere-torch2120 = [
|
||||
"unsloth[huggingface]",
|
||||
"bitsandbytes>=0.45.5,!=0.46.0,!=0.48.0",
|
||||
"unsloth[cu130onlytorch2120]",
|
||||
]
|
||||
cu126-ampere-torch2121 = [
|
||||
"unsloth[huggingface]",
|
||||
"bitsandbytes>=0.45.5,!=0.46.0,!=0.48.0",
|
||||
"unsloth[cu126onlytorch2121]",
|
||||
]
|
||||
cu130-ampere-torch2121 = [
|
||||
"unsloth[huggingface]",
|
||||
"bitsandbytes>=0.45.5,!=0.46.0,!=0.48.0",
|
||||
"unsloth[cu130onlytorch2121]",
|
||||
]
|
||||
flashattentiontorch260abiFALSEcu12x = [
|
||||
"flash-attn @ https://github.com/Dao-AILab/flash-attention/releases/download/v2.7.4.post1/flash_attn-2.7.4.post1+cu12torch2.6cxx11abiFALSE-cp39-cp39-linux_x86_64.whl ; ('linux' in sys_platform) and python_version == '3.9'",
|
||||
"flash-attn @ https://github.com/Dao-AILab/flash-attention/releases/download/v2.7.4.post1/flash_attn-2.7.4.post1+cu12torch2.6cxx11abiFALSE-cp310-cp310-linux_x86_64.whl ; ('linux' in sys_platform) and python_version == '3.10'",
|
||||
|
|
|
|||
|
|
@ -14,11 +14,13 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
"""Regression guard for the CUDA torch2110 optional-dependency extras.
|
||||
"""Regression guard for the CUDA torch2110 and torch212x optional-dependency extras.
|
||||
|
||||
The cuXXXonlytorch2110 extras must pin the torch trio to the matching +cuXXX local
|
||||
build (torch 2.11 defaults to a CUDA-13 PyPI wheel), or resolution mismatches the
|
||||
xformers wheel. Hermetic: only parses pyproject.toml, no network or install.
|
||||
The cuXXXonlytorch2110 / cuXXXonlytorch212X extras must pin the torch trio to the
|
||||
matching +cuXXX local build (these releases default to a CUDA-13 PyPI wheel, and
|
||||
xformers 0.0.35 depends on torch without pinning it), or resolution walks torch up
|
||||
to the newest release on the index and mismatches the xformers wheel. Hermetic:
|
||||
only parses pyproject.toml and _auto_install.py, no network or install.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -33,14 +35,27 @@ try: # tomllib is stdlib on 3.11+; older interpreters need the tomli backport.
|
|||
except ModuleNotFoundError: # pragma: no cover - Python 3.9 / 3.10
|
||||
tomllib = pytest.importorskip("tomli")
|
||||
|
||||
PYPROJECT = Path(__file__).resolve().parents[1] / "pyproject.toml"
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
PYPROJECT = REPO / "pyproject.toml"
|
||||
AUTO_INSTALL = REPO / "unsloth" / "_auto_install.py"
|
||||
_TORCH_TRIO = ("torch", "torchvision", "torchaudio")
|
||||
# torchaudio has no 2.12 release, so the 2.12 leaves keep the unpinned 2.11.0 audio wheel.
|
||||
_TORCH212_TRIO = {
|
||||
"torch2120": {"torch": "2.12.0", "torchvision": "0.27.0", "torchaudio": "2.11.0"},
|
||||
"torch2121": {"torch": "2.12.1", "torchvision": "0.27.1", "torchaudio": "2.11.0"},
|
||||
}
|
||||
# torch 2.12 is absent from the cu128 index, so only these two flavors get 2.12 extras.
|
||||
_TORCH212_CUDA = ("cu126", "cu130")
|
||||
|
||||
|
||||
def _extras() -> dict[str, list[str]]:
|
||||
with open(PYPROJECT, "rb") as f:
|
||||
data = tomllib.load(f)
|
||||
return data["project"]["optional-dependencies"]
|
||||
|
||||
|
||||
def _extra(name: str) -> list[str]:
|
||||
with open(PYPROJECT, "rb") as f:
|
||||
data = tomllib.load(f)
|
||||
return data["project"]["optional-dependencies"][name]
|
||||
return _extras()[name]
|
||||
|
||||
|
||||
def _reqs(specs: list[str]) -> dict[str, list[Requirement]]:
|
||||
|
|
@ -89,6 +104,71 @@ def test_torch2110_wrapper_references_matching_leaf(cuda: str, variant: str):
|
|||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("cuda", _TORCH212_CUDA)
|
||||
@pytest.mark.parametrize("series", sorted(_TORCH212_TRIO))
|
||||
def test_cuda12_torch212_pins_matching_local_build(cuda: str, series: str):
|
||||
reqs = _reqs(_extra(f"{cuda}only{series}"))
|
||||
for pkg, want in _TORCH212_TRIO[series].items():
|
||||
(req,) = reqs[pkg]
|
||||
spec = str(req.specifier)
|
||||
assert spec == f"=={want}+{cuda}", (
|
||||
f"{cuda}only{series}: {pkg} pinned as '{spec}', "
|
||||
f"expected the =={want}+{cuda} local build"
|
||||
)
|
||||
assert req.marker is None, f"the {pkg} pin must apply on every machine"
|
||||
xformers = reqs["xformers"]
|
||||
linux = [r for r in xformers if r.url and r.url.endswith("manylinux_2_28_x86_64.whl")]
|
||||
windows = [r for r in xformers if r.url and r.url.endswith("win_amd64.whl")]
|
||||
assert len(linux) == 1 and len(windows) == 1, f"unexpected xformers wheels: {xformers}"
|
||||
for r in linux + windows:
|
||||
assert f"/whl/{cuda}/xformers-0.0.35-" in r.url, f"xformers not on the {cuda} index: {r.url}"
|
||||
assert r.marker is not None
|
||||
assert not r.marker.evaluate({"sys_platform": "linux", "platform_machine": "aarch64"})
|
||||
assert not r.marker.evaluate({"sys_platform": "win32", "platform_machine": "ARM64"})
|
||||
assert linux[0].marker.evaluate({"sys_platform": "linux", "platform_machine": "x86_64"})
|
||||
assert windows[0].marker.evaluate({"sys_platform": "win32", "platform_machine": "AMD64"})
|
||||
|
||||
|
||||
@pytest.mark.parametrize("cuda", _TORCH212_CUDA)
|
||||
@pytest.mark.parametrize("series", sorted(_TORCH212_TRIO))
|
||||
@pytest.mark.parametrize("variant", ["", "ampere-"])
|
||||
def test_torch212_wrapper_references_matching_leaf(cuda: str, series: str, variant: str):
|
||||
specs = _extra(f"{cuda}-{variant}{series}")
|
||||
assert specs == [
|
||||
"unsloth[huggingface]",
|
||||
"bitsandbytes>=0.45.5,!=0.46.0,!=0.48.0",
|
||||
f"unsloth[{cuda}only{series}]",
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("series", sorted(_TORCH212_TRIO))
|
||||
def test_no_cu128_torch212_extras(series: str):
|
||||
# torch 2.12 is not published on the cu128 index; a cu128 leaf would be unresolvable.
|
||||
names = _extras()
|
||||
for name in (f"cu128only{series}", f"cu128-{series}", f"cu128-ampere-{series}"):
|
||||
assert name not in names, f"{name} cannot resolve: no torch 2.12 on the cu128 index"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("series", sorted(_TORCH212_TRIO))
|
||||
def test_auto_install_maps_torch212_to_defined_extras(series: str):
|
||||
# The printed command must name extras that exist, and must add the index that
|
||||
# serves the +cuNNN local builds those extras pin.
|
||||
source = AUTO_INSTALL.read_text()
|
||||
assert f"'cu{{}}{{}}-{series}'" in source, f"_auto_install.py never selects {series}"
|
||||
assert f"'-{series}'" in source, f"{series} missing from the extra-index-url gate"
|
||||
names = _extras()
|
||||
for cuda in _TORCH212_CUDA:
|
||||
for variant in ("", "-ampere"):
|
||||
assert f"cu{cuda[2:]}{variant}-{series}" in names
|
||||
|
||||
|
||||
def test_auto_install_rejects_cuda128_on_torch212():
|
||||
# cu128 tops out at torch 2.11, so 2.12 on that flavor must fail loudly rather
|
||||
# than print an install command for an extra that does not exist.
|
||||
source = AUTO_INSTALL.read_text()
|
||||
assert 'if v >= V(\'2.12.0\') and cuda not in ("12.6", "13.0")' in source
|
||||
|
||||
|
||||
@pytest.mark.parametrize("cuda", ["cu126", "cu128", "cu130"])
|
||||
def test_cuda12_torch2100_keeps_torch_pinned_off_x86(cuda: str):
|
||||
# xformers wheels now carry x86-64 markers, so the leaf must pin torch for ARM64.
|
||||
|
|
|
|||
|
|
@ -38,10 +38,15 @@ elif v < V('2.9.2'): x = 'cu{}{}-torch291'
|
|||
elif v < V('2.10.1'): x = 'cu{}{}-torch2100'
|
||||
elif v < V('2.11.0'): raise RuntimeError(f"Torch = {v} not supported!")
|
||||
elif v < V('2.11.1'): x = 'cu{}{}-torch2110'
|
||||
elif v < V('2.12.0'): raise RuntimeError(f"Torch = {v} not supported!")
|
||||
elif v < V('2.12.1'): x = 'cu{}{}-torch2120'
|
||||
elif v < V('2.12.2'): x = 'cu{}{}-torch2121'
|
||||
else: raise RuntimeError(f"Torch = {v} too new!")
|
||||
if v > V('2.6.9') and cuda not in ("11.8", "12.6", "12.8", "13.0"): raise RuntimeError(f"CUDA = {cuda} not supported!")
|
||||
if v >= V('2.10.0') and cuda not in ("12.6", "12.8", "13.0"): raise RuntimeError(f"Torch = {v} requires CUDA 12.6, 12.8, or 13.0! Got CUDA = {cuda}")
|
||||
# torch 2.12 is published on the cu126 and cu130 indexes only, so there is no cu128 extra.
|
||||
if v >= V('2.12.0') and cuda not in ("12.6", "13.0"): raise RuntimeError(f"Torch = {v} requires CUDA 12.6 or 13.0! Got CUDA = {cuda}")
|
||||
x = x.format(cuda.replace(".", ""), "-ampere" if False else "") # is_ampere is broken due to flash-attn
|
||||
# torch2110 extras pin +cuNNN local builds that only resolve from the matching index.
|
||||
extra_index = f' --extra-index-url https://download.pytorch.org/whl/cu{cuda.replace(".", "")}' if (x.endswith('-torch2110') and cuda in ("12.6", "12.8", "13.0")) else ''
|
||||
# torch2110 and later extras pin +cuNNN local builds that only resolve from the matching index.
|
||||
extra_index = f' --extra-index-url https://download.pytorch.org/whl/cu{cuda.replace(".", "")}' if (x.endswith(('-torch2110', '-torch2120', '-torch2121')) and cuda in ("12.6", "12.8", "13.0")) else ''
|
||||
print(f'pip install --upgrade pip setuptools wheel && pip install --no-deps git+https://github.com/unslothai/unsloth-zoo.git && pip install "unsloth[{x}] @ git+https://github.com/unslothai/unsloth.git" --no-build-isolation{extra_index}')
|
||||
Loading…
Add table
Add a link
Reference in a new issue