From 44752e65df122cfd70e05804c45d00a149c2f4a4 Mon Sep 17 00:00:00 2001 From: Boris Fomitchev Date: Fri, 11 Oct 2024 23:35:34 -0700 Subject: [PATCH 01/26] Removed CPU randn() from schedulers Signed-off-by: Boris Fomitchev --- monai/networks/schedulers/ddim.py | 2 +- monai/networks/schedulers/ddpm.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/monai/networks/schedulers/ddim.py b/monai/networks/schedulers/ddim.py index 2a0121d063..50a680336d 100644 --- a/monai/networks/schedulers/ddim.py +++ b/monai/networks/schedulers/ddim.py @@ -220,7 +220,7 @@ def step( if eta > 0: # randn_like does not support generator https://github.com/pytorch/pytorch/issues/27072 device: torch.device = torch.device(model_output.device if torch.is_tensor(model_output) else "cpu") - noise = torch.randn(model_output.shape, dtype=model_output.dtype, generator=generator).to(device) + noise = torch.randn(model_output.shape, dtype=model_output.dtype, generator=generator, device=device) variance = self._get_variance(timestep, prev_timestep) ** 0.5 * eta * noise pred_prev_sample = pred_prev_sample + variance diff --git a/monai/networks/schedulers/ddpm.py b/monai/networks/schedulers/ddpm.py index 93ad833031..d64e11d379 100644 --- a/monai/networks/schedulers/ddpm.py +++ b/monai/networks/schedulers/ddpm.py @@ -241,8 +241,12 @@ def step( variance = 0 if timestep > 0: noise = torch.randn( - model_output.size(), dtype=model_output.dtype, layout=model_output.layout, generator=generator - ).to(model_output.device) + model_output.size(), + dtype=model_output.dtype, + layout=model_output.layout, + generator=generator, + device=model_output.device, + ) variance = (self._get_variance(timestep, predicted_variance=predicted_variance) ** 0.5) * noise pred_prev_sample = pred_prev_sample + variance From 1893375e8ec5cdd52c99d431e607feb7dbd80802 Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Tue, 15 Oct 2024 11:05:36 +0800 Subject: [PATCH 02/26] workaround for #8149 Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 6d0ccd378a..72654d3534 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -22,7 +22,7 @@ isort>=5.1 ruff pytype>=2020.6.1; platform_system != "Windows" types-setuptools -mypy>=1.5.0 +mypy>=1.5.0, <1.12.0 ninja torchvision psutil From 1c126a9464e1187212eb3ceca36a6d3508e39192 Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Tue, 15 Oct 2024 22:17:17 +0800 Subject: [PATCH 03/26] add `PythonicWorkflow` Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/__init__.py | 2 +- monai/bundle/workflows.py | 109 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/monai/bundle/__init__.py b/monai/bundle/__init__.py index a4a2176f14..3f3c8d545e 100644 --- a/monai/bundle/__init__.py +++ b/monai/bundle/__init__.py @@ -43,4 +43,4 @@ MACRO_KEY, load_bundle_config, ) -from .workflows import BundleWorkflow, ConfigWorkflow +from .workflows import BundleWorkflow, ConfigWorkflow, PythonicWorkflow diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index d728d7d930..1b0690a6be 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -225,6 +225,115 @@ def check_properties(self) -> list[str] | None: return [n for n, p in self.properties.items() if p.get(BundleProperty.REQUIRED, False) and not hasattr(self, n)] +class PythonicWorkflow(BundleWorkflow): + """ + Base class for the workflow specification in bundle, it can be a training, evaluation or inference workflow. + It defines the basic interfaces for the bundle workflow behavior: `initialize`, `run`, `finalize`, etc. + And also provides the interface to get / set public properties to interact with a bundle workflow. + + Args: + workflow_type: specifies the workflow type: "train" or "training" for a training workflow, + or "infer", "inference", "eval", "evaluation" for a inference workflow, + other unsupported string will raise a ValueError. + default to `train` for train workflow. + workflow: specifies the workflow type: "train" or "training" for a training workflow, + or "infer", "inference", "eval", "evaluation" for a inference workflow, + other unsupported string will raise a ValueError. + default to `None` for common workflow. + properties_path: the path to the JSON file of properties. + meta_file: filepath of the metadata file, if this is a list of file paths, their contents will be merged in order. + logging_file: config file for `logging` module in the program. for more details: + https://docs.python.org/3/library/logging.config.html#logging.config.fileConfig. + + """ + + supported_train_type: tuple = ("train", "training") + supported_infer_type: tuple = ("infer", "inference", "eval", "evaluation") + + @deprecated_arg( + "workflow", + since="1.2", + removed="1.5", + new_name="workflow_type", + msg_suffix="please use `workflow_type` instead.", + ) + def __init__( + self, + workflow_type: str | None = None, + workflow: str | None = None, + properties_path: PathLike | None = None, + meta_file: str | Sequence[str] | None = None, + logging_file: str | None = None, + ): + super().__init__(workflow_type=workflow_type, workflow=workflow, properties_path=properties_path, meta_file=meta_file, logging_file=logging_file) + self._props = {} + self._set_props = {} + + def initialize(self, *args: Any, **kwargs: Any) -> Any: + """ + Initialize the bundle workflow before running. + """ + self._props = {} + + @abstractmethod + def run(self, *args: Any, **kwargs: Any) -> Any: + """ + Run the bundle workflow, it can be a training, evaluation or inference. + + """ + raise NotImplementedError() + + @abstractmethod + def finalize(self, *args: Any, **kwargs: Any) -> Any: + """ + Finalize step after the running of bundle workflow. + + """ + raise NotImplementedError() + + def _get_property(self, name: str, property: dict) -> Any: + """ + With specified property name and information, get the expected property value. + If the property is already generated, return from the bucket directly. + If user explicitly set the property, return it directly. + Otherwise, generate the expected property as a class private property with prefix "_". + + Args: + name: the name of target property. + property: other information for the target property, defined in `TrainProperties` or `InferProperties`. + """ + value = None + if name in self._set_props: + value = self._set_props[name] + self._props[name] = value + elif name in self._props: + value = self._props[name] + else: + try: + value = getattr(self, f"get_{name}")() + except AttributeError: + if property[BundleProperty.REQUIRED]: + raise ValueError( + f"unsupported property '{name}' is required in the bundle properties," + f"need to implement a method 'get_{name}' to provide the property." + ) + self._props[name] = value + return value + + def _set_property(self, name: str, property: dict, value: Any) -> Any: + """ + With specified property name and information, set value for the expected property. + Stores user-reset initialized objects that should not be re-initialized. + + Args: + name: the name of target property. + property: other information for the target property, defined in `TrainProperties` or `InferProperties`. + value: value to set for the property. + + """ + self._set_props[name] = value + + class ConfigWorkflow(BundleWorkflow): """ Specification for the config-based bundle workflow. From 7228989ea1720e07dadaee222b3819576a71c24e Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Tue, 15 Oct 2024 22:19:55 +0800 Subject: [PATCH 04/26] minor update Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/workflows.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index 1b0690a6be..fa2223261d 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -227,7 +227,7 @@ def check_properties(self) -> list[str] | None: class PythonicWorkflow(BundleWorkflow): """ - Base class for the workflow specification in bundle, it can be a training, evaluation or inference workflow. + Base class for the pythonic workflow specification in bundle, it can be a training, evaluation or inference workflow. It defines the basic interfaces for the bundle workflow behavior: `initialize`, `run`, `finalize`, etc. And also provides the interface to get / set public properties to interact with a bundle workflow. From cf31a38720e53aade0efb87b89151c0e538ce8ff Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Thu, 17 Oct 2024 15:51:17 +0800 Subject: [PATCH 05/26] support for multipy properties file Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/workflows.py | 52 +++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index fa2223261d..6f1e761e4d 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -96,29 +96,39 @@ def __init__( meta_file = None workflow_type = workflow if workflow is not None else workflow_type - if workflow_type is None and properties_path is None: - self.properties = copy(MetaProperties) - self.workflow_type = None - self.meta_file = meta_file - return + if workflow_type is not None: + if workflow_type.lower() in self.supported_train_type: + workflow_type = "train" + elif workflow_type.lower() in self.supported_infer_type: + workflow_type = "infer" + else: + raise ValueError(f"Unsupported workflow type: '{workflow_type}'.") + if properties_path is not None: properties_path = Path(properties_path) if not properties_path.is_file(): raise ValueError(f"Property file {properties_path} does not exist.") + if workflow_type is None: + workflow_type = "train" + logger.info("No workflow type specified, default to 'train' for property file loading.") with open(properties_path) as json_file: - self.properties = json.load(json_file) - self.workflow_type = None - self.meta_file = meta_file - return - if workflow_type.lower() in self.supported_train_type: # type: ignore[union-attr] - self.properties = {**TrainProperties, **MetaProperties} - self.workflow_type = "train" - elif workflow_type.lower() in self.supported_infer_type: # type: ignore[union-attr] - self.properties = {**InferProperties, **MetaProperties} - self.workflow_type = "infer" + try: + self.properties = json.load(json_file)[workflow_type] + print(self.properties) + except: + raise ValueError(f"{self.workflow_type} not find in property file {properties_path}") else: - raise ValueError(f"Unsupported workflow type: '{workflow_type}'.") + if workflow_type == "train": + self.properties = {**TrainProperties, **MetaProperties} + elif workflow_type == "infer": + self.properties = {**InferProperties, **MetaProperties} + elif workflow_type is None: + self.properties = copy(MetaProperties) + logger.info("No workflow type and property file specified, default to 'meta' properties.") + else: + raise ValueError(f"Unsupported workflow type: '{workflow_type}'.") + self.workflow_type = workflow_type self.meta_file = meta_file @abstractmethod @@ -262,12 +272,22 @@ def __init__( workflow_type: str | None = None, workflow: str | None = None, properties_path: PathLike | None = None, + config_file: str | Sequence[str] | None = None, meta_file: str | Sequence[str] | None = None, logging_file: str | None = None, + **override: Any ): super().__init__(workflow_type=workflow_type, workflow=workflow, properties_path=properties_path, meta_file=meta_file, logging_file=logging_file) self._props = {} self._set_props = {} + + self.parser = ConfigParser() + self.parser.read_config(f=config_file) + if self.meta_file is not None: + self.parser.read_meta(f=self.meta_file) + + # the rest key-values in the _args are to override config content + self.parser.update(pairs=override) def initialize(self, *args: Any, **kwargs: Any) -> Any: """ From b94b2a0e9668d2fa20a1a8fe97d52c096d952225 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 07:52:00 +0000 Subject: [PATCH 06/26] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/bundle/workflows.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index 6f1e761e4d..b3037d4e03 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -280,7 +280,7 @@ def __init__( super().__init__(workflow_type=workflow_type, workflow=workflow, properties_path=properties_path, meta_file=meta_file, logging_file=logging_file) self._props = {} self._set_props = {} - + self.parser = ConfigParser() self.parser.read_config(f=config_file) if self.meta_file is not None: From 7248c2be5976603d55a88b6189880420a1c48060 Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Fri, 18 Oct 2024 10:36:46 +0800 Subject: [PATCH 07/26] minor fix Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/workflows.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index 6f1e761e4d..0acdbe46c3 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -282,7 +282,8 @@ def __init__( self._set_props = {} self.parser = ConfigParser() - self.parser.read_config(f=config_file) + if config_file is not None: + self.parser.read_config(f=config_file) if self.meta_file is not None: self.parser.read_meta(f=self.meta_file) From f99763d7c2183aa7759069c5229f21c9aa768197 Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:22:39 +0800 Subject: [PATCH 08/26] add test case Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/workflows.py | 13 +- tests/nonconfig_workflow.py | 63 ++++++++- tests/test_bundle_workflow.py | 34 ++++- tests/testing_data/fl_infer_properties.json | 139 +++++++++++--------- 4 files changed, 175 insertions(+), 74 deletions(-) diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index 29652fbc93..ebc4fe7139 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -114,8 +114,10 @@ def __init__( logger.info("No workflow type specified, default to 'train' for property file loading.") with open(properties_path) as json_file: try: - self.properties = json.load(json_file)[workflow_type] - print(self.properties) + properties = json.load(json_file) + self.properties = properties[workflow_type] + if "meta" in properties: + self.properties.update(properties["meta"]) except: raise ValueError(f"{self.workflow_type} not find in property file {properties_path}") else: @@ -278,10 +280,10 @@ def __init__( logging_file: str | None = None, **override: Any ): + meta_file = str(Path(os.getcwd()) / "metadata.json") if meta_file is None else meta_file super().__init__(workflow_type=workflow_type, workflow=workflow, properties_path=properties_path, meta_file=meta_file, logging_file=logging_file) self._props = {} self._set_props = {} - self.parser = ConfigParser() if config_file is not None: self.parser.read_config(f=config_file) @@ -325,11 +327,15 @@ def _get_property(self, name: str, property: dict) -> Any: property: other information for the target property, defined in `TrainProperties` or `InferProperties`. """ value = None + id = self.properties.get(name, None).get(BundlePropertyConfig.ID, None) + print(self.parser.config.keys(), self.parser.config["_meta_"], '*********') if name in self._set_props: value = self._set_props[name] self._props[name] = value elif name in self._props: value = self._props[name] + elif name in self.parser.config["_meta_"]: + value = self.parser.__getitem__(id) else: try: value = getattr(self, f"get_{name}")() @@ -454,7 +460,6 @@ def __init__( self.parser.read_config(f=config_file) if self.meta_file is not None: self.parser.read_meta(f=self.meta_file) - # the rest key-values in the _args are to override config content self.parser.update(pairs=override) self.init_id = init_id diff --git a/tests/nonconfig_workflow.py b/tests/nonconfig_workflow.py index b2c44c12c6..f1f5cbcd10 100644 --- a/tests/nonconfig_workflow.py +++ b/tests/nonconfig_workflow.py @@ -13,7 +13,7 @@ import torch -from monai.bundle import BundleWorkflow +from monai.bundle import BundleWorkflow, PythonicWorkflow from monai.data import DataLoader, Dataset from monai.engines import SupervisedEvaluator from monai.inferers import SlidingWindowInferer @@ -26,8 +26,9 @@ LoadImaged, SaveImaged, ScaleIntensityd, + ScaleIntensityRanged, ) -from monai.utils import BundleProperty, set_determinism +from monai.utils import BundleProperty, set_determinism, CommonKeys class NonConfigWorkflow(BundleWorkflow): @@ -176,3 +177,61 @@ def _set_property(self, name, property, value): self._numpy_version = value elif property[BundleProperty.REQUIRED]: raise ValueError(f"unsupported property '{name}' is required in the bundle properties.") + + +class PythonicWorkflowImpl(PythonicWorkflow): + """ + Test class simulates the bundle workflow defined by Python script directly. + """ + + def __init__(self, workflow_type: str = "inference", config_file: str | None = None, properties_path: str | None = None, meta_file: str | None = None): + super().__init__(workflow_type=workflow_type, properties_path=properties_path, config_file=config_file, meta_file=meta_file) + self.dataflow = {} + + def initialize(self): + self.net = UNet( + spatial_dims=3, + in_channels=1, + out_channels=2, + channels=(16, 32, 64, 128), + strides=(2, 2, 2), + num_res_units=2, + ).to(self.device) + preprocessing = Compose( + [ + EnsureChannelFirstd(keys=["image"]), + ScaleIntensityd(keys="image"), + ScaleIntensityRanged(keys="image", a_min=-57, a_max=164, b_min=0.0, b_max=1.0, clip=True), + ] + ) + self.dataset = Dataset( + data=[self.dataflow], + transform=preprocessing, + ) + self.postprocessing = Compose( + [ + Activationsd(keys="pred", softmax=True), + AsDiscreted(keys="pred", argmax=True), + ] + ) + self.inferer = SlidingWindowInferer(roi_size=self.parser.roi_size, sw_batch_size=1, overlap=0) + + def run(self): + data = self.dataset[0] + inputs = data[CommonKeys.IMAGE].unsqueeze(0).to(self.device) + self.net.eval() + with torch.no_grad(): + data[CommonKeys.PRED] = self.inferer(inputs, self.net) + self.dataflow.update({CommonKeys.PRED: self.postprocessing(data)[CommonKeys.PRED]}) + + def finalize(self): + pass + + def get_bundle_root(self): + return "." + + def get_device(self): + return torch.device("cuda" if torch.cuda.is_available() else "cpu") + + def get_inferer(self): + return self.inferer diff --git a/tests/test_bundle_workflow.py b/tests/test_bundle_workflow.py index 1727fcdf53..38fe2ae824 100644 --- a/tests/test_bundle_workflow.py +++ b/tests/test_bundle_workflow.py @@ -22,12 +22,12 @@ import torch from parameterized import parameterized -from monai.bundle import ConfigWorkflow +from monai.bundle import ConfigWorkflow, create_workflow from monai.data import Dataset from monai.inferers import SimpleInferer, SlidingWindowInferer from monai.networks.nets import UNet -from monai.transforms import Compose, LoadImage -from tests.nonconfig_workflow import NonConfigWorkflow +from monai.transforms import Compose, LoadImage, LoadImaged +from tests.nonconfig_workflow import NonConfigWorkflow, PythonicWorkflowImpl TEST_CASE_1 = [os.path.join(os.path.dirname(__file__), "testing_data", "inference.json")] @@ -45,7 +45,9 @@ def setUp(self): self.expected_shape = (128, 128, 128) test_image = np.random.rand(*self.expected_shape) self.filename = os.path.join(self.data_dir, "image.nii") + self.filename2 = os.path.join(self.data_dir, "image2.nii") nib.save(nib.Nifti1Image(test_image, np.eye(4)), self.filename) + nib.save(nib.Nifti1Image(test_image, np.eye(4)), self.filename2) def tearDown(self): shutil.rmtree(self.data_dir) @@ -108,12 +110,13 @@ def test_inference_config(self, config_file): # test property path inferer = ConfigWorkflow( config_file=config_file, + workflow_type="infer", properties_path=os.path.join(os.path.dirname(__file__), "testing_data", "fl_infer_properties.json"), logging_file=os.path.join(os.path.dirname(__file__), "testing_data", "logging.conf"), **override, ) self._test_inferer(inferer) - self.assertEqual(inferer.workflow_type, None) + self.assertEqual(inferer.workflow_type, "infer") @parameterized.expand([TEST_CASE_3]) def test_train_config(self, config_file): @@ -164,6 +167,29 @@ def test_non_config_wrong_log_cases(self, meta_file, logging_file, expected_erro with self.assertRaisesRegex(FileNotFoundError, expected_error): NonConfigWorkflow(self.filename, self.data_dir, meta_file, logging_file) + def test_pythonic_workflow(self): + device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + config_file = {"roi_size": (64, 64, 32)} + meta_file = os.path.join(os.path.dirname(__file__), "testing_data", "metadata.json") + workflow = PythonicWorkflowImpl(workflow_type="infer", config_file=config_file, meta_file=meta_file) + workflow.initialize() + # Load input data + input_loader = LoadImaged(keys="image") + workflow.dataflow.update(input_loader({"image": self.filename})) + self.assertEqual(workflow.bundle_root, ".") + self.assertEqual(workflow.device, device) + self.assertEqual(workflow.version, "0.1.0") + # check config override correctly + self.assertEqual(workflow.inferer.roi_size, (64, 64, 32)) + workflow.run() + # update input data and run again + workflow.dataflow.update(input_loader({"image": self.filename2})) + workflow.run() + pred = workflow.dataflow["pred"] + self.assertEqual(pred.shape[2:], self.expected_shape) + self.assertEqual(pred.meta["filename_or_obj"], self.filename2) + workflow.finalize() + if __name__ == "__main__": unittest.main() diff --git a/tests/testing_data/fl_infer_properties.json b/tests/testing_data/fl_infer_properties.json index 72e97cd2c6..337741488c 100644 --- a/tests/testing_data/fl_infer_properties.json +++ b/tests/testing_data/fl_infer_properties.json @@ -1,67 +1,78 @@ -{ - "bundle_root": { - "description": "root path of the bundle.", - "required": true, - "id": "bundle_root" +{ +"infer": + { + "bundle_root": { + "description": "root path of the bundle.", + "required": true, + "id": "bundle_root" + }, + "device": { + "description": "target device to execute the bundle workflow.", + "required": true, + "id": "device" + }, + "dataset_dir": { + "description": "directory path of the dataset.", + "required": true, + "id": "dataset_dir" + }, + "dataset": { + "description": "PyTorch dataset object for the inference / evaluation logic.", + "required": true, + "id": "dataset" + }, + "evaluator": { + "description": "inference / evaluation workflow engine.", + "required": true, + "id": "evaluator" + }, + "network_def": { + "description": "network module for the inference.", + "required": true, + "id": "network_def" + }, + "inferer": { + "description": "MONAI Inferer object to execute the model computation in inference.", + "required": true, + "id": "inferer" + }, + "dataset_data": { + "description": "data source for the inference / evaluation dataset.", + "required": false, + "id": "dataset::data", + "refer_id": null + }, + "handlers": { + "description": "event-handlers for the inference / evaluation logic.", + "required": false, + "id": "handlers", + "refer_id": "evaluator::val_handlers" + }, + "preprocessing": { + "description": "preprocessing for the input data.", + "required": false, + "id": "preprocessing", + "refer_id": "dataset::transform" + }, + "postprocessing": { + "description": "postprocessing for the model output data.", + "required": false, + "id": "postprocessing", + "refer_id": "evaluator::postprocessing" + }, + "key_metric": { + "description": "the key metric during evaluation.", + "required": false, + "id": "key_metric", + "refer_id": "evaluator::key_val_metric" + } }, - "device": { - "description": "target device to execute the bundle workflow.", - "required": true, - "id": "device" - }, - "dataset_dir": { - "description": "directory path of the dataset.", - "required": true, - "id": "dataset_dir" - }, - "dataset": { - "description": "PyTorch dataset object for the inference / evaluation logic.", - "required": true, - "id": "dataset" - }, - "evaluator": { - "description": "inference / evaluation workflow engine.", - "required": true, - "id": "evaluator" - }, - "network_def": { - "description": "network module for the inference.", - "required": true, - "id": "network_def" - }, - "inferer": { - "description": "MONAI Inferer object to execute the model computation in inference.", - "required": true, - "id": "inferer" - }, - "dataset_data": { - "description": "data source for the inference / evaluation dataset.", - "required": false, - "id": "dataset::data", - "refer_id": null - }, - "handlers": { - "description": "event-handlers for the inference / evaluation logic.", - "required": false, - "id": "handlers", - "refer_id": "evaluator::val_handlers" - }, - "preprocessing": { - "description": "preprocessing for the input data.", - "required": false, - "id": "preprocessing", - "refer_id": "dataset::transform" - }, - "postprocessing": { - "description": "postprocessing for the model output data.", - "required": false, - "id": "postprocessing", - "refer_id": "evaluator::postprocessing" - }, - "key_metric": { - "description": "the key metric during evaluation.", - "required": false, - "id": "key_metric", - "refer_id": "evaluator::key_val_metric" +"meta": + { + "version": { + "description": "version of the inference configuration.", + "required": true, + "id": "_meta_::version" + } } } From 6441466e637a0761dda0d91340629e5e67153f11 Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:24:41 +0800 Subject: [PATCH 09/26] fix format Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/workflows.py | 18 +++++++++++++----- tests/nonconfig_workflow.py | 28 ++++++++++++++-------------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index ebc4fe7139..07b2b5a9a6 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -118,8 +118,10 @@ def __init__( self.properties = properties[workflow_type] if "meta" in properties: self.properties.update(properties["meta"]) - except: - raise ValueError(f"{self.workflow_type} not find in property file {properties_path}") + except KeyError: + raise ValueError(f"{workflow_type} not found in property file {properties_path}") + except json.JSONDecodeError: + raise ValueError(f"Error decoding JSON from property file {properties_path}") else: if workflow_type == "train": self.properties = {**TrainProperties, **MetaProperties} @@ -278,10 +280,16 @@ def __init__( config_file: str | Sequence[str] | None = None, meta_file: str | Sequence[str] | None = None, logging_file: str | None = None, - **override: Any + **override: Any, ): meta_file = str(Path(os.getcwd()) / "metadata.json") if meta_file is None else meta_file - super().__init__(workflow_type=workflow_type, workflow=workflow, properties_path=properties_path, meta_file=meta_file, logging_file=logging_file) + super().__init__( + workflow_type=workflow_type, + workflow=workflow, + properties_path=properties_path, + meta_file=meta_file, + logging_file=logging_file, + ) self._props = {} self._set_props = {} self.parser = ConfigParser() @@ -328,7 +336,7 @@ def _get_property(self, name: str, property: dict) -> Any: """ value = None id = self.properties.get(name, None).get(BundlePropertyConfig.ID, None) - print(self.parser.config.keys(), self.parser.config["_meta_"], '*********') + print(self.parser.config.keys(), self.parser.config["_meta_"], "*********") if name in self._set_props: value = self._set_props[name] self._props[name] = value diff --git a/tests/nonconfig_workflow.py b/tests/nonconfig_workflow.py index f1f5cbcd10..345e4e1f56 100644 --- a/tests/nonconfig_workflow.py +++ b/tests/nonconfig_workflow.py @@ -28,7 +28,7 @@ ScaleIntensityd, ScaleIntensityRanged, ) -from monai.utils import BundleProperty, set_determinism, CommonKeys +from monai.utils import BundleProperty, CommonKeys, set_determinism class NonConfigWorkflow(BundleWorkflow): @@ -184,8 +184,16 @@ class PythonicWorkflowImpl(PythonicWorkflow): Test class simulates the bundle workflow defined by Python script directly. """ - def __init__(self, workflow_type: str = "inference", config_file: str | None = None, properties_path: str | None = None, meta_file: str | None = None): - super().__init__(workflow_type=workflow_type, properties_path=properties_path, config_file=config_file, meta_file=meta_file) + def __init__( + self, + workflow_type: str = "inference", + config_file: str | None = None, + properties_path: str | None = None, + meta_file: str | None = None, + ): + super().__init__( + workflow_type=workflow_type, properties_path=properties_path, config_file=config_file, meta_file=meta_file + ) self.dataflow = {} def initialize(self): @@ -204,16 +212,8 @@ def initialize(self): ScaleIntensityRanged(keys="image", a_min=-57, a_max=164, b_min=0.0, b_max=1.0, clip=True), ] ) - self.dataset = Dataset( - data=[self.dataflow], - transform=preprocessing, - ) - self.postprocessing = Compose( - [ - Activationsd(keys="pred", softmax=True), - AsDiscreted(keys="pred", argmax=True), - ] - ) + self.dataset = Dataset(data=[self.dataflow], transform=preprocessing) + self.postprocessing = Compose([Activationsd(keys="pred", softmax=True), AsDiscreted(keys="pred", argmax=True)]) self.inferer = SlidingWindowInferer(roi_size=self.parser.roi_size, sw_batch_size=1, overlap=0) def run(self): @@ -232,6 +232,6 @@ def get_bundle_root(self): def get_device(self): return torch.device("cuda" if torch.cuda.is_available() else "cpu") - + def get_inferer(self): return self.inferer From c2e2dad6797317bb4f17be4952bce139ac712e43 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 08:25:20 +0000 Subject: [PATCH 10/26] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_bundle_workflow.py | 2 +- tests/testing_data/fl_infer_properties.json | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/test_bundle_workflow.py b/tests/test_bundle_workflow.py index 38fe2ae824..bfb92f3746 100644 --- a/tests/test_bundle_workflow.py +++ b/tests/test_bundle_workflow.py @@ -22,7 +22,7 @@ import torch from parameterized import parameterized -from monai.bundle import ConfigWorkflow, create_workflow +from monai.bundle import ConfigWorkflow from monai.data import Dataset from monai.inferers import SimpleInferer, SlidingWindowInferer from monai.networks.nets import UNet diff --git a/tests/testing_data/fl_infer_properties.json b/tests/testing_data/fl_infer_properties.json index 337741488c..6b40edd2ab 100644 --- a/tests/testing_data/fl_infer_properties.json +++ b/tests/testing_data/fl_infer_properties.json @@ -1,6 +1,5 @@ -{ -"infer": - { +{ + "infer": { "bundle_root": { "description": "root path of the bundle.", "required": true, @@ -67,8 +66,7 @@ "refer_id": "evaluator::key_val_metric" } }, -"meta": - { + "meta": { "version": { "description": "version of the inference configuration.", "required": true, From 17e4f32f12b6a25f50e64cfd047ccce3d6469d3a Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:30:08 +0800 Subject: [PATCH 11/26] add docstring Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/workflows.py | 1 + 1 file changed, 1 insertion(+) diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index 07b2b5a9a6..991b14a6be 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -256,6 +256,7 @@ class PythonicWorkflow(BundleWorkflow): other unsupported string will raise a ValueError. default to `None` for common workflow. properties_path: the path to the JSON file of properties. + config_file: path to the config file, typically used to store hyperparameters. meta_file: filepath of the metadata file, if this is a list of file paths, their contents will be merged in order. logging_file: config file for `logging` module in the program. for more details: https://docs.python.org/3/library/logging.config.html#logging.config.fileConfig. From 6b2817e36942b86fc88fac38ebbc62bc083417e2 Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:40:33 +0800 Subject: [PATCH 12/26] fix format Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/workflows.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index 991b14a6be..811a6faef4 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -118,10 +118,10 @@ def __init__( self.properties = properties[workflow_type] if "meta" in properties: self.properties.update(properties["meta"]) - except KeyError: - raise ValueError(f"{workflow_type} not found in property file {properties_path}") - except json.JSONDecodeError: - raise ValueError(f"Error decoding JSON from property file {properties_path}") + except KeyError as e: + raise ValueError(f"{workflow_type} not found in property file {properties_path}") from e + except json.JSONDecodeError as e: + raise ValueError(f"Error decoding JSON from property file {properties_path}") from e else: if workflow_type == "train": self.properties = {**TrainProperties, **MetaProperties} @@ -337,7 +337,6 @@ def _get_property(self, name: str, property: dict) -> Any: """ value = None id = self.properties.get(name, None).get(BundlePropertyConfig.ID, None) - print(self.parser.config.keys(), self.parser.config["_meta_"], "*********") if name in self._set_props: value = self._set_props[name] self._props[name] = value @@ -348,12 +347,12 @@ def _get_property(self, name: str, property: dict) -> Any: else: try: value = getattr(self, f"get_{name}")() - except AttributeError: + except AttributeError as e: if property[BundleProperty.REQUIRED]: raise ValueError( f"unsupported property '{name}' is required in the bundle properties," f"need to implement a method 'get_{name}' to provide the property." - ) + ) from e self._props[name] = value return value From 6eed10defb212d75e08595a3a1864fb0a8b3ebfc Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:44:18 +0800 Subject: [PATCH 13/26] update docstring Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/workflows.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index 811a6faef4..7e6bd0f53c 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -44,12 +44,14 @@ class BundleWorkflow(ABC): workflow_type: specifies the workflow type: "train" or "training" for a training workflow, or "infer", "inference", "eval", "evaluation" for a inference workflow, other unsupported string will raise a ValueError. - default to `train` for train workflow. + default to `None` for only using meta properties. workflow: specifies the workflow type: "train" or "training" for a training workflow, or "infer", "inference", "eval", "evaluation" for a inference workflow, other unsupported string will raise a ValueError. default to `None` for common workflow. - properties_path: the path to the JSON file of properties. + properties_path: the path to the JSON file of properties. If workflow type is specified, the properties + will be loaded from the file based on the workflow type and meta, otherwise, the properties will be loaded from + "train". If the file does not exist, the default properties will be used. meta_file: filepath of the metadata file, if this is a list of file paths, their contents will be merged in order. logging_file: config file for `logging` module in the program. for more details: https://docs.python.org/3/library/logging.config.html#logging.config.fileConfig. @@ -250,12 +252,14 @@ class PythonicWorkflow(BundleWorkflow): workflow_type: specifies the workflow type: "train" or "training" for a training workflow, or "infer", "inference", "eval", "evaluation" for a inference workflow, other unsupported string will raise a ValueError. - default to `train` for train workflow. + default to `None` for only using meta properties. workflow: specifies the workflow type: "train" or "training" for a training workflow, or "infer", "inference", "eval", "evaluation" for a inference workflow, other unsupported string will raise a ValueError. default to `None` for common workflow. - properties_path: the path to the JSON file of properties. + properties_path: the path to the JSON file of properties. If workflow type is specified, the properties + will be loaded from the file based on the workflow type and meta, otherwise, the properties will be loaded from + "train". If the file does not exist, the default properties will be used. config_file: path to the config file, typically used to store hyperparameters. meta_file: filepath of the metadata file, if this is a list of file paths, their contents will be merged in order. logging_file: config file for `logging` module in the program. for more details: From f31e39d3a095fe1cbdf8fe2b4de54b41a54e0e73 Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:54:05 +0800 Subject: [PATCH 14/26] fix mypy Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/workflows.py | 6 +++--- tests/nonconfig_workflow.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index 7e6bd0f53c..df89d6ae8c 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -295,8 +295,8 @@ def __init__( meta_file=meta_file, logging_file=logging_file, ) - self._props = {} - self._set_props = {} + self._props: dict = {} + self._set_props: dict = {} self.parser = ConfigParser() if config_file is not None: self.parser.read_config(f=config_file) @@ -346,7 +346,7 @@ def _get_property(self, name: str, property: dict) -> Any: self._props[name] = value elif name in self._props: value = self._props[name] - elif name in self.parser.config["_meta_"]: + elif name in self.parser.config[self.parser.meta_key]: # type: ignore[index] value = self.parser.__getitem__(id) else: try: diff --git a/tests/nonconfig_workflow.py b/tests/nonconfig_workflow.py index 345e4e1f56..dd3a921429 100644 --- a/tests/nonconfig_workflow.py +++ b/tests/nonconfig_workflow.py @@ -194,7 +194,7 @@ def __init__( super().__init__( workflow_type=workflow_type, properties_path=properties_path, config_file=config_file, meta_file=meta_file ) - self.dataflow = {} + self.dataflow: dict = {} def initialize(self): self.net = UNet( From 7f5c43b72fe4b571a047b3128d419d71693715a2 Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:14:31 +0800 Subject: [PATCH 15/26] Update monai/bundle/workflows.py Co-authored-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/workflows.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index df89d6ae8c..210f0909ec 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -347,7 +347,7 @@ def _get_property(self, name: str, property: dict) -> Any: elif name in self._props: value = self._props[name] elif name in self.parser.config[self.parser.meta_key]: # type: ignore[index] - value = self.parser.__getitem__(id) + value = self.parser[id] else: try: value = getattr(self, f"get_{name}")() From 9c3155fed85e4154e74e8b37d7542278779243a6 Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:50:27 +0800 Subject: [PATCH 16/26] address comments Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/workflows.py | 76 +++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 43 deletions(-) diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index 210f0909ec..584bb86a72 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -49,9 +49,13 @@ class BundleWorkflow(ABC): or "infer", "inference", "eval", "evaluation" for a inference workflow, other unsupported string will raise a ValueError. default to `None` for common workflow. - properties_path: the path to the JSON file of properties. If workflow type is specified, the properties - will be loaded from the file based on the workflow type and meta, otherwise, the properties will be loaded from - "train". If the file does not exist, the default properties will be used. + properties_path: the path to the JSON file of properties. If `workflow_type` is specified, properties will be + loaded from the file based on the provided `workflow_type` and meta. If no `workflow_type` is specified, + properties will default to loading from "train". If the specified file is unavailable, default properties + will be sourced from "monai/bundle/properties.py" based on the workflow_type: + For a training workflow, properties load from `TrainProperties` and `MetaProperties`. + For a inference workflow, properties load from `InferProperties` and `MetaProperties`. + For workflow_type = None : only `MetaProperties` will be loaded. meta_file: filepath of the metadata file, if this is a list of file paths, their contents will be merged in order. logging_file: config file for `logging` module in the program. for more details: https://docs.python.org/3/library/logging.config.html#logging.config.fileConfig. @@ -246,7 +250,8 @@ class PythonicWorkflow(BundleWorkflow): """ Base class for the pythonic workflow specification in bundle, it can be a training, evaluation or inference workflow. It defines the basic interfaces for the bundle workflow behavior: `initialize`, `run`, `finalize`, etc. - And also provides the interface to get / set public properties to interact with a bundle workflow. + This also provides the interface to get / set public properties to interact with a bundle workflow through + defined `get_` accessor methods or directly defining members of the object. Args: workflow_type: specifies the workflow type: "train" or "training" for a training workflow, @@ -257,9 +262,13 @@ class PythonicWorkflow(BundleWorkflow): or "infer", "inference", "eval", "evaluation" for a inference workflow, other unsupported string will raise a ValueError. default to `None` for common workflow. - properties_path: the path to the JSON file of properties. If workflow type is specified, the properties - will be loaded from the file based on the workflow type and meta, otherwise, the properties will be loaded from - "train". If the file does not exist, the default properties will be used. + properties_path: the path to the JSON file of properties. If `workflow_type` is specified, properties will be + loaded from the file based on the provided `workflow_type` and meta. If no `workflow_type` is specified, + properties will default to loading from "train". If the specified file is unavailable, default properties + will be sourced from "monai/bundle/properties.py" based on the workflow_type: + For a training workflow, properties load from `TrainProperties` and `MetaProperties`. + For a inference workflow, properties load from `InferProperties` and `MetaProperties`. + For workflow_type = None : only `MetaProperties` will be loaded. config_file: path to the config file, typically used to store hyperparameters. meta_file: filepath of the metadata file, if this is a list of file paths, their contents will be merged in order. logging_file: config file for `logging` module in the program. for more details: @@ -270,17 +279,9 @@ class PythonicWorkflow(BundleWorkflow): supported_train_type: tuple = ("train", "training") supported_infer_type: tuple = ("infer", "inference", "eval", "evaluation") - @deprecated_arg( - "workflow", - since="1.2", - removed="1.5", - new_name="workflow_type", - msg_suffix="please use `workflow_type` instead.", - ) def __init__( self, workflow_type: str | None = None, - workflow: str | None = None, properties_path: PathLike | None = None, config_file: str | Sequence[str] | None = None, meta_file: str | Sequence[str] | None = None, @@ -290,13 +291,12 @@ def __init__( meta_file = str(Path(os.getcwd()) / "metadata.json") if meta_file is None else meta_file super().__init__( workflow_type=workflow_type, - workflow=workflow, properties_path=properties_path, meta_file=meta_file, logging_file=logging_file, ) - self._props: dict = {} - self._set_props: dict = {} + self._props_vals: dict = {} + self._set_props_vals: dict = {} self.parser = ConfigParser() if config_file is not None: self.parser.read_config(f=config_file) @@ -312,22 +312,6 @@ def initialize(self, *args: Any, **kwargs: Any) -> Any: """ self._props = {} - @abstractmethod - def run(self, *args: Any, **kwargs: Any) -> Any: - """ - Run the bundle workflow, it can be a training, evaluation or inference. - - """ - raise NotImplementedError() - - @abstractmethod - def finalize(self, *args: Any, **kwargs: Any) -> Any: - """ - Finalize step after the running of bundle workflow. - - """ - raise NotImplementedError() - def _get_property(self, name: str, property: dict) -> Any: """ With specified property name and information, get the expected property value. @@ -340,13 +324,13 @@ def _get_property(self, name: str, property: dict) -> Any: property: other information for the target property, defined in `TrainProperties` or `InferProperties`. """ value = None - id = self.properties.get(name, None).get(BundlePropertyConfig.ID, None) - if name in self._set_props: - value = self._set_props[name] - self._props[name] = value - elif name in self._props: - value = self._props[name] + if name in self._set_props_vals: + value = self._set_props_vals[name] + self._props_vals[name] = value + elif name in self._props_vals: + value = self._props_vals[name] elif name in self.parser.config[self.parser.meta_key]: # type: ignore[index] + id = self.properties.get(name, None).get(BundlePropertyConfig.ID, None) value = self.parser[id] else: try: @@ -357,7 +341,7 @@ def _get_property(self, name: str, property: dict) -> Any: f"unsupported property '{name}' is required in the bundle properties," f"need to implement a method 'get_{name}' to provide the property." ) from e - self._props[name] = value + self._props_vals[name] = value return value def _set_property(self, name: str, property: dict, value: Any) -> Any: @@ -371,7 +355,7 @@ def _set_property(self, name: str, property: dict, value: Any) -> Any: value: value to set for the property. """ - self._set_props[name] = value + self._set_props_vals[name] = value class ConfigWorkflow(BundleWorkflow): @@ -410,7 +394,13 @@ class ConfigWorkflow(BundleWorkflow): or "infer", "inference", "eval", "evaluation" for a inference workflow, other unsupported string will raise a ValueError. default to `None` for common workflow. - properties_path: the path to the JSON file of properties. + properties_path: the path to the JSON file of properties. If `workflow_type` is specified, properties will be + loaded from the file based on the provided `workflow_type` and meta. If no `workflow_type` is specified, + properties will default to loading from "train". If the specified file is unavailable, default properties + will be sourced from "monai/bundle/properties.py" based on the workflow_type: + For a training workflow, properties load from `TrainProperties` and `MetaProperties`. + For a inference workflow, properties load from `InferProperties` and `MetaProperties`. + For workflow_type = None : only `MetaProperties` will be loaded. override: id-value pairs to override or add the corresponding config content. e.g. ``--net#input_chns 42``, ``--net %/data/other.json#net_arg`` From ef41603cee1873e49ce9d7f4606c65864fef33f5 Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:50:39 +0800 Subject: [PATCH 17/26] fix format Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/workflows.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index 584bb86a72..b2e19cfef6 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -290,10 +290,7 @@ def __init__( ): meta_file = str(Path(os.getcwd()) / "metadata.json") if meta_file is None else meta_file super().__init__( - workflow_type=workflow_type, - properties_path=properties_path, - meta_file=meta_file, - logging_file=logging_file, + workflow_type=workflow_type, properties_path=properties_path, meta_file=meta_file, logging_file=logging_file ) self._props_vals: dict = {} self._set_props_vals: dict = {} From 9acad6b2603fe088ee0583fc060f288d8d435c0b Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Thu, 14 Nov 2024 13:49:57 +0800 Subject: [PATCH 18/26] fix ci Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/workflows.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index b2e19cfef6..38489d4778 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -307,7 +307,7 @@ def initialize(self, *args: Any, **kwargs: Any) -> Any: """ Initialize the bundle workflow before running. """ - self._props = {} + self._props_vals = {} def _get_property(self, name: str, property: dict) -> Any: """ From 99af7fd815d14d68554d868ba3a19d58c8f664dc Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Fri, 15 Nov 2024 00:40:18 +0800 Subject: [PATCH 19/26] address comments Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/workflows.py | 1 - 1 file changed, 1 deletion(-) diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index 38489d4778..45ef90767b 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -323,7 +323,6 @@ def _get_property(self, name: str, property: dict) -> Any: value = None if name in self._set_props_vals: value = self._set_props_vals[name] - self._props_vals[name] = value elif name in self._props_vals: value = self._props_vals[name] elif name in self.parser.config[self.parser.meta_key]: # type: ignore[index] From f93326388c09dd892e9d11a9e5d3ec447cd56cfa Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:25:46 +0800 Subject: [PATCH 20/26] address comments Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/workflows.py | 20 +++++++--- tests/nonconfig_workflow.py | 5 ++- tests/test_bundle_workflow.py | 37 ++++++++++++++++++- .../python_workflow_properties.json | 26 +++++++++++++ 4 files changed, 79 insertions(+), 9 deletions(-) create mode 100644 tests/testing_data/python_workflow_properties.json diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index 45ef90767b..2fa579f72d 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -51,7 +51,7 @@ class BundleWorkflow(ABC): default to `None` for common workflow. properties_path: the path to the JSON file of properties. If `workflow_type` is specified, properties will be loaded from the file based on the provided `workflow_type` and meta. If no `workflow_type` is specified, - properties will default to loading from "train". If the specified file is unavailable, default properties + properties will default to loading from "meta". If the specified file is unavailable, default properties will be sourced from "monai/bundle/properties.py" based on the workflow_type: For a training workflow, properties load from `TrainProperties` and `MetaProperties`. For a inference workflow, properties load from `InferProperties` and `MetaProperties`. @@ -116,8 +116,7 @@ def __init__( if not properties_path.is_file(): raise ValueError(f"Property file {properties_path} does not exist.") if workflow_type is None: - workflow_type = "train" - logger.info("No workflow type specified, default to 'train' for property file loading.") + logger.info("No workflow type specified, default to load meta properties from property file.") with open(properties_path) as json_file: try: properties = json.load(json_file) @@ -249,9 +248,13 @@ def check_properties(self) -> list[str] | None: class PythonicWorkflow(BundleWorkflow): """ Base class for the pythonic workflow specification in bundle, it can be a training, evaluation or inference workflow. - It defines the basic interfaces for the bundle workflow behavior: `initialize`, `run`, `finalize`, etc. + It defines the basic interfaces for the bundle workflow behavior: `initialize`, `finalize`, etc. This also provides the interface to get / set public properties to interact with a bundle workflow through defined `get_` accessor methods or directly defining members of the object. + For how to set the properties, users can define the `_set_` methods or directly set the members of the object. + The `initialize` method is called to set up the workflow before running. This method sets up internal state and prepares properties. + If properties are modified after the workflow has been initialized, `self._is_initialized` is set to `False`. Before running the + workflow again, `initialize` should be called to ensure that the workflow is properly set up with the new property values. Args: workflow_type: specifies the workflow type: "train" or "training" for a training workflow, @@ -264,7 +267,7 @@ class PythonicWorkflow(BundleWorkflow): default to `None` for common workflow. properties_path: the path to the JSON file of properties. If `workflow_type` is specified, properties will be loaded from the file based on the provided `workflow_type` and meta. If no `workflow_type` is specified, - properties will default to loading from "train". If the specified file is unavailable, default properties + properties will default to loading from "meta". If the specified file is unavailable, default properties will be sourced from "monai/bundle/properties.py" based on the workflow_type: For a training workflow, properties load from `TrainProperties` and `MetaProperties`. For a inference workflow, properties load from `InferProperties` and `MetaProperties`. @@ -302,12 +305,14 @@ def __init__( # the rest key-values in the _args are to override config content self.parser.update(pairs=override) + self._is_initialized: bool = False def initialize(self, *args: Any, **kwargs: Any) -> Any: """ Initialize the bundle workflow before running. """ self._props_vals = {} + self._is_initialized = True def _get_property(self, name: str, property: dict) -> Any: """ @@ -320,6 +325,8 @@ def _get_property(self, name: str, property: dict) -> Any: name: the name of target property. property: other information for the target property, defined in `TrainProperties` or `InferProperties`. """ + if not self._is_initialized: + raise RuntimeError("Please execute 'initialize' before getting any properties.") value = None if name in self._set_props_vals: value = self._set_props_vals[name] @@ -343,7 +350,7 @@ def _get_property(self, name: str, property: dict) -> Any: def _set_property(self, name: str, property: dict, value: Any) -> Any: """ With specified property name and information, set value for the expected property. - Stores user-reset initialized objects that should not be re-initialized. + Stores user-reset initialized objects that should not be re-initialized and marks the workflow as not initialized. Args: name: the name of target property. @@ -352,6 +359,7 @@ def _set_property(self, name: str, property: dict, value: Any) -> Any: """ self._set_props_vals[name] = value + self._is_initialized = False class ConfigWorkflow(BundleWorkflow): diff --git a/tests/nonconfig_workflow.py b/tests/nonconfig_workflow.py index dd3a921429..fcfc5b2951 100644 --- a/tests/nonconfig_workflow.py +++ b/tests/nonconfig_workflow.py @@ -197,6 +197,8 @@ def __init__( self.dataflow: dict = {} def initialize(self): + self._props_vals = {} + self._is_initialized = True self.net = UNet( spatial_dims=3, in_channels=1, @@ -214,7 +216,6 @@ def initialize(self): ) self.dataset = Dataset(data=[self.dataflow], transform=preprocessing) self.postprocessing = Compose([Activationsd(keys="pred", softmax=True), AsDiscreted(keys="pred", argmax=True)]) - self.inferer = SlidingWindowInferer(roi_size=self.parser.roi_size, sw_batch_size=1, overlap=0) def run(self): data = self.dataset[0] @@ -234,4 +235,4 @@ def get_device(self): return torch.device("cuda" if torch.cuda.is_available() else "cpu") def get_inferer(self): - return self.inferer + return SlidingWindowInferer(roi_size=self.parser.roi_size, sw_batch_size=1, overlap=0) diff --git a/tests/test_bundle_workflow.py b/tests/test_bundle_workflow.py index bfb92f3746..1496c1a23a 100644 --- a/tests/test_bundle_workflow.py +++ b/tests/test_bundle_workflow.py @@ -22,7 +22,7 @@ import torch from parameterized import parameterized -from monai.bundle import ConfigWorkflow +from monai.bundle import ConfigWorkflow, create_workflow from monai.data import Dataset from monai.inferers import SimpleInferer, SlidingWindowInferer from monai.networks.nets import UNet @@ -190,6 +190,41 @@ def test_pythonic_workflow(self): self.assertEqual(pred.meta["filename_or_obj"], self.filename2) workflow.finalize() + def test_create_pythonic_workflow(self): + device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + config_file = {"roi_size": (64, 64, 32)} + meta_file = os.path.join(os.path.dirname(__file__), "testing_data", "metadata.json") + property_path = os.path.join(os.path.dirname(__file__), "testing_data", "python_workflow_properties.json") + import sys + sys.path.append(os.path.dirname(__file__)) + workflow = create_workflow("nonconfig_workflow.PythonicWorkflowImpl", workflow_type="infer", config_file=config_file, meta_file=meta_file, properties_path=property_path) + # Load input data + input_loader = LoadImaged(keys="image") + workflow.dataflow.update(input_loader({"image": self.filename})) + self.assertEqual(workflow.bundle_root, ".") + self.assertEqual(workflow.device, device) + self.assertEqual(workflow.version, "0.1.0") + # check config override correctly + self.assertEqual(workflow.inferer.roi_size, (64, 64, 32)) + + # check set property override correctly + workflow.inferer = SlidingWindowInferer(roi_size=config_file["roi_size"], sw_batch_size=1, overlap=0.5) + workflow.initialize() + self.assertEqual(workflow.inferer.overlap, 0.5) + + workflow.run() + # update input data and run again + workflow.dataflow.update(input_loader({"image": self.filename2})) + workflow.run() + pred = workflow.dataflow["pred"] + self.assertEqual(pred.shape[2:], self.expected_shape) + self.assertEqual(pred.meta["filename_or_obj"], self.filename2) + + # test add properties + workflow.add_property(name="net", required=True, desc="network for the training.") + self.assertIn("net", workflow.properties) + workflow.finalize() + if __name__ == "__main__": unittest.main() diff --git a/tests/testing_data/python_workflow_properties.json b/tests/testing_data/python_workflow_properties.json new file mode 100644 index 0000000000..cd4295839a --- /dev/null +++ b/tests/testing_data/python_workflow_properties.json @@ -0,0 +1,26 @@ +{ + "infer": { + "bundle_root": { + "description": "root path of the bundle.", + "required": true, + "id": "bundle_root" + }, + "device": { + "description": "target device to execute the bundle workflow.", + "required": true, + "id": "device" + }, + "inferer": { + "description": "MONAI Inferer object to execute the model computation in inference.", + "required": true, + "id": "inferer" + } + }, + "meta": { + "version": { + "description": "version of the inference configuration.", + "required": true, + "id": "_meta_::version" + } + } +} From 4724f68b196f3dbc0c6371c8275fa60ec3c3e7c3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 07:26:45 +0000 Subject: [PATCH 21/26] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_bundle_workflow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_bundle_workflow.py b/tests/test_bundle_workflow.py index 1496c1a23a..cedba9a605 100644 --- a/tests/test_bundle_workflow.py +++ b/tests/test_bundle_workflow.py @@ -206,7 +206,7 @@ def test_create_pythonic_workflow(self): self.assertEqual(workflow.version, "0.1.0") # check config override correctly self.assertEqual(workflow.inferer.roi_size, (64, 64, 32)) - + # check set property override correctly workflow.inferer = SlidingWindowInferer(roi_size=config_file["roi_size"], sw_batch_size=1, overlap=0.5) workflow.initialize() @@ -219,7 +219,7 @@ def test_create_pythonic_workflow(self): pred = workflow.dataflow["pred"] self.assertEqual(pred.shape[2:], self.expected_shape) self.assertEqual(pred.meta["filename_or_obj"], self.filename2) - + # test add properties workflow.add_property(name="net", required=True, desc="network for the training.") self.assertIn("net", workflow.properties) From 50b1a5094eda2b92dc0c26ad77719c4769ae99a8 Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:36:52 +0800 Subject: [PATCH 22/26] address comments Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/workflows.py | 21 +++++++++++++-------- tests/test_bundle_workflow.py | 14 ++++++++++---- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index 2fa579f72d..f9dc881736 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -115,14 +115,18 @@ def __init__( properties_path = Path(properties_path) if not properties_path.is_file(): raise ValueError(f"Property file {properties_path} does not exist.") - if workflow_type is None: - logger.info("No workflow type specified, default to load meta properties from property file.") with open(properties_path) as json_file: try: properties = json.load(json_file) - self.properties = properties[workflow_type] - if "meta" in properties: - self.properties.update(properties["meta"]) + self.properties: dict = {} + if workflow_type is not None and workflow_type in properties: + self.properties = properties[workflow_type] + elif workflow_type is None: + if "meta" in properties: + self.properties = properties["meta"] + logger.info("No workflow type specified, default to load meta properties from property file.") + else: + logger.warning("No 'meta' key found in properties while workflow_type is None.") except KeyError as e: raise ValueError(f"{workflow_type} not found in property file {properties_path}") from e except json.JSONDecodeError as e: @@ -252,9 +256,10 @@ class PythonicWorkflow(BundleWorkflow): This also provides the interface to get / set public properties to interact with a bundle workflow through defined `get_` accessor methods or directly defining members of the object. For how to set the properties, users can define the `_set_` methods or directly set the members of the object. - The `initialize` method is called to set up the workflow before running. This method sets up internal state and prepares properties. - If properties are modified after the workflow has been initialized, `self._is_initialized` is set to `False`. Before running the - workflow again, `initialize` should be called to ensure that the workflow is properly set up with the new property values. + The `initialize` method is called to set up the workflow before running. This method sets up internal state + and prepares properties. If properties are modified after the workflow has been initialized, `self._is_initialized` + is set to `False`. Before running the workflow again, `initialize` should be called to ensure that the workflow is + properly set up with the new property values. Args: workflow_type: specifies the workflow type: "train" or "training" for a training workflow, diff --git a/tests/test_bundle_workflow.py b/tests/test_bundle_workflow.py index 1496c1a23a..1f797fbc4c 100644 --- a/tests/test_bundle_workflow.py +++ b/tests/test_bundle_workflow.py @@ -12,6 +12,7 @@ from __future__ import annotations import os +import sys import shutil import tempfile import unittest @@ -195,9 +196,14 @@ def test_create_pythonic_workflow(self): config_file = {"roi_size": (64, 64, 32)} meta_file = os.path.join(os.path.dirname(__file__), "testing_data", "metadata.json") property_path = os.path.join(os.path.dirname(__file__), "testing_data", "python_workflow_properties.json") - import sys sys.path.append(os.path.dirname(__file__)) - workflow = create_workflow("nonconfig_workflow.PythonicWorkflowImpl", workflow_type="infer", config_file=config_file, meta_file=meta_file, properties_path=property_path) + workflow = create_workflow( + "nonconfig_workflow.PythonicWorkflowImpl", + workflow_type="infer", + config_file=config_file, + meta_file=meta_file, + properties_path=property_path + ) # Load input data input_loader = LoadImaged(keys="image") workflow.dataflow.update(input_loader({"image": self.filename})) @@ -206,7 +212,7 @@ def test_create_pythonic_workflow(self): self.assertEqual(workflow.version, "0.1.0") # check config override correctly self.assertEqual(workflow.inferer.roi_size, (64, 64, 32)) - + # check set property override correctly workflow.inferer = SlidingWindowInferer(roi_size=config_file["roi_size"], sw_batch_size=1, overlap=0.5) workflow.initialize() @@ -219,7 +225,7 @@ def test_create_pythonic_workflow(self): pred = workflow.dataflow["pred"] self.assertEqual(pred.shape[2:], self.expected_shape) self.assertEqual(pred.meta["filename_or_obj"], self.filename2) - + # test add properties workflow.add_property(name="net", required=True, desc="network for the training.") self.assertIn("net", workflow.properties) From 150a60ad53304d318bfecc34a34a11fcb797706b Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:46:41 +0800 Subject: [PATCH 23/26] address comments Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/workflows.py | 2 ++ tests/test_bundle_workflow.py | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index f9dc881736..63c1a3e4d7 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -121,6 +121,8 @@ def __init__( self.properties: dict = {} if workflow_type is not None and workflow_type in properties: self.properties = properties[workflow_type] + if "meta" in properties: + self.properties.update(properties["meta"]) elif workflow_type is None: if "meta" in properties: self.properties = properties["meta"] diff --git a/tests/test_bundle_workflow.py b/tests/test_bundle_workflow.py index 1f797fbc4c..3799b60dac 100644 --- a/tests/test_bundle_workflow.py +++ b/tests/test_bundle_workflow.py @@ -172,7 +172,13 @@ def test_pythonic_workflow(self): device = torch.device("cuda" if torch.cuda.is_available() else "cpu") config_file = {"roi_size": (64, 64, 32)} meta_file = os.path.join(os.path.dirname(__file__), "testing_data", "metadata.json") - workflow = PythonicWorkflowImpl(workflow_type="infer", config_file=config_file, meta_file=meta_file) + property_path = os.path.join(os.path.dirname(__file__), "testing_data", "python_workflow_properties.json") + workflow = PythonicWorkflowImpl( + workflow_type="infer", + config_file=config_file, + meta_file=meta_file, + properties_path=property_path + ) workflow.initialize() # Load input data input_loader = LoadImaged(keys="image") From c026441f54595bdc901efacb0b5324f7a9fbc314 Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:59:09 +0800 Subject: [PATCH 24/26] fix format Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/workflows.py | 4 +++- tests/test_bundle_workflow.py | 9 +++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index 63c1a3e4d7..1330bc5dc3 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -126,7 +126,9 @@ def __init__( elif workflow_type is None: if "meta" in properties: self.properties = properties["meta"] - logger.info("No workflow type specified, default to load meta properties from property file.") + logger.info( + "No workflow type specified, default to load meta properties from property file." + ) else: logger.warning("No 'meta' key found in properties while workflow_type is None.") except KeyError as e: diff --git a/tests/test_bundle_workflow.py b/tests/test_bundle_workflow.py index 3799b60dac..3888f20ebc 100644 --- a/tests/test_bundle_workflow.py +++ b/tests/test_bundle_workflow.py @@ -12,8 +12,8 @@ from __future__ import annotations import os -import sys import shutil +import sys import tempfile import unittest from copy import deepcopy @@ -174,10 +174,7 @@ def test_pythonic_workflow(self): meta_file = os.path.join(os.path.dirname(__file__), "testing_data", "metadata.json") property_path = os.path.join(os.path.dirname(__file__), "testing_data", "python_workflow_properties.json") workflow = PythonicWorkflowImpl( - workflow_type="infer", - config_file=config_file, - meta_file=meta_file, - properties_path=property_path + workflow_type="infer", config_file=config_file, meta_file=meta_file, properties_path=property_path ) workflow.initialize() # Load input data @@ -208,7 +205,7 @@ def test_create_pythonic_workflow(self): workflow_type="infer", config_file=config_file, meta_file=meta_file, - properties_path=property_path + properties_path=property_path, ) # Load input data input_loader = LoadImaged(keys="image") From ec202e0c75d6008326c055ac8df013405167ad29 Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:57:21 +0800 Subject: [PATCH 25/26] update `compute_capabilities_after` Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/utils/module.py | 6 +++--- tests/test_bundle_trt_export.py | 2 +- tests/test_convert_to_trt.py | 2 +- tests/test_trt_compile.py | 2 +- tests/test_version_after.py | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/monai/utils/module.py b/monai/utils/module.py index 1ad001fc87..d3f2ff09f2 100644 --- a/monai/utils/module.py +++ b/monai/utils/module.py @@ -649,7 +649,7 @@ def compute_capabilities_after(major: int, minor: int = 0, current_ver_string: s current_ver_string: if None, the current system GPU CUDA compute capability will be used. Returns: - True if the current system GPU CUDA compute capability is greater than the specified version. + True if the current system GPU CUDA compute capability is greater than or equal to the specified version. """ if current_ver_string is None: cuda_available = torch.cuda.is_available() @@ -667,11 +667,11 @@ def compute_capabilities_after(major: int, minor: int = 0, current_ver_string: s ver, has_ver = optional_import("packaging.version", name="parse") if has_ver: - return ver(".".join((f"{major}", f"{minor}"))) < ver(f"{current_ver_string}") # type: ignore + return ver(".".join((f"{major}", f"{minor}"))) <= ver(f"{current_ver_string}") # type: ignore parts = f"{current_ver_string}".split("+", 1)[0].split(".", 2) while len(parts) < 2: parts += ["0"] c_major, c_minor = parts[:2] c_mn = int(c_major), int(c_minor) mn = int(major), int(minor) - return c_mn >= mn + return c_mn > mn diff --git a/tests/test_bundle_trt_export.py b/tests/test_bundle_trt_export.py index 835c8e5c1d..27e1ee97a8 100644 --- a/tests/test_bundle_trt_export.py +++ b/tests/test_bundle_trt_export.py @@ -53,7 +53,7 @@ @skip_if_windows @skip_if_no_cuda @skip_if_quick -@SkipIfBeforeComputeCapabilityVersion((7, 0)) +@SkipIfBeforeComputeCapabilityVersion((7, 5)) class TestTRTExport(unittest.TestCase): def setUp(self): diff --git a/tests/test_convert_to_trt.py b/tests/test_convert_to_trt.py index 712d887c3b..a7b1edec3c 100644 --- a/tests/test_convert_to_trt.py +++ b/tests/test_convert_to_trt.py @@ -38,7 +38,7 @@ @skip_if_windows @skip_if_no_cuda @skip_if_quick -@SkipIfBeforeComputeCapabilityVersion((7, 0)) +@SkipIfBeforeComputeCapabilityVersion((7, 5)) class TestConvertToTRT(unittest.TestCase): def setUp(self): diff --git a/tests/test_trt_compile.py b/tests/test_trt_compile.py index e1323c201f..f7779fec9b 100644 --- a/tests/test_trt_compile.py +++ b/tests/test_trt_compile.py @@ -50,7 +50,7 @@ def forward(self, x: list[torch.Tensor], y: torch.Tensor, z: torch.Tensor, bs: f @skip_if_quick @unittest.skipUnless(trt_imported, "tensorrt is required") @unittest.skipUnless(polygraphy_imported, "polygraphy is required") -@SkipIfBeforeComputeCapabilityVersion((7, 0)) +@SkipIfBeforeComputeCapabilityVersion((7, 5)) class TestTRTCompile(unittest.TestCase): def setUp(self): diff --git a/tests/test_version_after.py b/tests/test_version_after.py index 34a5054974..b6cb741382 100644 --- a/tests/test_version_after.py +++ b/tests/test_version_after.py @@ -38,7 +38,7 @@ TEST_CASES_SM = [ # (major, minor, sm, expected) - (6, 1, "6.1", False), + (6, 1, "6.1", True), (6, 1, "6.0", False), (6, 0, "8.6", True), (7, 0, "8", True), From 42d5d0be8f8ff0157b1e3363e76918b6c0e1c8c3 Mon Sep 17 00:00:00 2001 From: YunLiu <55491388+KumoLiu@users.noreply.github.com> Date: Wed, 27 Nov 2024 14:34:38 +0800 Subject: [PATCH 26/26] fix docstring Signed-off-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> --- monai/bundle/workflows.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/monai/bundle/workflows.py b/monai/bundle/workflows.py index 78cad0bf1f..75cf7b0b09 100644 --- a/monai/bundle/workflows.py +++ b/monai/bundle/workflows.py @@ -51,7 +51,7 @@ class BundleWorkflow(ABC): default to `None` for common workflow. properties_path: the path to the JSON file of properties. If `workflow_type` is specified, properties will be loaded from the file based on the provided `workflow_type` and meta. If no `workflow_type` is specified, - properties will default to loading from "meta". If the specified file is unavailable, default properties + properties will default to loading from "meta". If `properties_path` is None, default properties will be sourced from "monai/bundle/properties.py" based on the workflow_type: For a training workflow, properties load from `TrainProperties` and `MetaProperties`. For a inference workflow, properties load from `InferProperties` and `MetaProperties`. @@ -276,7 +276,7 @@ class PythonicWorkflow(BundleWorkflow): default to `None` for common workflow. properties_path: the path to the JSON file of properties. If `workflow_type` is specified, properties will be loaded from the file based on the provided `workflow_type` and meta. If no `workflow_type` is specified, - properties will default to loading from "meta". If the specified file is unavailable, default properties + properties will default to loading from "meta". If `properties_path` is None, default properties will be sourced from "monai/bundle/properties.py" based on the workflow_type: For a training workflow, properties load from `TrainProperties` and `MetaProperties`. For a inference workflow, properties load from `InferProperties` and `MetaProperties`. @@ -409,7 +409,7 @@ class ConfigWorkflow(BundleWorkflow): default to `None` for common workflow. properties_path: the path to the JSON file of properties. If `workflow_type` is specified, properties will be loaded from the file based on the provided `workflow_type` and meta. If no `workflow_type` is specified, - properties will default to loading from "train". If the specified file is unavailable, default properties + properties will default to loading from "train". If `properties_path` is None, default properties will be sourced from "monai/bundle/properties.py" based on the workflow_type: For a training workflow, properties load from `TrainProperties` and `MetaProperties`. For a inference workflow, properties load from `InferProperties` and `MetaProperties`.