We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
additional events in the deepgrow interaction engine
**Is your feature request related to a problem? Please describe.**
This is a feature request for adding extra engine events within the click simulation loops during the deepgrow model training:
https://github.com/Project-MONAI/MONAI/blob/abad8416153e67aac04417bbd9398f334b9c0912/monai/apps/deepgrow/interaction.py#L61-L77

the main benefit is to have flexible simulation handlers attached to the inner loops

cc @danieltudosiu @diazandr3s @SachidanandAlle 



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `monai/engines/utils.py`
Content:
```
1 # Copyright 2020 - 2021 MONAI Consortium
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at
5 #     http://www.apache.org/licenses/LICENSE-2.0
6 # Unless required by applicable law or agreed to in writing, software
7 # distributed under the License is distributed on an "AS IS" BASIS,
8 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 # See the License for the specific language governing permissions and
10 # limitations under the License.
11 
12 from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
13 
14 import torch
15 
16 from monai.transforms import apply_transform
17 from monai.utils import exact_version, optional_import
18 from monai.utils.enums import CommonKeys
19 
20 if TYPE_CHECKING:
21     from ignite.engine import EventEnum
22 else:
23     EventEnum, _ = optional_import("ignite.engine", "0.4.4", exact_version, "EventEnum")
24 
25 __all__ = [
26     "IterationEvents",
27     "GanKeys",
28     "get_devices_spec",
29     "default_prepare_batch",
30     "default_make_latent",
31     "engine_apply_transform",
32 ]
33 
34 
35 class IterationEvents(EventEnum):
36     """
37     Additional Events engine can register and trigger in the iteration process.
38     Refer to the example in ignite: https://github.com/pytorch/ignite/blob/master/ignite/engine/events.py#L146
39     These Events can be triggered during training iteration:
40     `FORWARD_COMPLETED` is the Event when `network(image, label)` completed.
41     `LOSS_COMPLETED` is the Event when `loss(pred, label)` completed.
42     `BACKWARD_COMPLETED` is the Event when `loss.backward()` completed.
43     `MODEL_COMPLETED` is the Event when all the model related operations completed.
44 
45     """
46 
47     FORWARD_COMPLETED = "forward_completed"
48     LOSS_COMPLETED = "loss_completed"
49     BACKWARD_COMPLETED = "backward_completed"
50     MODEL_COMPLETED = "model_completed"
51 
52 
53 class GanKeys:
54     """
55     A set of common keys for generative adversarial networks.
56 
57     """
58 
59     REALS = "reals"
60     FAKES = "fakes"
61     LATENTS = "latents"
62     GLOSS = "g_loss"
63     DLOSS = "d_loss"
64 
65 
66 def get_devices_spec(devices: Optional[Sequence[torch.device]] = None) -> List[torch.device]:
67     """
68     Get a valid specification for one or more devices. If `devices` is None get devices for all CUDA devices available.
69     If `devices` is and zero-length structure a single CPU compute device is returned. In any other cases `devices` is
70     returned unchanged.
71 
72     Args:
73         devices: list of devices to request, None for all GPU devices, [] for CPU.
74 
75     Raises:
76         RuntimeError: When all GPUs are selected (``devices=None``) but no GPUs are available.
77 
78     Returns:
79         list of torch.device: list of devices.
80 
81     """
82     if devices is None:
83         devices = [torch.device(f"cuda:{d:d}") for d in range(torch.cuda.device_count())]
84 
85         if len(devices) == 0:
86             raise RuntimeError("No GPU devices available.")
87 
88     elif len(devices) == 0:
89         devices = [torch.device("cpu")]
90 
91     else:
92         devices = list(devices)
93 
94     return devices
95 
96 
97 def default_prepare_batch(
98     batchdata: Dict[str, torch.Tensor],
99     device: Optional[Union[str, torch.device]] = None,
100     non_blocking: bool = False,
101 ) -> Union[Tuple[torch.Tensor, Optional[torch.Tensor]], torch.Tensor]:
102     """
103     Default function to prepare the data for current iteration.
104     Refer to ignite: https://github.com/pytorch/ignite/blob/v0.4.2/ignite/engine/__init__.py#L28.
105 
106     Returns:
107         image, label(optional).
108 
109     """
110     if not isinstance(batchdata, dict):
111         raise AssertionError("default prepare_batch expects dictionary input data.")
112     if isinstance(batchdata.get(CommonKeys.LABEL, None), torch.Tensor):
113         return (
114             batchdata[CommonKeys.IMAGE].to(device=device, non_blocking=non_blocking),
115             batchdata[CommonKeys.LABEL].to(device=device, non_blocking=non_blocking),
116         )
117     if GanKeys.REALS in batchdata:
118         return batchdata[GanKeys.REALS].to(device=device, non_blocking=non_blocking)
119     return batchdata[CommonKeys.IMAGE].to(device=device, non_blocking=non_blocking), None
120 
121 
122 def default_make_latent(
123     num_latents: int,
124     latent_size: int,
125     device: Optional[Union[str, torch.device]] = None,
126     non_blocking: bool = False,
127 ) -> torch.Tensor:
128     return torch.randn(num_latents, latent_size).to(device=device, non_blocking=non_blocking)
129 
130 
131 def engine_apply_transform(batch: Any, output: Any, transform: Callable):
132     """
133     Apply transform for the engine.state.batch and engine.state.output.
134     If `batch` and `output` are dictionaries, temporarily combine them for the transform,
135     otherwise, apply the transform for `output` data only.
136 
137     """
138     if isinstance(batch, dict) and isinstance(output, dict):
139         data = dict(batch)
140         data.update(output)
141         data = apply_transform(transform, data)
142         for k, v in data.items():
143             # split the output data of post transforms into `output` and `batch`,
144             # `batch` should be read-only, so save the generated key-value into `output`
145             if k in output or k not in batch:
146                 output[k] = v
147             else:
148                 batch[k] = v
149     else:
150         output = apply_transform(transform, output)
151 
152     return batch, output
153
```

Path: `monai/apps/deepgrow/interaction.py`
Content:
```
1 # Copyright 2020 - 2021 MONAI Consortium
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at
5 #     http://www.apache.org/licenses/LICENSE-2.0
6 # Unless required by applicable law or agreed to in writing, software
7 # distributed under the License is distributed on an "AS IS" BASIS,
8 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 # See the License for the specific language governing permissions and
10 # limitations under the License.
11 from typing import Callable, Dict, Sequence, Union
12 
13 import torch
14 
15 from monai.engines import SupervisedEvaluator, SupervisedTrainer
16 from monai.engines.workflow import Events
17 from monai.transforms import Compose
18 from monai.utils.enums import CommonKeys
19 
20 
21 class Interaction:
22     """
23     Ignite handler used to introduce interactions (simulation of clicks) for Deepgrow Training/Evaluation.
24     This implementation is based on:
25 
26         Sakinis et al., Interactive segmentation of medical images through
27         fully convolutional neural networks. (2019) https://arxiv.org/abs/1903.08205
28 
29     Args:
30         transforms: execute additional transformation during every iteration (before train).
31             Typically, several Tensor based transforms composed by `Compose`.
32         max_interactions: maximum number of interactions per iteration
33         train: training or evaluation
34         key_probability: field name to fill probability for every interaction
35     """
36 
37     def __init__(
38         self,
39         transforms: Union[Sequence[Callable], Callable],
40         max_interactions: int,
41         train: bool,
42         key_probability: str = "probability",
43     ) -> None:
44 
45         if not isinstance(transforms, Compose):
46             transforms = Compose(transforms)
47 
48         self.transforms = transforms
49         self.max_interactions = max_interactions
50         self.train = train
51         self.key_probability = key_probability
52 
53     def attach(self, engine: Union[SupervisedTrainer, SupervisedEvaluator]) -> None:
54         if not engine.has_event_handler(self, Events.ITERATION_STARTED):
55             engine.add_event_handler(Events.ITERATION_STARTED, self)
56 
57     def __call__(self, engine: Union[SupervisedTrainer, SupervisedEvaluator], batchdata: Dict[str, torch.Tensor]):
58         if batchdata is None:
59             raise ValueError("Must provide batch data for current iteration.")
60 
61         for j in range(self.max_interactions):
62             inputs, _ = engine.prepare_batch(batchdata)
63             inputs = inputs.to(engine.state.device)
64 
65             engine.network.eval()
66             with torch.no_grad():
67                 if engine.amp:
68                     with torch.cuda.amp.autocast():
69                         predictions = engine.inferer(inputs, engine.network)
70                 else:
71                     predictions = engine.inferer(inputs, engine.network)
72 
73             batchdata.update({CommonKeys.PRED: predictions})
74             batchdata[self.key_probability] = torch.as_tensor(
75                 ([1.0 - ((1.0 / self.max_interactions) * j)] if self.train else [1.0]) * len(inputs)
76             )
77             batchdata = self.transforms(batchdata)
78 
79         return engine._iteration(engine, batchdata)
80
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/monai/apps/deepgrow/interaction.py b/monai/apps/deepgrow/interaction.py
--- a/monai/apps/deepgrow/interaction.py
+++ b/monai/apps/deepgrow/interaction.py
@@ -13,14 +13,14 @@
 import torch
 
 from monai.engines import SupervisedEvaluator, SupervisedTrainer
-from monai.engines.workflow import Events
+from monai.engines.utils import IterationEvents
 from monai.transforms import Compose
 from monai.utils.enums import CommonKeys
 
 
 class Interaction:
     """
-    Ignite handler used to introduce interactions (simulation of clicks) for Deepgrow Training/Evaluation.
+    Ignite process_function used to introduce interactions (simulation of clicks) for Deepgrow Training/Evaluation.
     This implementation is based on:
 
         Sakinis et al., Interactive segmentation of medical images through
@@ -50,10 +50,6 @@
         self.train = train
         self.key_probability = key_probability
 
-    def attach(self, engine: Union[SupervisedTrainer, SupervisedEvaluator]) -> None:
-        if not engine.has_event_handler(self, Events.ITERATION_STARTED):
-            engine.add_event_handler(Events.ITERATION_STARTED, self)
-
     def __call__(self, engine: Union[SupervisedTrainer, SupervisedEvaluator], batchdata: Dict[str, torch.Tensor]):
         if batchdata is None:
             raise ValueError("Must provide batch data for current iteration.")
@@ -62,6 +58,8 @@
             inputs, _ = engine.prepare_batch(batchdata)
             inputs = inputs.to(engine.state.device)
 
+            engine.fire_event(IterationEvents.INNER_ITERATION_STARTED)
+
             engine.network.eval()
             with torch.no_grad():
                 if engine.amp:
@@ -70,6 +68,8 @@
                 else:
                     predictions = engine.inferer(inputs, engine.network)
 
+            engine.fire_event(IterationEvents.INNER_ITERATION_COMPLETED)
+
             batchdata.update({CommonKeys.PRED: predictions})
             batchdata[self.key_probability] = torch.as_tensor(
                 ([1.0 - ((1.0 / self.max_interactions) * j)] if self.train else [1.0]) * len(inputs)
diff --git a/monai/engines/utils.py b/monai/engines/utils.py
--- a/monai/engines/utils.py
+++ b/monai/engines/utils.py
@@ -41,13 +41,16 @@
     `LOSS_COMPLETED` is the Event when `loss(pred, label)` completed.
     `BACKWARD_COMPLETED` is the Event when `loss.backward()` completed.
     `MODEL_COMPLETED` is the Event when all the model related operations completed.
-
+    `INNER_ITERATION_STARTED` is the Event when the iteration has an inner loop and the loop is started.
+    `INNER_ITERATION_COMPLETED` is the Event when the iteration has an inner loop and the loop is completed.
     """
 
     FORWARD_COMPLETED = "forward_completed"
     LOSS_COMPLETED = "loss_completed"
     BACKWARD_COMPLETED = "backward_completed"
     MODEL_COMPLETED = "model_completed"
+    INNER_ITERATION_STARTED = "inner_iteration_started"
+    INNER_ITERATION_COMPLETED = "inner_iteration_completed"
 
 
 class GanKeys:

{"golden_diff": "diff --git a/monai/apps/deepgrow/interaction.py b/monai/apps/deepgrow/interaction.py\n--- a/monai/apps/deepgrow/interaction.py\n+++ b/monai/apps/deepgrow/interaction.py\n@@ -13,14 +13,14 @@\n import torch\n \n from monai.engines import SupervisedEvaluator, SupervisedTrainer\n-from monai.engines.workflow import Events\n+from monai.engines.utils import IterationEvents\n from monai.transforms import Compose\n from monai.utils.enums import CommonKeys\n \n \n class Interaction:\n     \"\"\"\n-    Ignite handler used to introduce interactions (simulation of clicks) for Deepgrow Training/Evaluation.\n+    Ignite process_function used to introduce interactions (simulation of clicks) for Deepgrow Training/Evaluation.\n     This implementation is based on:\n \n         Sakinis et al., Interactive segmentation of medical images through\n@@ -50,10 +50,6 @@\n         self.train = train\n         self.key_probability = key_probability\n \n-    def attach(self, engine: Union[SupervisedTrainer, SupervisedEvaluator]) -> None:\n-        if not engine.has_event_handler(self, Events.ITERATION_STARTED):\n-            engine.add_event_handler(Events.ITERATION_STARTED, self)\n-\n     def __call__(self, engine: Union[SupervisedTrainer, SupervisedEvaluator], batchdata: Dict[str, torch.Tensor]):\n         if batchdata is None:\n             raise ValueError(\"Must provide batch data for current iteration.\")\n@@ -62,6 +58,8 @@\n             inputs, _ = engine.prepare_batch(batchdata)\n             inputs = inputs.to(engine.state.device)\n \n+            engine.fire_event(IterationEvents.INNER_ITERATION_STARTED)\n+\n             engine.network.eval()\n             with torch.no_grad():\n                 if engine.amp:\n@@ -70,6 +68,8 @@\n                 else:\n                     predictions = engine.inferer(inputs, engine.network)\n \n+            engine.fire_event(IterationEvents.INNER_ITERATION_COMPLETED)\n+\n             batchdata.update({CommonKeys.PRED: predictions})\n             batchdata[self.key_probability] = torch.as_tensor(\n                 ([1.0 - ((1.0 / self.max_interactions) * j)] if self.train else [1.0]) * len(inputs)\ndiff --git a/monai/engines/utils.py b/monai/engines/utils.py\n--- a/monai/engines/utils.py\n+++ b/monai/engines/utils.py\n@@ -41,13 +41,16 @@\n     `LOSS_COMPLETED` is the Event when `loss(pred, label)` completed.\n     `BACKWARD_COMPLETED` is the Event when `loss.backward()` completed.\n     `MODEL_COMPLETED` is the Event when all the model related operations completed.\n-\n+    `INNER_ITERATION_STARTED` is the Event when the iteration has an inner loop and the loop is started.\n+    `INNER_ITERATION_COMPLETED` is the Event when the iteration has an inner loop and the loop is completed.\n     \"\"\"\n \n     FORWARD_COMPLETED = \"forward_completed\"\n     LOSS_COMPLETED = \"loss_completed\"\n     BACKWARD_COMPLETED = \"backward_completed\"\n     MODEL_COMPLETED = \"model_completed\"\n+    INNER_ITERATION_STARTED = \"inner_iteration_started\"\n+    INNER_ITERATION_COMPLETED = \"inner_iteration_completed\"\n \n \n class GanKeys:\n", "issue": "additional events in the deepgrow interaction engine\n**Is your feature request related to a problem? Please describe.**\r\nThis is a feature request for adding extra engine events within the click simulation loops during the deepgrow model training:\r\nhttps://github.com/Project-MONAI/MONAI/blob/abad8416153e67aac04417bbd9398f334b9c0912/monai/apps/deepgrow/interaction.py#L61-L77\r\n\r\nthe main benefit is to have flexible simulation handlers attached to the inner loops\r\n\r\ncc @danieltudosiu @diazandr3s @SachidanandAlle \r\n\r\n\n", "before_files": [{"content": "# Copyright 2020 - 2021 MONAI Consortium\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#     http://www.apache.org/licenses/LICENSE-2.0\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Sequence, Tuple, Union\n\nimport torch\n\nfrom monai.transforms import apply_transform\nfrom monai.utils import exact_version, optional_import\nfrom monai.utils.enums import CommonKeys\n\nif TYPE_CHECKING:\n    from ignite.engine import EventEnum\nelse:\n    EventEnum, _ = optional_import(\"ignite.engine\", \"0.4.4\", exact_version, \"EventEnum\")\n\n__all__ = [\n    \"IterationEvents\",\n    \"GanKeys\",\n    \"get_devices_spec\",\n    \"default_prepare_batch\",\n    \"default_make_latent\",\n    \"engine_apply_transform\",\n]\n\n\nclass IterationEvents(EventEnum):\n    \"\"\"\n    Additional Events engine can register and trigger in the iteration process.\n    Refer to the example in ignite: https://github.com/pytorch/ignite/blob/master/ignite/engine/events.py#L146\n    These Events can be triggered during training iteration:\n    `FORWARD_COMPLETED` is the Event when `network(image, label)` completed.\n    `LOSS_COMPLETED` is the Event when `loss(pred, label)` completed.\n    `BACKWARD_COMPLETED` is the Event when `loss.backward()` completed.\n    `MODEL_COMPLETED` is the Event when all the model related operations completed.\n\n    \"\"\"\n\n    FORWARD_COMPLETED = \"forward_completed\"\n    LOSS_COMPLETED = \"loss_completed\"\n    BACKWARD_COMPLETED = \"backward_completed\"\n    MODEL_COMPLETED = \"model_completed\"\n\n\nclass GanKeys:\n    \"\"\"\n    A set of common keys for generative adversarial networks.\n\n    \"\"\"\n\n    REALS = \"reals\"\n    FAKES = \"fakes\"\n    LATENTS = \"latents\"\n    GLOSS = \"g_loss\"\n    DLOSS = \"d_loss\"\n\n\ndef get_devices_spec(devices: Optional[Sequence[torch.device]] = None) -> List[torch.device]:\n    \"\"\"\n    Get a valid specification for one or more devices. If `devices` is None get devices for all CUDA devices available.\n    If `devices` is and zero-length structure a single CPU compute device is returned. In any other cases `devices` is\n    returned unchanged.\n\n    Args:\n        devices: list of devices to request, None for all GPU devices, [] for CPU.\n\n    Raises:\n        RuntimeError: When all GPUs are selected (``devices=None``) but no GPUs are available.\n\n    Returns:\n        list of torch.device: list of devices.\n\n    \"\"\"\n    if devices is None:\n        devices = [torch.device(f\"cuda:{d:d}\") for d in range(torch.cuda.device_count())]\n\n        if len(devices) == 0:\n            raise RuntimeError(\"No GPU devices available.\")\n\n    elif len(devices) == 0:\n        devices = [torch.device(\"cpu\")]\n\n    else:\n        devices = list(devices)\n\n    return devices\n\n\ndef default_prepare_batch(\n    batchdata: Dict[str, torch.Tensor],\n    device: Optional[Union[str, torch.device]] = None,\n    non_blocking: bool = False,\n) -> Union[Tuple[torch.Tensor, Optional[torch.Tensor]], torch.Tensor]:\n    \"\"\"\n    Default function to prepare the data for current iteration.\n    Refer to ignite: https://github.com/pytorch/ignite/blob/v0.4.2/ignite/engine/__init__.py#L28.\n\n    Returns:\n        image, label(optional).\n\n    \"\"\"\n    if not isinstance(batchdata, dict):\n        raise AssertionError(\"default prepare_batch expects dictionary input data.\")\n    if isinstance(batchdata.get(CommonKeys.LABEL, None), torch.Tensor):\n        return (\n            batchdata[CommonKeys.IMAGE].to(device=device, non_blocking=non_blocking),\n            batchdata[CommonKeys.LABEL].to(device=device, non_blocking=non_blocking),\n        )\n    if GanKeys.REALS in batchdata:\n        return batchdata[GanKeys.REALS].to(device=device, non_blocking=non_blocking)\n    return batchdata[CommonKeys.IMAGE].to(device=device, non_blocking=non_blocking), None\n\n\ndef default_make_latent(\n    num_latents: int,\n    latent_size: int,\n    device: Optional[Union[str, torch.device]] = None,\n    non_blocking: bool = False,\n) -> torch.Tensor:\n    return torch.randn(num_latents, latent_size).to(device=device, non_blocking=non_blocking)\n\n\ndef engine_apply_transform(batch: Any, output: Any, transform: Callable):\n    \"\"\"\n    Apply transform for the engine.state.batch and engine.state.output.\n    If `batch` and `output` are dictionaries, temporarily combine them for the transform,\n    otherwise, apply the transform for `output` data only.\n\n    \"\"\"\n    if isinstance(batch, dict) and isinstance(output, dict):\n        data = dict(batch)\n        data.update(output)\n        data = apply_transform(transform, data)\n        for k, v in data.items():\n            # split the output data of post transforms into `output` and `batch`,\n            # `batch` should be read-only, so save the generated key-value into `output`\n            if k in output or k not in batch:\n                output[k] = v\n            else:\n                batch[k] = v\n    else:\n        output = apply_transform(transform, output)\n\n    return batch, output\n", "path": "monai/engines/utils.py"}, {"content": "# Copyright 2020 - 2021 MONAI Consortium\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#     http://www.apache.org/licenses/LICENSE-2.0\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom typing import Callable, Dict, Sequence, Union\n\nimport torch\n\nfrom monai.engines import SupervisedEvaluator, SupervisedTrainer\nfrom monai.engines.workflow import Events\nfrom monai.transforms import Compose\nfrom monai.utils.enums import CommonKeys\n\n\nclass Interaction:\n    \"\"\"\n    Ignite handler used to introduce interactions (simulation of clicks) for Deepgrow Training/Evaluation.\n    This implementation is based on:\n\n        Sakinis et al., Interactive segmentation of medical images through\n        fully convolutional neural networks. (2019) https://arxiv.org/abs/1903.08205\n\n    Args:\n        transforms: execute additional transformation during every iteration (before train).\n            Typically, several Tensor based transforms composed by `Compose`.\n        max_interactions: maximum number of interactions per iteration\n        train: training or evaluation\n        key_probability: field name to fill probability for every interaction\n    \"\"\"\n\n    def __init__(\n        self,\n        transforms: Union[Sequence[Callable], Callable],\n        max_interactions: int,\n        train: bool,\n        key_probability: str = \"probability\",\n    ) -> None:\n\n        if not isinstance(transforms, Compose):\n            transforms = Compose(transforms)\n\n        self.transforms = transforms\n        self.max_interactions = max_interactions\n        self.train = train\n        self.key_probability = key_probability\n\n    def attach(self, engine: Union[SupervisedTrainer, SupervisedEvaluator]) -> None:\n        if not engine.has_event_handler(self, Events.ITERATION_STARTED):\n            engine.add_event_handler(Events.ITERATION_STARTED, self)\n\n    def __call__(self, engine: Union[SupervisedTrainer, SupervisedEvaluator], batchdata: Dict[str, torch.Tensor]):\n        if batchdata is None:\n            raise ValueError(\"Must provide batch data for current iteration.\")\n\n        for j in range(self.max_interactions):\n            inputs, _ = engine.prepare_batch(batchdata)\n            inputs = inputs.to(engine.state.device)\n\n            engine.network.eval()\n            with torch.no_grad():\n                if engine.amp:\n                    with torch.cuda.amp.autocast():\n                        predictions = engine.inferer(inputs, engine.network)\n                else:\n                    predictions = engine.inferer(inputs, engine.network)\n\n            batchdata.update({CommonKeys.PRED: predictions})\n            batchdata[self.key_probability] = torch.as_tensor(\n                ([1.0 - ((1.0 / self.max_interactions) * j)] if self.train else [1.0]) * len(inputs)\n            )\n            batchdata = self.transforms(batchdata)\n\n        return engine._iteration(engine, batchdata)\n", "path": "monai/apps/deepgrow/interaction.py"}], "after_files": [{"content": "# Copyright 2020 - 2021 MONAI Consortium\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#     http://www.apache.org/licenses/LICENSE-2.0\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Sequence, Tuple, Union\n\nimport torch\n\nfrom monai.transforms import apply_transform\nfrom monai.utils import exact_version, optional_import\nfrom monai.utils.enums import CommonKeys\n\nif TYPE_CHECKING:\n    from ignite.engine import EventEnum\nelse:\n    EventEnum, _ = optional_import(\"ignite.engine\", \"0.4.4\", exact_version, \"EventEnum\")\n\n__all__ = [\n    \"IterationEvents\",\n    \"GanKeys\",\n    \"get_devices_spec\",\n    \"default_prepare_batch\",\n    \"default_make_latent\",\n    \"engine_apply_transform\",\n]\n\n\nclass IterationEvents(EventEnum):\n    \"\"\"\n    Additional Events engine can register and trigger in the iteration process.\n    Refer to the example in ignite: https://github.com/pytorch/ignite/blob/master/ignite/engine/events.py#L146\n    These Events can be triggered during training iteration:\n    `FORWARD_COMPLETED` is the Event when `network(image, label)` completed.\n    `LOSS_COMPLETED` is the Event when `loss(pred, label)` completed.\n    `BACKWARD_COMPLETED` is the Event when `loss.backward()` completed.\n    `MODEL_COMPLETED` is the Event when all the model related operations completed.\n    `INNER_ITERATION_STARTED` is the Event when the iteration has an inner loop and the loop is started.\n    `INNER_ITERATION_COMPLETED` is the Event when the iteration has an inner loop and the loop is completed.\n    \"\"\"\n\n    FORWARD_COMPLETED = \"forward_completed\"\n    LOSS_COMPLETED = \"loss_completed\"\n    BACKWARD_COMPLETED = \"backward_completed\"\n    MODEL_COMPLETED = \"model_completed\"\n    INNER_ITERATION_STARTED = \"inner_iteration_started\"\n    INNER_ITERATION_COMPLETED = \"inner_iteration_completed\"\n\n\nclass GanKeys:\n    \"\"\"\n    A set of common keys for generative adversarial networks.\n\n    \"\"\"\n\n    REALS = \"reals\"\n    FAKES = \"fakes\"\n    LATENTS = \"latents\"\n    GLOSS = \"g_loss\"\n    DLOSS = \"d_loss\"\n\n\ndef get_devices_spec(devices: Optional[Sequence[torch.device]] = None) -> List[torch.device]:\n    \"\"\"\n    Get a valid specification for one or more devices. If `devices` is None get devices for all CUDA devices available.\n    If `devices` is and zero-length structure a single CPU compute device is returned. In any other cases `devices` is\n    returned unchanged.\n\n    Args:\n        devices: list of devices to request, None for all GPU devices, [] for CPU.\n\n    Raises:\n        RuntimeError: When all GPUs are selected (``devices=None``) but no GPUs are available.\n\n    Returns:\n        list of torch.device: list of devices.\n\n    \"\"\"\n    if devices is None:\n        devices = [torch.device(f\"cuda:{d:d}\") for d in range(torch.cuda.device_count())]\n\n        if len(devices) == 0:\n            raise RuntimeError(\"No GPU devices available.\")\n\n    elif len(devices) == 0:\n        devices = [torch.device(\"cpu\")]\n\n    else:\n        devices = list(devices)\n\n    return devices\n\n\ndef default_prepare_batch(\n    batchdata: Dict[str, torch.Tensor],\n    device: Optional[Union[str, torch.device]] = None,\n    non_blocking: bool = False,\n) -> Union[Tuple[torch.Tensor, Optional[torch.Tensor]], torch.Tensor]:\n    \"\"\"\n    Default function to prepare the data for current iteration.\n    Refer to ignite: https://github.com/pytorch/ignite/blob/v0.4.2/ignite/engine/__init__.py#L28.\n\n    Returns:\n        image, label(optional).\n\n    \"\"\"\n    if not isinstance(batchdata, dict):\n        raise AssertionError(\"default prepare_batch expects dictionary input data.\")\n    if isinstance(batchdata.get(CommonKeys.LABEL, None), torch.Tensor):\n        return (\n            batchdata[CommonKeys.IMAGE].to(device=device, non_blocking=non_blocking),\n            batchdata[CommonKeys.LABEL].to(device=device, non_blocking=non_blocking),\n        )\n    if GanKeys.REALS in batchdata:\n        return batchdata[GanKeys.REALS].to(device=device, non_blocking=non_blocking)\n    return batchdata[CommonKeys.IMAGE].to(device=device, non_blocking=non_blocking), None\n\n\ndef default_make_latent(\n    num_latents: int,\n    latent_size: int,\n    device: Optional[Union[str, torch.device]] = None,\n    non_blocking: bool = False,\n) -> torch.Tensor:\n    return torch.randn(num_latents, latent_size).to(device=device, non_blocking=non_blocking)\n\n\ndef engine_apply_transform(batch: Any, output: Any, transform: Callable):\n    \"\"\"\n    Apply transform for the engine.state.batch and engine.state.output.\n    If `batch` and `output` are dictionaries, temporarily combine them for the transform,\n    otherwise, apply the transform for `output` data only.\n\n    \"\"\"\n    if isinstance(batch, dict) and isinstance(output, dict):\n        data = dict(batch)\n        data.update(output)\n        data = apply_transform(transform, data)\n        for k, v in data.items():\n            # split the output data of post transforms into `output` and `batch`,\n            # `batch` should be read-only, so save the generated key-value into `output`\n            if k in output or k not in batch:\n                output[k] = v\n            else:\n                batch[k] = v\n    else:\n        output = apply_transform(transform, output)\n\n    return batch, output\n", "path": "monai/engines/utils.py"}, {"content": "# Copyright 2020 - 2021 MONAI Consortium\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#     http://www.apache.org/licenses/LICENSE-2.0\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom typing import Callable, Dict, Sequence, Union\n\nimport torch\n\nfrom monai.engines import SupervisedEvaluator, SupervisedTrainer\nfrom monai.engines.utils import IterationEvents\nfrom monai.transforms import Compose\nfrom monai.utils.enums import CommonKeys\n\n\nclass Interaction:\n    \"\"\"\n    Ignite process_function used to introduce interactions (simulation of clicks) for Deepgrow Training/Evaluation.\n    This implementation is based on:\n\n        Sakinis et al., Interactive segmentation of medical images through\n        fully convolutional neural networks. (2019) https://arxiv.org/abs/1903.08205\n\n    Args:\n        transforms: execute additional transformation during every iteration (before train).\n            Typically, several Tensor based transforms composed by `Compose`.\n        max_interactions: maximum number of interactions per iteration\n        train: training or evaluation\n        key_probability: field name to fill probability for every interaction\n    \"\"\"\n\n    def __init__(\n        self,\n        transforms: Union[Sequence[Callable], Callable],\n        max_interactions: int,\n        train: bool,\n        key_probability: str = \"probability\",\n    ) -> None:\n\n        if not isinstance(transforms, Compose):\n            transforms = Compose(transforms)\n\n        self.transforms = transforms\n        self.max_interactions = max_interactions\n        self.train = train\n        self.key_probability = key_probability\n\n    def __call__(self, engine: Union[SupervisedTrainer, SupervisedEvaluator], batchdata: Dict[str, torch.Tensor]):\n        if batchdata is None:\n            raise ValueError(\"Must provide batch data for current iteration.\")\n\n        for j in range(self.max_interactions):\n            inputs, _ = engine.prepare_batch(batchdata)\n            inputs = inputs.to(engine.state.device)\n\n            engine.fire_event(IterationEvents.INNER_ITERATION_STARTED)\n\n            engine.network.eval()\n            with torch.no_grad():\n                if engine.amp:\n                    with torch.cuda.amp.autocast():\n                        predictions = engine.inferer(inputs, engine.network)\n                else:\n                    predictions = engine.inferer(inputs, engine.network)\n\n            engine.fire_event(IterationEvents.INNER_ITERATION_COMPLETED)\n\n            batchdata.update({CommonKeys.PRED: predictions})\n            batchdata[self.key_probability] = torch.as_tensor(\n                ([1.0 - ((1.0 / self.max_interactions) * j)] if self.train else [1.0]) * len(inputs)\n            )\n            batchdata = self.transforms(batchdata)\n\n        return engine._iteration(engine, batchdata)\n", "path": "monai/apps/deepgrow/interaction.py"}]}