diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 1dcc7675f0..003a746de4 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -34,7 +34,7 @@ jobs: which python python -m pip install --upgrade pip wheel python -m pip uninstall -y torch torchvision - python -m pip install torch==1.7.0 torchvision==0.8.1 + python -m pip install torch==1.7.1 torchvision==0.8.2 python -m pip install -r requirements-dev.txt - name: Run integration tests run: | diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml index 4db50b2723..8e92ea0ed7 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonapp.yml @@ -80,13 +80,13 @@ jobs: - if: runner.os == 'windows' name: Install torch cpu from pytorch.org (Windows only) run: | - python -m pip install torch==1.7.0+cpu torchvision==0.8.1+cpu -f https://download.pytorch.org/whl/torch_stable.html + python -m pip install torch==1.7.1+cpu torchvision==0.8.2+cpu -f https://download.pytorch.org/whl/torch_stable.html # min. requirements for windows instances python -c "f=open('requirements-dev.txt', 'r'); txt=f.readlines(); f.close(); print(txt); f=open('requirements-dev.txt', 'w'); f.writelines(txt[1:12]); f.close()" - name: Install the dependencies run: | - python -m pip install torch==1.7.0 - python -m pip install torchvision==0.8.1 + python -m pip install torch==1.7.1 + python -m pip install torchvision==0.8.2 cat "requirements-dev.txt" python -m pip install -r requirements-dev.txt python -m pip list @@ -108,7 +108,7 @@ jobs: fail-fast: false matrix: os: [windows-latest, macOS-latest, ubuntu-latest] - timeout-minutes: 20 + timeout-minutes: 40 steps: - uses: actions/checkout@v2 - name: Set up Python 3.8 @@ -134,11 +134,11 @@ jobs: - if: runner.os == 'windows' name: Install torch cpu from pytorch.org (Windows only) run: | - python -m pip install torch==1.7.0+cpu -f https://download.pytorch.org/whl/torch_stable.html + python -m pip install torch==1.7.1+cpu -f https://download.pytorch.org/whl/torch_stable.html - name: Install the dependencies run: | # min. requirements - python -m pip install torch==1.7.0 + python -m pip install torch==1.7.1 python -m pip install -r requirements-min.txt python -m pip list BUILD_MONAI=0 python setup.py develop # no compile of extensions @@ -173,7 +173,7 @@ jobs: pytorch: "-h" base: "nvcr.io/nvidia/pytorch:20.07-py3" - environment: PT17+CUDA102 - pytorch: "torch==1.7.0 torchvision==0.8.1" + pytorch: "torch==1.7.1 torchvision==0.8.2" base: "nvcr.io/nvidia/cuda:10.2-devel-ubuntu18.04" - environment: PT17+CUDA110 # we explicitly set pytorch to -h to avoid pip install error diff --git a/.github/workflows/setupapp.yml b/.github/workflows/setupapp.yml index a3a1745977..ed5d560861 100644 --- a/.github/workflows/setupapp.yml +++ b/.github/workflows/setupapp.yml @@ -36,7 +36,7 @@ jobs: which python python -m pip install --upgrade pip wheel python -m pip uninstall -y torch torchvision - python -m pip install torch==1.7.0 torchvision==0.8.1 + python -m pip install torch==1.7.1 torchvision==0.8.2 python -m pip install -r requirements-dev.txt - name: Run unit tests report coverage run: | @@ -82,7 +82,7 @@ jobs: - name: Install the dependencies run: | python -m pip install --upgrade pip wheel - python -m pip install torch==1.7.0 torchvision==0.8.1 + python -m pip install torch==1.7.1 torchvision==0.8.2 python -m pip install -r requirements-dev.txt - name: Run quick tests CPU ubuntu run: | @@ -151,7 +151,7 @@ jobs: run: | docker build -t localhost:5000/local_monai:latest -f Dockerfile . docker push localhost:5000/local_monai:latest - sed -i '/flake/d' requirements-dev.txt + sed -i '/flake/d' requirements-dev.txt docker build -t projectmonai/monai:latest -f Dockerfile . docker login -u projectmonai -p ${{ secrets.DOCKER_PW }} docker push projectmonai/monai:latest diff --git a/docs/source/utils.rst b/docs/source/utils.rst index 6a03529c30..e0b993da60 100644 --- a/docs/source/utils.rst +++ b/docs/source/utils.rst @@ -26,3 +26,8 @@ Misc ---- .. automodule:: monai.utils.misc :members: + +Profiling +--------- +.. automodule:: monai.utils.profiling + :members: diff --git a/monai/config/deviceconfig.py b/monai/config/deviceconfig.py index 1bb2bb7907..355069f941 100644 --- a/monai/config/deviceconfig.py +++ b/monai/config/deviceconfig.py @@ -100,14 +100,6 @@ def set_visible_devices(*dev_inds): os.environ["CUDA_VISIBLE_DEVICES"] = ",".join(map(str, dev_inds)) -def get_torch_version_tuple(): - """ - Returns: - tuple of ints represents the pytorch major/minor version. - """ - return tuple((int(x) for x in torch.__version__.split(".")[:2])) - - def _dict_append(in_dict, key, fn): try: in_dict[key] = fn() diff --git a/monai/networks/layers/simplelayers.py b/monai/networks/layers/simplelayers.py index bd800a7c91..4860b2862c 100644 --- a/monai/networks/layers/simplelayers.py +++ b/monai/networks/layers/simplelayers.py @@ -17,12 +17,11 @@ from torch import nn from torch.autograd import Function -from monai.config import get_torch_version_tuple from monai.networks.layers.convutils import gaussian_1d, same_padding -from monai.utils import InvalidPyTorchVersionError, SkipMode, ensure_tuple_rep, optional_import +from monai.utils import PT_BEFORE_1_7, InvalidPyTorchVersionError, SkipMode, ensure_tuple_rep, optional_import _C, _ = optional_import("monai._C") -if tuple(int(s) for s in torch.__version__.split(".")[0:2]) >= (1, 7): +if not PT_BEFORE_1_7: fft, _ = optional_import("torch.fft") __all__ = ["SkipConnection", "Flatten", "GaussianFilter", "LLTM", "Reshape", "separable_filtering", "HilbertTransform"] @@ -145,7 +144,7 @@ class HilbertTransform(nn.Module): def __init__(self, axis: int = 2, n: Union[int, None] = None) -> None: - if get_torch_version_tuple() < (1, 7): + if PT_BEFORE_1_7: raise InvalidPyTorchVersionError("1.7.0", self.__class__.__name__) super().__init__() diff --git a/monai/transforms/intensity/array.py b/monai/transforms/intensity/array.py index b1b11fe6f4..58eb0da2a7 100644 --- a/monai/transforms/intensity/array.py +++ b/monai/transforms/intensity/array.py @@ -20,11 +20,10 @@ import numpy as np import torch -from monai.config import get_torch_version_tuple from monai.networks.layers import GaussianFilter, HilbertTransform from monai.transforms.compose import Randomizable, Transform from monai.transforms.utils import rescale_array -from monai.utils import InvalidPyTorchVersionError, dtype_torch_to_numpy, ensure_tuple_size +from monai.utils import PT_BEFORE_1_7, InvalidPyTorchVersionError, dtype_torch_to_numpy, ensure_tuple_size class RandGaussianNoise(Randomizable, Transform): @@ -524,7 +523,7 @@ class DetectEnvelope(Transform): def __init__(self, axis: int = 1, n: Union[int, None] = None) -> None: - if get_torch_version_tuple() < (1, 7): + if PT_BEFORE_1_7: raise InvalidPyTorchVersionError("1.7.0", self.__class__.__name__) if axis < 0: diff --git a/monai/utils/misc.py b/monai/utils/misc.py index ef688174f1..020884bbcc 100644 --- a/monai/utils/misc.py +++ b/monai/utils/misc.py @@ -12,7 +12,6 @@ import collections.abc import itertools import random -import time from ast import literal_eval from distutils.util import strtobool from typing import Any, Callable, Optional, Sequence, Tuple, Union @@ -287,23 +286,3 @@ def dtype_torch_to_numpy(dtype): def dtype_numpy_to_torch(dtype): """Convert a numpy dtype to its torch equivalent.""" return _np_to_torch_dtype[dtype] - - -class PerfContext: - """ - Context manager for tracking how much time is spent within context blocks. This uses `time.perf_counter` to - accumulate the total amount of time in seconds in the attribute `total_time` over however many context blocks - the object is used in. - """ - - def __init__(self): - self.total_time = 0 - self.start_time = None - - def __enter__(self): - self.start_time = time.perf_counter() - return self - - def __exit__(self, exc_type, exc_value, exc_traceback): - self.total_time += time.perf_counter() - self.start_time - self.start_time = None diff --git a/monai/utils/module.py b/monai/utils/module.py index 039c4f9fb5..4bc9a6d63b 100644 --- a/monai/utils/module.py +++ b/monai/utils/module.py @@ -16,6 +16,8 @@ from re import match from typing import Any, Callable, List, Sequence, Tuple, Union +import torch + from .misc import ensure_tuple OPTIONAL_IMPORT_MSG_FMT = "{}" @@ -31,6 +33,8 @@ "get_full_type_name", "has_option", "get_package_version", + "get_torch_version_tuple", + "PT_BEFORE_1_7", ] @@ -264,3 +268,22 @@ def get_package_version(dep_name, default="NOT INSTALLED or UNKNOWN VERSION."): del dep del sys.modules[dep_name] return dep_ver + + +def get_torch_version_tuple(): + """ + Returns: + tuple of ints represents the pytorch major/minor version. + """ + return tuple((int(x) for x in torch.__version__.split(".")[:2])) + + +PT_BEFORE_1_7 = True +ver, has_ver = optional_import("pkg_resources", name="parse_version") +try: + if has_ver: + PT_BEFORE_1_7 = ver(torch.__version__) < ver("1.7") + else: + PT_BEFORE_1_7 = get_torch_version_tuple() < (1, 7) +except (AttributeError, TypeError): + pass diff --git a/monai/utils/profiling.py b/monai/utils/profiling.py index a8d6e2c630..bcdc0357c4 100644 --- a/monai/utils/profiling.py +++ b/monai/utils/profiling.py @@ -14,7 +14,7 @@ import torch -__all__ = ["torch_profiler_full", "torch_profiler_time_cpu_gpu", "torch_profiler_time_end_to_end"] +__all__ = ["torch_profiler_full", "torch_profiler_time_cpu_gpu", "torch_profiler_time_end_to_end", "PerfContext"] def torch_profiler_full(func): @@ -88,3 +88,23 @@ def wrapper(*args, **kwargs): return result return wrapper + + +class PerfContext: + """ + Context manager for tracking how much time is spent within context blocks. This uses `time.perf_counter` to + accumulate the total amount of time in seconds in the attribute `total_time` over however many context blocks + the object is used in. + """ + + def __init__(self): + self.total_time = 0 + self.start_time = None + + def __enter__(self): + self.start_time = time.perf_counter() + return self + + def __exit__(self, exc_type, exc_value, exc_traceback): + self.total_time += time.perf_counter() - self.start_time + self.start_time = None diff --git a/tests/min_tests.py b/tests/min_tests.py index e22d94bc57..cd855aa543 100644 --- a/tests/min_tests.py +++ b/tests/min_tests.py @@ -32,6 +32,7 @@ def run_testsuit(): "test_cachedataset", "test_cachedataset_parallel", "test_dataset", + "test_detect_envelope", "test_iterable_dataset", "test_ensemble_evaluator", "test_handler_checkpoint_loader", @@ -51,6 +52,7 @@ def run_testsuit(): "test_handler_validation", "test_hausdorff_distance", "test_header_correct", + "test_hilbert_transform", "test_img2tensorboard", "test_integration_segmentation_3d", "test_integration_sliding_window", diff --git a/tests/test_detect_envelope.py b/tests/test_detect_envelope.py index aec014731b..cbd281f6e8 100644 --- a/tests/test_detect_envelope.py +++ b/tests/test_detect_envelope.py @@ -156,9 +156,8 @@ def test_no_fft_module_error(self): @SkipIfAtLeastPyTorchVersion((1, 7)) class TestDetectEnvelopeInvalidPyTorch(unittest.TestCase): def test_invalid_pytorch_error(self): - with self.assertRaises(InvalidPyTorchVersionError) as cm: + with self.assertRaisesRegexp(InvalidPyTorchVersionError, "version"): DetectEnvelope() - self.assertEqual("DetectEnvelope requires PyTorch version 1.7.0 or later", str(cm.exception)) if __name__ == "__main__": diff --git a/tests/test_highresnet.py b/tests/test_highresnet.py index 6a4b129588..0afa4b1a9b 100644 --- a/tests/test_highresnet.py +++ b/tests/test_highresnet.py @@ -53,7 +53,7 @@ def test_shape(self, input_param, input_shape, expected_shape): result = net.forward(torch.randn(input_shape).to(device)) self.assertEqual(result.shape, expected_shape) - @TimedCall(seconds=100, force_quit=True) + @TimedCall(seconds=200, force_quit=True) def test_script(self): input_param, input_shape, expected_shape = TEST_CASE_1 net = HighResNet(**input_param) diff --git a/tests/test_hilbert_transform.py b/tests/test_hilbert_transform.py index 1e9e0e660f..1099468102 100644 --- a/tests/test_hilbert_transform.py +++ b/tests/test_hilbert_transform.py @@ -217,9 +217,8 @@ def test_no_fft_module_error(self): @SkipIfAtLeastPyTorchVersion((1, 7)) class TestHilbertTransformInvalidPyTorch(unittest.TestCase): def test_invalid_pytorch_error(self): - with self.assertRaises(InvalidPyTorchVersionError) as cm: + with self.assertRaisesRegex(InvalidPyTorchVersionError, "version"): HilbertTransform() - self.assertEqual("HilbertTransform requires PyTorch version 1.7.0 or later", str(cm.exception)) if __name__ == "__main__": diff --git a/tests/test_integration_workflows.py b/tests/test_integration_workflows.py index 2a0b5d5d86..8e96947ccb 100644 --- a/tests/test_integration_workflows.py +++ b/tests/test_integration_workflows.py @@ -296,7 +296,7 @@ def train_and_infer(self, idx=0): def test_training(self): repeated = [] - test_rounds = 3 if monai.config.get_torch_version_tuple() >= (1, 6) else 2 + test_rounds = 3 if monai.utils.module.get_torch_version_tuple() >= (1, 6) else 2 for i in range(test_rounds): results = self.train_and_infer(idx=i) repeated.append(results) @@ -308,7 +308,7 @@ def test_training(self): daemon=False, ) def test_timing(self): - if monai.config.get_torch_version_tuple() >= (1, 6): + if monai.utils.module.get_torch_version_tuple() >= (1, 6): self.train_and_infer(idx=2) diff --git a/tests/utils.py b/tests/utils.py index 763ddaa9b2..50c159053e 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -28,11 +28,12 @@ import torch import torch.distributed as dist -from monai.config import get_torch_version_tuple from monai.data import create_test_image_2d, create_test_image_3d from monai.utils import ensure_tuple, optional_import, set_determinism +from monai.utils.module import get_torch_version_tuple nib, _ = optional_import("nibabel") +ver, has_pkg_res = optional_import("pkg_resources", name="parse_version") quick_test_var = "QUICKTEST" @@ -99,7 +100,10 @@ class SkipIfBeforePyTorchVersion(object): def __init__(self, pytorch_version_tuple): self.min_version = pytorch_version_tuple - self.version_too_old = get_torch_version_tuple() < self.min_version + if has_pkg_res: + self.version_too_old = ver(torch.__version__) < ver(".".join(map(str, self.min_version))) + else: + self.version_too_old = get_torch_version_tuple() < self.min_version def __call__(self, obj): return unittest.skipIf( @@ -113,7 +117,10 @@ class SkipIfAtLeastPyTorchVersion(object): def __init__(self, pytorch_version_tuple): self.max_version = pytorch_version_tuple - self.version_too_new = get_torch_version_tuple() >= self.max_version + if has_pkg_res: + self.version_too_new = ver(torch.__version__) >= ver(".".join(map(str, self.max_version))) + else: + self.version_too_new = get_torch_version_tuple() >= self.max_version def __call__(self, obj): return unittest.skipIf(