We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Documentation improvements for Randomizable
In here: https://docs.monai.io/en/latest/transforms.html#randomizable

From the text description.
I think it is not clear that this self.R is a class variable instead of an instance variable.
And it is also not clear that self.R is of type np.random.RandomState

--- 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/transforms/compose.py`
Content:
```
1 # Copyright 2020 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 A collection of generic interfaces for MONAI transforms.
13 """
14 
15 import warnings
16 from typing import Hashable, Optional, Tuple, Any
17 from abc import ABC, abstractmethod
18 import numpy as np
19 
20 from monai.config.type_definitions import KeysCollection
21 from monai.utils.misc import ensure_tuple, get_seed
22 from .utils import apply_transform
23 
24 
25 class Transform(ABC):
26     """
27     An abstract class of a ``Transform``.
28     A transform is callable that processes ``data``.
29 
30     It could be stateful and may modify ``data`` in place,
31     the implementation should be aware of:
32 
33         #. thread safety when mutating its own states.
34            When used from a multi-process context, transform's instance variables are read-only.
35         #. ``data`` content unused by this transform may still be used in the
36            subsequent transforms in a composed transform.
37         #. storing too much information in ``data`` may not scale.
38 
39     See Also
40 
41         :py:class:`monai.transforms.Compose`
42     """
43 
44     @abstractmethod
45     def __call__(self, data: Any):
46         """
47         ``data`` is an element which often comes from an iteration over an
48         iterable, such as :py:class:`torch.utils.data.Dataset`. This method should
49         return an updated version of ``data``.
50         To simplify the input validations, most of the transforms assume that
51 
52         - ``data`` is a Numpy ndarray, PyTorch Tensor or string
53         - the data shape can be:
54 
55           #. string data without shape, `LoadNifti` and `LoadPNG` transforms expect file paths
56           #. most of the pre-processing transforms expect: ``(num_channels, spatial_dim_1[, spatial_dim_2, ...])``,
57              except that `AddChannel` expects (spatial_dim_1[, spatial_dim_2, ...]) and
58              `AsChannelFirst` expects (spatial_dim_1[, spatial_dim_2, ...], num_channels)
59           #. most of the post-processing transforms expect
60              ``(batch_size, num_channels, spatial_dim_1[, spatial_dim_2, ...])``
61 
62         - the channel dimension is not omitted even if number of channels is one
63 
64         This method can optionally take additional arguments to help execute transformation operation.
65 
66         Raises:
67             NotImplementedError: Subclass {self.__class__.__name__} must implement the compute method
68 
69         """
70         raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement the compute method")
71 
72 
73 class Randomizable(ABC):
74     """
75     An interface for handling local numpy random state.
76     this is mainly for randomized data augmentation transforms.
77     """
78 
79     R: np.random.RandomState = np.random.RandomState()
80 
81     def set_random_state(self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None):
82         """
83         Set the random state locally, to control the randomness, the derived
84         classes should use :py:attr:`self.R` instead of `np.random` to introduce random
85         factors.
86 
87         Args:
88             seed: set the random state with an integer seed.
89             state (np.random.RandomState): set the random state with a `np.random.RandomState` object.
90 
91         Returns:
92             a Randomizable instance.
93 
94         Raises:
95             ValueError: `state` must be a `np.random.RandomState`, got {type(state)}
96 
97         """
98         if seed is not None:
99             _seed = id(seed) if not isinstance(seed, int) else seed
100             self.R = np.random.RandomState(_seed)
101             return self
102 
103         if state is not None:
104             if not isinstance(state, np.random.RandomState):
105                 raise ValueError(f"`state` must be a `np.random.RandomState`, got {type(state)}")
106             self.R = state
107             return self
108 
109         self.R = np.random.RandomState()
110         return self
111 
112     @abstractmethod
113     def randomize(self):
114         """
115         Within this method, :py:attr:`self.R` should be used, instead of `np.random`, to introduce random factors.
116 
117         all :py:attr:`self.R` calls happen here so that we have a better chance to
118         identify errors of sync the random state.
119 
120         This method can optionally take additional arguments so that the random factors are generated based on
121         properties of the input data.
122 
123         Raises:
124             NotImplementedError: Subclass {self.__class__.__name__} must implement the compute method
125 
126         """
127         raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement the compute method")
128 
129 
130 class Compose(Randomizable):
131     """
132     ``Compose`` provides the ability to chain a series of calls together in a
133     sequence. Each transform in the sequence must take a single argument and
134     return a single value, so that the transforms can be called in a chain.
135 
136     ``Compose`` can be used in two ways:
137 
138     #. With a series of transforms that accept and return a single
139        ndarray / tensor / tensor-like parameter.
140     #. With a series of transforms that accept and return a dictionary that
141        contains one or more parameters. Such transforms must have pass-through
142        semantics; unused values in the dictionary must be copied to the return
143        dictionary. It is required that the dictionary is copied between input
144        and output of each transform.
145 
146     If some transform generates a list batch of data in the transform chain,
147     every item in the list is still a dictionary, and all the following
148     transforms will apply to every item of the list, for example:
149 
150     #. transformA normalizes the intensity of 'img' field in the dict data.
151     #. transformB crops out a list batch of images on 'img' and 'seg' field.
152        And constructs a list of dict data, other fields are copied::
153 
154             {                          [{                   {
155                 'img': [1, 2],              'img': [1],         'img': [2],
156                 'seg': [1, 2],              'seg': [1],         'seg': [2],
157                 'extra': 123,    -->        'extra': 123,       'extra': 123,
158                 'shape': 'CHWD'             'shape': 'CHWD'     'shape': 'CHWD'
159             }                           },                  }]
160 
161     #. transformC then randomly rotates or flips 'img' and 'seg' fields of
162        every dictionary item in the list.
163 
164     The composed transforms will be set the same global random seed if user called
165     `set_determinism()`.
166 
167     When using the pass-through dictionary operation, you can make use of
168     :class:`monai.transforms.adaptors.adaptor` to wrap transforms that don't conform
169     to the requirements. This approach allows you to use transforms from
170     otherwise incompatible libraries with minimal additional work.
171 
172     Note:
173 
174         In many cases, Compose is not the best way to create pre-processing
175         pipelines. Pre-processing is often not a strictly sequential series of
176         operations, and much of the complexity arises when a not-sequential
177         set of functions must be called as if it were a sequence.
178 
179         Example: images and labels
180         Images typically require some kind of normalisation that labels do not.
181         Both are then typically augmented through the use of random rotations,
182         flips, and deformations.
183         Compose can be used with a series of transforms that take a dictionary
184         that contains 'image' and 'label' entries. This might require wrapping
185         `torchvision` transforms before passing them to compose.
186         Alternatively, one can create a class with a `__call__` function that
187         calls your pre-processing functions taking into account that not all of
188         them are called on the labels.
189     """
190 
191     def __init__(self, transforms=None) -> None:
192         if transforms is None:
193             transforms = []
194         if not isinstance(transforms, (list, tuple)):
195             raise ValueError("Parameters 'transforms' must be a list or tuple.")
196         self.transforms = transforms
197         self.set_random_state(seed=get_seed())
198 
199     def set_random_state(self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None):
200         for _transform in self.transforms:
201             if not isinstance(_transform, Randomizable):
202                 continue
203             _transform.set_random_state(seed, state)
204 
205     def randomize(self):
206         for _transform in self.transforms:
207             if not isinstance(_transform, Randomizable):
208                 continue
209             try:
210                 _transform.randomize()
211             except TypeError as type_error:
212                 tfm_name: str = type(_transform).__name__
213                 warnings.warn(
214                     f'Transform "{tfm_name}" in Compose not randomized\n{tfm_name}.{type_error}.', RuntimeWarning
215                 )
216 
217     def __call__(self, input_):
218         for _transform in self.transforms:
219             input_ = apply_transform(_transform, input_)
220         return input_
221 
222 
223 class MapTransform(Transform):
224     """
225     A subclass of :py:class:`monai.transforms.Transform` with an assumption
226     that the ``data`` input of ``self.__call__`` is a MutableMapping such as ``dict``.
227 
228     The ``keys`` parameter will be used to get and set the actual data
229     item to transform.  That is, the callable of this transform should
230     follow the pattern:
231 
232         .. code-block:: python
233 
234             def __call__(self, data):
235                 for key in self.keys:
236                     if key in data:
237                         # update output data with some_transform_function(data[key]).
238                     else:
239                         # do nothing or some exceptions handling.
240                 return data
241 
242     """
243 
244     def __init__(self, keys: KeysCollection):
245         self.keys: Tuple[Any, ...] = ensure_tuple(keys)
246         if not self.keys:
247             raise ValueError("keys unspecified")
248         for key in self.keys:
249             if not isinstance(key, Hashable):
250                 raise ValueError(f"keys should be a hashable or a sequence of hashables, got {type(key)}")
251 
252     @abstractmethod
253     def __call__(self, data):
254         """
255         ``data`` often comes from an iteration over an iterable,
256         such as :py:class:`torch.utils.data.Dataset`.
257 
258         To simplify the input validations, this method assumes:
259 
260         - ``data`` is a Python dictionary
261         - ``data[key]`` is a Numpy ndarray, PyTorch Tensor or string, where ``key`` is an element
262           of ``self.keys``, the data shape can be:
263 
264           #. string data without shape, `LoadNiftid` and `LoadPNGd` transforms expect file paths
265           #. most of the pre-processing transforms expect: ``(num_channels, spatial_dim_1[, spatial_dim_2, ...])``,
266              except that `AddChanneld` expects (spatial_dim_1[, spatial_dim_2, ...]) and
267              `AsChannelFirstd` expects (spatial_dim_1[, spatial_dim_2, ...], num_channels)
268           #. most of the post-processing transforms expect
269              ``(batch_size, num_channels, spatial_dim_1[, spatial_dim_2, ...])``
270 
271         - the channel dimension is not omitted even if number of channels is one
272 
273         returns:
274             An updated dictionary version of ``data`` by applying the transform.
275         """
276         raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement the compute method")
277
```
--- 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/transforms/compose.py b/monai/transforms/compose.py
--- a/monai/transforms/compose.py
+++ b/monai/transforms/compose.py
@@ -72,8 +72,20 @@
 
 class Randomizable(ABC):
     """
-    An interface for handling local numpy random state.
-    this is mainly for randomized data augmentation transforms.
+    An interface for handling random state locally, currently based on a class variable `R`,
+    which is an instance of `np.random.RandomState`.
+    This is mainly for randomized data augmentation transforms. For example::
+
+        class RandShiftIntensity(Randomizable):
+            def randomize():
+                self._offset = self.R.uniform(low=0, high=100)
+            def __call__(self, img):
+                self.randomize()
+                return img + self._offset
+
+        transform = RandShiftIntensity()
+        transform.set_random_state(seed=0)
+
     """
 
     R: np.random.RandomState = np.random.RandomState()

{"golden_diff": "diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py\n--- a/monai/transforms/compose.py\n+++ b/monai/transforms/compose.py\n@@ -72,8 +72,20 @@\n \n class Randomizable(ABC):\n     \"\"\"\n-    An interface for handling local numpy random state.\n-    this is mainly for randomized data augmentation transforms.\n+    An interface for handling random state locally, currently based on a class variable `R`,\n+    which is an instance of `np.random.RandomState`.\n+    This is mainly for randomized data augmentation transforms. For example::\n+\n+        class RandShiftIntensity(Randomizable):\n+            def randomize():\n+                self._offset = self.R.uniform(low=0, high=100)\n+            def __call__(self, img):\n+                self.randomize()\n+                return img + self._offset\n+\n+        transform = RandShiftIntensity()\n+        transform.set_random_state(seed=0)\n+\n     \"\"\"\n \n     R: np.random.RandomState = np.random.RandomState()\n", "issue": "Documentation improvements for Randomizable\nIn here: https://docs.monai.io/en/latest/transforms.html#randomizable\r\n\r\nFrom the text description.\r\nI think it is not clear that this self.R is a class variable instead of an instance variable.\r\nAnd it is also not clear that self.R is of type np.random.RandomState\n", "before_files": [{"content": "# Copyright 2020 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\"\"\"\nA collection of generic interfaces for MONAI transforms.\n\"\"\"\n\nimport warnings\nfrom typing import Hashable, Optional, Tuple, Any\nfrom abc import ABC, abstractmethod\nimport numpy as np\n\nfrom monai.config.type_definitions import KeysCollection\nfrom monai.utils.misc import ensure_tuple, get_seed\nfrom .utils import apply_transform\n\n\nclass Transform(ABC):\n    \"\"\"\n    An abstract class of a ``Transform``.\n    A transform is callable that processes ``data``.\n\n    It could be stateful and may modify ``data`` in place,\n    the implementation should be aware of:\n\n        #. thread safety when mutating its own states.\n           When used from a multi-process context, transform's instance variables are read-only.\n        #. ``data`` content unused by this transform may still be used in the\n           subsequent transforms in a composed transform.\n        #. storing too much information in ``data`` may not scale.\n\n    See Also\n\n        :py:class:`monai.transforms.Compose`\n    \"\"\"\n\n    @abstractmethod\n    def __call__(self, data: Any):\n        \"\"\"\n        ``data`` is an element which often comes from an iteration over an\n        iterable, such as :py:class:`torch.utils.data.Dataset`. This method should\n        return an updated version of ``data``.\n        To simplify the input validations, most of the transforms assume that\n\n        - ``data`` is a Numpy ndarray, PyTorch Tensor or string\n        - the data shape can be:\n\n          #. string data without shape, `LoadNifti` and `LoadPNG` transforms expect file paths\n          #. most of the pre-processing transforms expect: ``(num_channels, spatial_dim_1[, spatial_dim_2, ...])``,\n             except that `AddChannel` expects (spatial_dim_1[, spatial_dim_2, ...]) and\n             `AsChannelFirst` expects (spatial_dim_1[, spatial_dim_2, ...], num_channels)\n          #. most of the post-processing transforms expect\n             ``(batch_size, num_channels, spatial_dim_1[, spatial_dim_2, ...])``\n\n        - the channel dimension is not omitted even if number of channels is one\n\n        This method can optionally take additional arguments to help execute transformation operation.\n\n        Raises:\n            NotImplementedError: Subclass {self.__class__.__name__} must implement the compute method\n\n        \"\"\"\n        raise NotImplementedError(f\"Subclass {self.__class__.__name__} must implement the compute method\")\n\n\nclass Randomizable(ABC):\n    \"\"\"\n    An interface for handling local numpy random state.\n    this is mainly for randomized data augmentation transforms.\n    \"\"\"\n\n    R: np.random.RandomState = np.random.RandomState()\n\n    def set_random_state(self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None):\n        \"\"\"\n        Set the random state locally, to control the randomness, the derived\n        classes should use :py:attr:`self.R` instead of `np.random` to introduce random\n        factors.\n\n        Args:\n            seed: set the random state with an integer seed.\n            state (np.random.RandomState): set the random state with a `np.random.RandomState` object.\n\n        Returns:\n            a Randomizable instance.\n\n        Raises:\n            ValueError: `state` must be a `np.random.RandomState`, got {type(state)}\n\n        \"\"\"\n        if seed is not None:\n            _seed = id(seed) if not isinstance(seed, int) else seed\n            self.R = np.random.RandomState(_seed)\n            return self\n\n        if state is not None:\n            if not isinstance(state, np.random.RandomState):\n                raise ValueError(f\"`state` must be a `np.random.RandomState`, got {type(state)}\")\n            self.R = state\n            return self\n\n        self.R = np.random.RandomState()\n        return self\n\n    @abstractmethod\n    def randomize(self):\n        \"\"\"\n        Within this method, :py:attr:`self.R` should be used, instead of `np.random`, to introduce random factors.\n\n        all :py:attr:`self.R` calls happen here so that we have a better chance to\n        identify errors of sync the random state.\n\n        This method can optionally take additional arguments so that the random factors are generated based on\n        properties of the input data.\n\n        Raises:\n            NotImplementedError: Subclass {self.__class__.__name__} must implement the compute method\n\n        \"\"\"\n        raise NotImplementedError(f\"Subclass {self.__class__.__name__} must implement the compute method\")\n\n\nclass Compose(Randomizable):\n    \"\"\"\n    ``Compose`` provides the ability to chain a series of calls together in a\n    sequence. Each transform in the sequence must take a single argument and\n    return a single value, so that the transforms can be called in a chain.\n\n    ``Compose`` can be used in two ways:\n\n    #. With a series of transforms that accept and return a single\n       ndarray / tensor / tensor-like parameter.\n    #. With a series of transforms that accept and return a dictionary that\n       contains one or more parameters. Such transforms must have pass-through\n       semantics; unused values in the dictionary must be copied to the return\n       dictionary. It is required that the dictionary is copied between input\n       and output of each transform.\n\n    If some transform generates a list batch of data in the transform chain,\n    every item in the list is still a dictionary, and all the following\n    transforms will apply to every item of the list, for example:\n\n    #. transformA normalizes the intensity of 'img' field in the dict data.\n    #. transformB crops out a list batch of images on 'img' and 'seg' field.\n       And constructs a list of dict data, other fields are copied::\n\n            {                          [{                   {\n                'img': [1, 2],              'img': [1],         'img': [2],\n                'seg': [1, 2],              'seg': [1],         'seg': [2],\n                'extra': 123,    -->        'extra': 123,       'extra': 123,\n                'shape': 'CHWD'             'shape': 'CHWD'     'shape': 'CHWD'\n            }                           },                  }]\n\n    #. transformC then randomly rotates or flips 'img' and 'seg' fields of\n       every dictionary item in the list.\n\n    The composed transforms will be set the same global random seed if user called\n    `set_determinism()`.\n\n    When using the pass-through dictionary operation, you can make use of\n    :class:`monai.transforms.adaptors.adaptor` to wrap transforms that don't conform\n    to the requirements. This approach allows you to use transforms from\n    otherwise incompatible libraries with minimal additional work.\n\n    Note:\n\n        In many cases, Compose is not the best way to create pre-processing\n        pipelines. Pre-processing is often not a strictly sequential series of\n        operations, and much of the complexity arises when a not-sequential\n        set of functions must be called as if it were a sequence.\n\n        Example: images and labels\n        Images typically require some kind of normalisation that labels do not.\n        Both are then typically augmented through the use of random rotations,\n        flips, and deformations.\n        Compose can be used with a series of transforms that take a dictionary\n        that contains 'image' and 'label' entries. This might require wrapping\n        `torchvision` transforms before passing them to compose.\n        Alternatively, one can create a class with a `__call__` function that\n        calls your pre-processing functions taking into account that not all of\n        them are called on the labels.\n    \"\"\"\n\n    def __init__(self, transforms=None) -> None:\n        if transforms is None:\n            transforms = []\n        if not isinstance(transforms, (list, tuple)):\n            raise ValueError(\"Parameters 'transforms' must be a list or tuple.\")\n        self.transforms = transforms\n        self.set_random_state(seed=get_seed())\n\n    def set_random_state(self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None):\n        for _transform in self.transforms:\n            if not isinstance(_transform, Randomizable):\n                continue\n            _transform.set_random_state(seed, state)\n\n    def randomize(self):\n        for _transform in self.transforms:\n            if not isinstance(_transform, Randomizable):\n                continue\n            try:\n                _transform.randomize()\n            except TypeError as type_error:\n                tfm_name: str = type(_transform).__name__\n                warnings.warn(\n                    f'Transform \"{tfm_name}\" in Compose not randomized\\n{tfm_name}.{type_error}.', RuntimeWarning\n                )\n\n    def __call__(self, input_):\n        for _transform in self.transforms:\n            input_ = apply_transform(_transform, input_)\n        return input_\n\n\nclass MapTransform(Transform):\n    \"\"\"\n    A subclass of :py:class:`monai.transforms.Transform` with an assumption\n    that the ``data`` input of ``self.__call__`` is a MutableMapping such as ``dict``.\n\n    The ``keys`` parameter will be used to get and set the actual data\n    item to transform.  That is, the callable of this transform should\n    follow the pattern:\n\n        .. code-block:: python\n\n            def __call__(self, data):\n                for key in self.keys:\n                    if key in data:\n                        # update output data with some_transform_function(data[key]).\n                    else:\n                        # do nothing or some exceptions handling.\n                return data\n\n    \"\"\"\n\n    def __init__(self, keys: KeysCollection):\n        self.keys: Tuple[Any, ...] = ensure_tuple(keys)\n        if not self.keys:\n            raise ValueError(\"keys unspecified\")\n        for key in self.keys:\n            if not isinstance(key, Hashable):\n                raise ValueError(f\"keys should be a hashable or a sequence of hashables, got {type(key)}\")\n\n    @abstractmethod\n    def __call__(self, data):\n        \"\"\"\n        ``data`` often comes from an iteration over an iterable,\n        such as :py:class:`torch.utils.data.Dataset`.\n\n        To simplify the input validations, this method assumes:\n\n        - ``data`` is a Python dictionary\n        - ``data[key]`` is a Numpy ndarray, PyTorch Tensor or string, where ``key`` is an element\n          of ``self.keys``, the data shape can be:\n\n          #. string data without shape, `LoadNiftid` and `LoadPNGd` transforms expect file paths\n          #. most of the pre-processing transforms expect: ``(num_channels, spatial_dim_1[, spatial_dim_2, ...])``,\n             except that `AddChanneld` expects (spatial_dim_1[, spatial_dim_2, ...]) and\n             `AsChannelFirstd` expects (spatial_dim_1[, spatial_dim_2, ...], num_channels)\n          #. most of the post-processing transforms expect\n             ``(batch_size, num_channels, spatial_dim_1[, spatial_dim_2, ...])``\n\n        - the channel dimension is not omitted even if number of channels is one\n\n        returns:\n            An updated dictionary version of ``data`` by applying the transform.\n        \"\"\"\n        raise NotImplementedError(f\"Subclass {self.__class__.__name__} must implement the compute method\")\n", "path": "monai/transforms/compose.py"}], "after_files": [{"content": "# Copyright 2020 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\"\"\"\nA collection of generic interfaces for MONAI transforms.\n\"\"\"\n\nimport warnings\nfrom typing import Hashable, Optional, Tuple, Any\nfrom abc import ABC, abstractmethod\nimport numpy as np\n\nfrom monai.config.type_definitions import KeysCollection\nfrom monai.utils.misc import ensure_tuple, get_seed\nfrom .utils import apply_transform\n\n\nclass Transform(ABC):\n    \"\"\"\n    An abstract class of a ``Transform``.\n    A transform is callable that processes ``data``.\n\n    It could be stateful and may modify ``data`` in place,\n    the implementation should be aware of:\n\n        #. thread safety when mutating its own states.\n           When used from a multi-process context, transform's instance variables are read-only.\n        #. ``data`` content unused by this transform may still be used in the\n           subsequent transforms in a composed transform.\n        #. storing too much information in ``data`` may not scale.\n\n    See Also\n\n        :py:class:`monai.transforms.Compose`\n    \"\"\"\n\n    @abstractmethod\n    def __call__(self, data: Any):\n        \"\"\"\n        ``data`` is an element which often comes from an iteration over an\n        iterable, such as :py:class:`torch.utils.data.Dataset`. This method should\n        return an updated version of ``data``.\n        To simplify the input validations, most of the transforms assume that\n\n        - ``data`` is a Numpy ndarray, PyTorch Tensor or string\n        - the data shape can be:\n\n          #. string data without shape, `LoadNifti` and `LoadPNG` transforms expect file paths\n          #. most of the pre-processing transforms expect: ``(num_channels, spatial_dim_1[, spatial_dim_2, ...])``,\n             except that `AddChannel` expects (spatial_dim_1[, spatial_dim_2, ...]) and\n             `AsChannelFirst` expects (spatial_dim_1[, spatial_dim_2, ...], num_channels)\n          #. most of the post-processing transforms expect\n             ``(batch_size, num_channels, spatial_dim_1[, spatial_dim_2, ...])``\n\n        - the channel dimension is not omitted even if number of channels is one\n\n        This method can optionally take additional arguments to help execute transformation operation.\n\n        Raises:\n            NotImplementedError: Subclass {self.__class__.__name__} must implement the compute method\n\n        \"\"\"\n        raise NotImplementedError(f\"Subclass {self.__class__.__name__} must implement the compute method\")\n\n\nclass Randomizable(ABC):\n    \"\"\"\n    An interface for handling random state locally, currently based on a class variable `R`,\n    which is an instance of `np.random.RandomState`.\n    This is mainly for randomized data augmentation transforms. For example::\n\n        class RandShiftIntensity(Randomizable):\n            def randomize():\n                self._offset = self.R.uniform(low=0, high=100)\n            def __call__(self, img):\n                self.randomize()\n                return img + self._offset\n\n        transform = RandShiftIntensity()\n        transform.set_random_state(seed=0)\n\n    \"\"\"\n\n    R: np.random.RandomState = np.random.RandomState()\n\n    def set_random_state(self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None):\n        \"\"\"\n        Set the random state locally, to control the randomness, the derived\n        classes should use :py:attr:`self.R` instead of `np.random` to introduce random\n        factors.\n\n        Args:\n            seed: set the random state with an integer seed.\n            state (np.random.RandomState): set the random state with a `np.random.RandomState` object.\n\n        Returns:\n            a Randomizable instance.\n\n        Raises:\n            ValueError: `state` must be a `np.random.RandomState`, got {type(state)}\n\n        \"\"\"\n        if seed is not None:\n            _seed = id(seed) if not isinstance(seed, int) else seed\n            self.R = np.random.RandomState(_seed)\n            return self\n\n        if state is not None:\n            if not isinstance(state, np.random.RandomState):\n                raise ValueError(f\"`state` must be a `np.random.RandomState`, got {type(state)}\")\n            self.R = state\n            return self\n\n        self.R = np.random.RandomState()\n        return self\n\n    @abstractmethod\n    def randomize(self):\n        \"\"\"\n        Within this method, :py:attr:`self.R` should be used, instead of `np.random`, to introduce random factors.\n\n        all :py:attr:`self.R` calls happen here so that we have a better chance to\n        identify errors of sync the random state.\n\n        This method can optionally take additional arguments so that the random factors are generated based on\n        properties of the input data.\n\n        Raises:\n            NotImplementedError: Subclass {self.__class__.__name__} must implement the compute method\n\n        \"\"\"\n        raise NotImplementedError(f\"Subclass {self.__class__.__name__} must implement the compute method\")\n\n\nclass Compose(Randomizable):\n    \"\"\"\n    ``Compose`` provides the ability to chain a series of calls together in a\n    sequence. Each transform in the sequence must take a single argument and\n    return a single value, so that the transforms can be called in a chain.\n\n    ``Compose`` can be used in two ways:\n\n    #. With a series of transforms that accept and return a single\n       ndarray / tensor / tensor-like parameter.\n    #. With a series of transforms that accept and return a dictionary that\n       contains one or more parameters. Such transforms must have pass-through\n       semantics; unused values in the dictionary must be copied to the return\n       dictionary. It is required that the dictionary is copied between input\n       and output of each transform.\n\n    If some transform generates a list batch of data in the transform chain,\n    every item in the list is still a dictionary, and all the following\n    transforms will apply to every item of the list, for example:\n\n    #. transformA normalizes the intensity of 'img' field in the dict data.\n    #. transformB crops out a list batch of images on 'img' and 'seg' field.\n       And constructs a list of dict data, other fields are copied::\n\n            {                          [{                   {\n                'img': [1, 2],              'img': [1],         'img': [2],\n                'seg': [1, 2],              'seg': [1],         'seg': [2],\n                'extra': 123,    -->        'extra': 123,       'extra': 123,\n                'shape': 'CHWD'             'shape': 'CHWD'     'shape': 'CHWD'\n            }                           },                  }]\n\n    #. transformC then randomly rotates or flips 'img' and 'seg' fields of\n       every dictionary item in the list.\n\n    The composed transforms will be set the same global random seed if user called\n    `set_determinism()`.\n\n    When using the pass-through dictionary operation, you can make use of\n    :class:`monai.transforms.adaptors.adaptor` to wrap transforms that don't conform\n    to the requirements. This approach allows you to use transforms from\n    otherwise incompatible libraries with minimal additional work.\n\n    Note:\n\n        In many cases, Compose is not the best way to create pre-processing\n        pipelines. Pre-processing is often not a strictly sequential series of\n        operations, and much of the complexity arises when a not-sequential\n        set of functions must be called as if it were a sequence.\n\n        Example: images and labels\n        Images typically require some kind of normalisation that labels do not.\n        Both are then typically augmented through the use of random rotations,\n        flips, and deformations.\n        Compose can be used with a series of transforms that take a dictionary\n        that contains 'image' and 'label' entries. This might require wrapping\n        `torchvision` transforms before passing them to compose.\n        Alternatively, one can create a class with a `__call__` function that\n        calls your pre-processing functions taking into account that not all of\n        them are called on the labels.\n    \"\"\"\n\n    def __init__(self, transforms=None) -> None:\n        if transforms is None:\n            transforms = []\n        if not isinstance(transforms, (list, tuple)):\n            raise ValueError(\"Parameters 'transforms' must be a list or tuple.\")\n        self.transforms = transforms\n        self.set_random_state(seed=get_seed())\n\n    def set_random_state(self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None):\n        for _transform in self.transforms:\n            if not isinstance(_transform, Randomizable):\n                continue\n            _transform.set_random_state(seed, state)\n\n    def randomize(self):\n        for _transform in self.transforms:\n            if not isinstance(_transform, Randomizable):\n                continue\n            try:\n                _transform.randomize()\n            except TypeError as type_error:\n                tfm_name: str = type(_transform).__name__\n                warnings.warn(\n                    f'Transform \"{tfm_name}\" in Compose not randomized\\n{tfm_name}.{type_error}.', RuntimeWarning\n                )\n\n    def __call__(self, input_):\n        for _transform in self.transforms:\n            input_ = apply_transform(_transform, input_)\n        return input_\n\n\nclass MapTransform(Transform):\n    \"\"\"\n    A subclass of :py:class:`monai.transforms.Transform` with an assumption\n    that the ``data`` input of ``self.__call__`` is a MutableMapping such as ``dict``.\n\n    The ``keys`` parameter will be used to get and set the actual data\n    item to transform.  That is, the callable of this transform should\n    follow the pattern:\n\n        .. code-block:: python\n\n            def __call__(self, data):\n                for key in self.keys:\n                    if key in data:\n                        # update output data with some_transform_function(data[key]).\n                    else:\n                        # do nothing or some exceptions handling.\n                return data\n\n    \"\"\"\n\n    def __init__(self, keys: KeysCollection):\n        self.keys: Tuple[Any, ...] = ensure_tuple(keys)\n        if not self.keys:\n            raise ValueError(\"keys unspecified\")\n        for key in self.keys:\n            if not isinstance(key, Hashable):\n                raise ValueError(f\"keys should be a hashable or a sequence of hashables, got {type(key)}\")\n\n    @abstractmethod\n    def __call__(self, data):\n        \"\"\"\n        ``data`` often comes from an iteration over an iterable,\n        such as :py:class:`torch.utils.data.Dataset`.\n\n        To simplify the input validations, this method assumes:\n\n        - ``data`` is a Python dictionary\n        - ``data[key]`` is a Numpy ndarray, PyTorch Tensor or string, where ``key`` is an element\n          of ``self.keys``, the data shape can be:\n\n          #. string data without shape, `LoadNiftid` and `LoadPNGd` transforms expect file paths\n          #. most of the pre-processing transforms expect: ``(num_channels, spatial_dim_1[, spatial_dim_2, ...])``,\n             except that `AddChanneld` expects (spatial_dim_1[, spatial_dim_2, ...]) and\n             `AsChannelFirstd` expects (spatial_dim_1[, spatial_dim_2, ...], num_channels)\n          #. most of the post-processing transforms expect\n             ``(batch_size, num_channels, spatial_dim_1[, spatial_dim_2, ...])``\n\n        - the channel dimension is not omitted even if number of channels is one\n\n        returns:\n            An updated dictionary version of ``data`` by applying the transform.\n        \"\"\"\n        raise NotImplementedError(f\"Subclass {self.__class__.__name__} must implement the compute method\")\n", "path": "monai/transforms/compose.py"}]}