From effbe1c5850620087aa59b574f95345f9f48f6ed Mon Sep 17 00:00:00 2001 From: "Patel, Nilaykumar K" Date: Wed, 7 Oct 2026 04:55:04 -0400 Subject: [PATCH] Add [rocm] extra, ROCm install docs, and HIP version diagnostics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds support for the AMD ROCm imaging stack as a dedicated pip extra: - **pyproject.toml**: new `rocm = ["amd-hipcim>=26.6.0; platform_system == 'Linux'"]` extra. `amd-hipcim` ships the `cucim` Python namespace so all existing `optional_import("cucim…")` call sites work on ROCm without code changes. Omitted from `all` (requires AMD's package index, not PyPI). The `>=26.6.0` floor skips the public PyPI placeholder stub (0.0.2). - **docs/source/installation.md**: new `rocm` section documenting the extra, the AMD index requirement, the cp312/x86_64-only wheel availability, and the `[all]` incompatibility on ROCm. Fixes the developer-setup recipe (`print_dependencies.py all testing` instead of `\*`, which now also includes the index-restricted `rocm` extra). - **monai/config/deviceconfig.py**, **monai/config/check_env.py**: report `HIP version` instead of `CUDA version: None` on ROCm PyTorch builds, where `torch.version.cuda` is `None` and the toolkit version is `torch.version.hip`. No existing consumer of the `get_gpu_info()` dict keys is affected. - **Dockerfile.rocm**: align `amd-hipcim>=26.6.0` floor with pyproject.toml. - **tests/config/test_print_info.py**: two mock-based tests covering the HIP/CUDA version branch in `get_gpu_info()` under both ROCm and CUDA builds. Signed-off-by: Patel, Nilaykumar K --- Dockerfile.rocm | 2 +- docs/source/installation.md | 32 +++++++++++++++++++++++++++--- monai/config/check_env.py | 6 +++++- monai/config/deviceconfig.py | 7 ++++++- pyproject.toml | 4 ++++ tests/config/test_print_info.py | 35 +++++++++++++++++++++++++++++++++ 6 files changed, 80 insertions(+), 6 deletions(-) diff --git a/Dockerfile.rocm b/Dockerfile.rocm index 339c4ecd919..15524a88565 100644 --- a/Dockerfile.rocm +++ b/Dockerfile.rocm @@ -104,7 +104,7 @@ ENV BUILD_MONAI=1 # than no image at all. ARG HIPCIM_INDEX_URL="https://pypi.amd.com/rocm-${ROCM_SERIES}.0/simple/" RUN if [ -n "${HIPCIM_INDEX_URL}" ]; then \ - pip install --no-cache-dir --extra-index-url "${HIPCIM_INDEX_URL}" "amd-hipcim" \ + pip install --no-cache-dir --extra-index-url "${HIPCIM_INDEX_URL}" "amd-hipcim>=26.6.0" \ && python -c "from cucim import CuImage"; \ else \ echo "hipCIM not installed; whole-slide-image (cucim) backends are unavailable."; \ diff --git a/docs/source/installation.md b/docs/source/installation.md index 31c99a794c1..823dd39c134 100644 --- a/docs/source/installation.md +++ b/docs/source/installation.md @@ -77,10 +77,10 @@ pip install monai MONAI supports the extras syntax such as `pip install 'monai[nibabel]'`. The options are ```text -clearml, cucim, cupy, einops, fire, gdown, h5py, huggingface_hub, hyena, ignite, imagecodecs, itk, jsonschema, lmdb, lpips, matplotlib, metrics_reloaded, mlflow, nibabel, nni, nvimgcodec, onnx, openslide, optuna, pandas, pillow, polygraphy, psutil, pyamg, pybind11, pydicom, pynrrd, pynvml, pyyaml, requests, segment_anything, scipy, skimage, tensorboard, tensorboardX, tifffile, torchio, torchvision, tqdm, transformers, zarr +clearml, cucim, cupy, einops, fire, gdown, h5py, huggingface_hub, hyena, ignite, imagecodecs, itk, jsonschema, lmdb, lpips, matplotlib, metrics_reloaded, mlflow, nibabel, nni, nvimgcodec, onnx, openslide, optuna, pandas, pillow, polygraphy, psutil, pyamg, pybind11, pydicom, pynrrd, pynvml, pyyaml, requests, rocm, segment_anything, scipy, skimage, tensorboard, tensorboardX, tifffile, torchio, torchvision, tqdm, transformers, zarr ``` -which correspond to the packages: `clearml`, `cucim` (`cucim-cu12` or `cucim-cu13`), `cupy-cuda13x`, `einops`, `fire`, `gdown`, `h5py`, `huggingface_hub`, `nvsubquadratic`, `omegaconf`, `pytorch-ignite`, `imagecodecs`, `itk`, `jsonschema`, `lmdb`, `lpips`, `matplotlib`, `MetricsReloaded`, `mlflow`, `nibabel`, `nni`, `filelock`, `nvidia-nvimgcodec-cu13`, `onnx`, `onnxruntime`, `onnx_graphsurgeon`, `onnxscript`, `openslide-python`, `openslide-bin`, `optuna`, `pandas`, `pillow`, `polygraphy`, `psutil`, `pyamg`, `pybind11`, `pydicom`, `pynrrd`, `nvidia-ml-py`, `pyyaml`, `requests`, `segment_anything`, `scipy`, `scikit-image`, `tensorboard`, `tensorboardX`, `tifffile`, `torchio`, `torchvision`, `tqdm`, `transformers`, `zarr`. +which correspond to the packages: `clearml`, `cucim` (`cucim-cu12` or `cucim-cu13`), `cupy-cuda13x`, `einops`, `fire`, `gdown`, `h5py`, `huggingface_hub`, `nvsubquadratic`, `omegaconf`, `pytorch-ignite`, `imagecodecs`, `itk`, `jsonschema`, `lmdb`, `lpips`, `matplotlib`, `MetricsReloaded`, `mlflow`, `nibabel`, `nni`, `filelock`, `nvidia-nvimgcodec-cu13`, `onnx`, `onnxruntime`, `onnx_graphsurgeon`, `onnxscript`, `openslide-python`, `openslide-bin`, `optuna`, `pandas`, `pillow`, `polygraphy`, `psutil`, `pyamg`, `pybind11`, `pydicom`, `pynrrd`, `nvidia-ml-py`, `pyyaml`, `requests`, `amd-hipcim`, `segment_anything`, `scipy`, `scikit-image`, `tensorboard`, `tensorboardX`, `tifffile`, `torchio`, `torchvision`, `tqdm`, `transformers`, `zarr`. Almost all of these can be installed together with the `all` option. For development on MONAI, this should be accompanied by `testing` which will install the testing static checking packages. Cupy and `nvimgcodec` are omitted from `all` since the choice between Cuda 12 and 13 versions of the libraries can't be resolved when installing and must be manually installed. @@ -89,6 +89,30 @@ The `nvimgcodec` extra installs GPU-accelerated DICOM decoding for `NvImgCodecPy (`pip install 'monai[nvimgcodec]'`). It is Linux-only in the extra definition; CUDA 13 is the default. CUDA 12 users should install matching `cupy-cuda12x` and `nvidia-nvimgcodec-cu12` wheels. +The `rocm` extra provides the AMD ROCm counterparts of the CUDA-only imaging dependencies: +[`amd-hipcim`](https://rocm.docs.amd.com/projects/hipCIM/en/latest/) (hipCIM), which pulls +`amd-cupy` in transitively. `amd-hipcim` ships the `cucim` Python namespace, so all existing MONAI +code that imports from `cucim` (e.g. `WSIReader` with `backend="cucim"`) works on ROCm without any +code changes. This extra covers MONAI's optional imaging dependencies only: a ROCm build of PyTorch +and the matching ROCm runtime must already be installed, see +[PyTorch on ROCm](https://rocm.docs.amd.com/projects/ai-ecosystem/en/latest/frameworks/pytorch/install.html). +Both `amd-hipcim` and `amd-cupy` are published on AMD's package index rather than PyPI, so an extra +index URL matching your ROCm series is required: + +```bash +pip install --extra-index-url https://pypi.amd.com/rocm-10.0.0/simple/ 'monai[rocm]' +``` + +AMD currently publishes these wheels for CPython 3.12 on x86_64 Linux only; on other interpreters or +architectures the install fails with `No matching distribution found for amd-hipcim`. + +Do not use `monai[all]` on ROCm: the `all` extra includes CUDA-only sub-extras (`cucim`, `cupy`, +`nvimgcodec`, `pynvml`, `hyena`) that pull in `cupy-cuda13x`, `cuda-toolkit`, and a cascade of +`nvidia-*` packages that will not work on ROCm. `Dockerfile.rocm` in the repo root shows how to +install the full optional dependency set on ROCm while filtering those packages — follow it as the +authoritative reference for the complete environment setup (library paths, compiler flags, +`rocm-sdk init`, and the full container recipe). + The `hyena` extra pulls in [`nvsubquadratic`](https://github.com/NVIDIA-BioNeMo/nvSubquadratic), required by `HyenaNDUNETR` / `HyenaMixer` / `HyenaTransformerBlock` (subquadratic O(N log N) alternatives to windowed self-attention). Install with @@ -333,8 +357,10 @@ MONAI itself: ```bash git clone https://github.com/Project-MONAI/MONAI.git cd MONAI/ -python monai/config/print_dependencies.py \* > requirements.txt +python monai/config/print_dependencies.py all testing > requirements.txt pip install -r requirements.txt +# Note: `all testing` is used rather than `*` because `*` now also includes the `rocm` extra, +# which requires AMD's package index and is not resolvable from PyPI. ``` To install all optional dependencies with `conda` based on MONAI development environment settings (`environment-dev.yml`; diff --git a/monai/config/check_env.py b/monai/config/check_env.py index f00c5b8fcd3..372f0834720 100644 --- a/monai/config/check_env.py +++ b/monai/config/check_env.py @@ -123,7 +123,11 @@ def check_torch_cuda(): """ import torch - fprint("CUDA version:", torch.version.cuda) + # `torch.version.cuda` is None on a ROCm build, where the toolkit version is `torch.version.hip`. + if torch.version.hip: + fprint("HIP version:", torch.version.hip) + else: + fprint("CUDA version:", torch.version.cuda) try: dcount = torch.cuda.device_count() diff --git a/monai/config/deviceconfig.py b/monai/config/deviceconfig.py index 6a0d2378aa7..7ae1cbceeca 100644 --- a/monai/config/deviceconfig.py +++ b/monai/config/deviceconfig.py @@ -214,7 +214,12 @@ def get_gpu_info() -> OrderedDict: _dict_append(output, "Has CUDA", lambda: bool(torch.cuda.is_available())) if output["Has CUDA"]: - _dict_append(output, "CUDA version", lambda: torch.version.cuda) + # On a ROCm build `torch.version.cuda` is None and `torch.version.hip` carries the toolkit + # version. Reporting "CUDA version: None" is misleading, so the key name switches with the build. + if torch.version.hip: + _dict_append(output, "HIP version", lambda: torch.version.hip) + else: + _dict_append(output, "CUDA version", lambda: torch.version.cuda) cudnn_ver = torch.backends.cudnn.version() _dict_append(output, "cuDNN enabled", lambda: bool(cudnn_ver)) _dict_append(output, "NVIDIA_TF32_OVERRIDE", os.environ.get("NVIDIA_TF32_OVERRIDE")) diff --git a/pyproject.toml b/pyproject.toml index aa434b364fa..ee5d6744474 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -155,6 +155,10 @@ pynrrd = ["pynrrd"] pynvml = ["nvidia-ml-py"] pyyaml = ["pyyaml"] requests = ["requests"] +# hipCIM is the ROCm counterpart of `cucim`; it pulls `amd-cupy` in transitively. Omitted from `all`: +# the real wheels live on AMD's package index, not PyPI, so an `--extra-index-url` is needed (see +# installation.md). The floor pins the first AMD-index release, skipping the PyPI placeholder stub. +rocm = ["amd-hipcim>=26.6.0; platform_system == 'Linux'"] # per #5882 this requirement must be on one line to get removed by sed in weekly-preview.yml segment_anything = ["segment_anything @ git+https://github.com/facebookresearch/segment-anything.git@6fdee8f2727f4506cfbbe553e23b895e27956588"] scipy = ["scipy>=1.12.0"] diff --git a/tests/config/test_print_info.py b/tests/config/test_print_info.py index da91c431498..5a67f15ddc4 100644 --- a/tests/config/test_print_info.py +++ b/tests/config/test_print_info.py @@ -11,10 +11,13 @@ from __future__ import annotations +import sys import unittest from io import StringIO +from unittest.mock import patch from monai.config import print_debug_info +from monai.config.deviceconfig import get_gpu_info class TestPrintInfo(unittest.TestCase): @@ -24,6 +27,38 @@ def test_print_info(self): print_debug_info(file=out) self.assertGreater(out.tell(), 0) + @patch("torch.backends.cudnn.version", lambda: 0) + @patch("torch.cuda.device_count", lambda: 0) + @patch("torch.cuda.is_available", lambda: True) + @patch("torch.version.hip", "7.0.0") + def test_hip_version(self): + """A ROCm build reports the HIP version, where `torch.version.cuda` would be None.""" + output = get_gpu_info() + self.assertEqual(output["HIP version"], "7.0.0") + self.assertNotIn("CUDA version", output) + + @patch("torch.backends.cudnn.version", lambda: 0) + @patch("torch.cuda.device_count", lambda: 0) + @patch("torch.cuda.is_available", lambda: True) + @patch("torch.version.hip", None) + @patch("torch.version.cuda", "12.4") + def test_cuda_version(self): + """A CUDA build is unaffected and still reports the CUDA version.""" + output = get_gpu_info() + self.assertEqual(output["CUDA version"], "12.4") + self.assertNotIn("HIP version", output) + + @patch("torch.version.hip", "7.0.0") + def test_check_env_hip_version(self): + """check_env.check_torch_cuda() prints 'HIP version:' on a ROCm build.""" + import monai.config.check_env as ce + + buf = StringIO() + with patch.object(sys, "stdout", buf): + ce.check_torch_cuda() + self.assertIn("HIP version:", buf.getvalue()) + self.assertNotIn("CUDA version:", buf.getvalue()) + if __name__ == "__main__": unittest.main()