From 249ba9e9f13039e6154bd29a9cdfd55a5ea85c09 Mon Sep 17 00:00:00 2001 From: Jammy2211 Date: Sun, 4 Oct 2026 15:43:01 +0100 Subject: [PATCH] fix: exclude deadlocking JAX releases while preserving older support --- README.md | 27 ++++++++++++++++++++ pyproject.toml | 6 +++-- test_autonerves/test_dependency_policy.py | 30 +++++++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 test_autonerves/test_dependency_policy.py diff --git a/README.md b/README.md index f117f0e..6180bfa 100644 --- a/README.md +++ b/README.md @@ -115,3 +115,30 @@ Nothing on the board edits config. [PyAutoBrain/ORGANISM.md](https://github.com/PyAutoLabs/PyAutoBrain/blob/main/ORGANISM.md), documented in full at - Ecosystem: [PyAutoLabs on GitHub](https://github.com/PyAutoLabs) + +## JAX compatibility + +The package retains JAX/JAXlib `>=0.7,<0.12`, excluding `0.10.*` and `0.11.0`. +Those excluded releases contain CPU batched LAPACK scheduling that can deadlock +while materializing a vectorized likelihood (PyAutoHeart#274). The failure was +captured on 0.10.2; tagged source identifies the same path in the other excluded +releases. Open-source builds disable it from 0.11.1. This restriction does not +replace the separate historical FFT/Eigen workaround. + +The compatibility diagnostics exercise **0.9.2** as an older endpoint and +**0.11.2** as the recommended validation baseline. They do not certify every +version permitted by the retained range or promise identical performance across +versions. A new minimum of 0.11.2 is deliberately avoided: it would unnecessarily +force newer NumPy/SciPy requirements on users with working older environments. + +Updated family packages require `autonerves>2026.10.4.1` so their resolver cannot +fall back to a Nerves release without the exclusions. This is a release ordering +requirement: publish a Nerves wheel containing this policy before the corresponding +family wheels. No release version is chosen by this bound, and existing published +wheels are not retroactively repaired. An unpinned family package can still be +backtracked to an entirely older family release; select the repaired family +release explicitly when applying the fix to a constrained environment. + +Keep JAX, JAXlib and any CUDA plugin on a mutually compatible version. Use pip's +resolver rather than `--no-deps`, and verify the resulting environment with +`python -m pip check`. Existing lockfiles need regeneration to adopt the policy. diff --git a/pyproject.toml b/pyproject.toml index e1d9d58..6cc63cf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,11 +32,13 @@ dependencies = [ # wheels for >=0.7), so the markers let Intel Macs resolve to the # NumPy-only path instead of failing at install. jax_wrapper warns loudly # at import when JAX is absent. + # CPU batched LAPACK can deadlock in 0.10.x / 0.11.0 (Heart#274). + # Preserve older versions; 0.9.2 and 0.11.2 are compatibility witnesses. # jax 0.11 note: 0.11 requires numpy>=2.1 and scipy>=1.15, so this cap # widen also raises those effective floors for every install that # resolves jax (PyAutoFit#1510). - 'jax>=0.7.0,<0.12.0; sys_platform != "darwin" or platform_machine == "arm64"', - 'jaxlib>=0.7.0,<0.12.0; sys_platform != "darwin" or platform_machine == "arm64"', + 'jax>=0.7.0,<0.12.0,!=0.10.*,!=0.11.0; sys_platform != "darwin" or platform_machine == "arm64"', + 'jaxlib>=0.7.0,<0.12.0,!=0.10.*,!=0.11.0; sys_platform != "darwin" or platform_machine == "arm64"', 'jaxnnls==1.0.1; sys_platform != "darwin" or platform_machine == "arm64"' ] diff --git a/test_autonerves/test_dependency_policy.py b/test_autonerves/test_dependency_policy.py new file mode 100644 index 0000000..2dbfabc --- /dev/null +++ b/test_autonerves/test_dependency_policy.py @@ -0,0 +1,30 @@ +"""Installer contract: exclude the CPU deadlock without discarding older JAX.""" +from pathlib import Path +import tomllib + +from packaging.requirements import Requirement +import pytest + + +@pytest.fixture(params=['jax', 'jaxlib']) +def requirement(request): + project = tomllib.loads((Path(__file__).parents[1] / 'pyproject.toml').read_text()) + return next(Requirement(value) for value in project['project']['dependencies'] + if Requirement(value).name == request.param) + + +@pytest.mark.parametrize('version,allowed', [ + ('0.7.0', True), ('0.9.2', True), ('0.10.0', False), ('0.10.1', False), + ('0.10.2', False), ('0.10.99', False), ('0.11.0', False), ('0.11.1', True), + ('0.11.2', True), ('0.12.0', False), +]) +def test_cpu_deadlock_exclusion_keeps_older_versions(requirement, version, allowed): + assert (version in requirement.specifier) is allowed + + +@pytest.mark.parametrize('system,machine,required', [ + ('darwin', 'x86_64', False), ('darwin', 'arm64', True), + ('linux', 'x86_64', True), ('win32', 'AMD64', True), +]) +def test_existing_platform_exemption_is_preserved(requirement, system, machine, required): + assert requirement.marker.evaluate({'sys_platform': system, 'platform_machine': machine}) is required