We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[BUG] TruncatedNormal crashing when computing entropy
## Describe the bug

Calling `.entropy()` on a `TruncatedNormal` distribution causes the code to crash.

## To Reproduce

First crash happened using a PPO agent with entropy bonus turned on and actor parametrized with a `TruncatedNormal`.
A simple snippet to reproduce is the following:

```python
import torch
from torchrl.modules.distributions import IndependentNormal, TruncatedNormal

if __name__ == '__main__':
	loc, scale = torch.zeros(1), torch.ones(1)
	d1 = IndependentNormal(loc, scale)
	print(d1.entropy())
	
	d2 = TruncatedNormal(loc, scale)
	print(d2.entropy())
```

```bash
tensor(1.4189)
Traceback (most recent call last):
  File "/home/diego/Desktop/test.py", line 10, in <module>
    print(d2.entropy())
  File "/home/diego/miniconda3/envs/pytorch/lib/python3.10/site-packages/torch/distributions/independent.py", line 103, in entropy
    entropy = self.base_dist.entropy()
TypeError: 'Tensor' object is not callable

```

## Expected behavior

The entropy value should be returned.

## System info
* Python 3.10.12
* torch 2.0.1

```python
import torchrl, numpy, sys
print(torchrl.__version__, numpy.__version__, sys.version, sys.platform)
```
```
0.1.1 1.25.1 3.10.12 (main, Jul  5 2023, 18:54:27) [GCC 11.2.0] linux
```
## Reason and Possible fixes

In the `TruncatedStandardNormal` class, the `self._entropy` attribute is a constant tensor computed at initialization. For some reason, calling `TruncatedStandardNormal.entropy` returns the `self._entropy` attribute, rather than the `entropy()` property:

```python
import torch
from torchrl.modules.distributions.truncated_normal import TruncatedStandardNormal
loc, scale = torch.zeros(1), torch.ones(1)
print(TruncatedStandardNormal(loc, scale).entropy)
print(TruncatedStandardNormal(loc, scale).entropy())
```

```bash
tensor([-0.0104])
Traceback (most recent call last):
  File "/home/diego/Desktop/test.py", line 5, in <module>
    print(TruncatedStandardNormal(loc, scale).entropy())
TypeError: 'Tensor' object is not callable

```

## Checklist

- [x] I have checked that there is no similar issue in the repo (**required**)
- [x] I have read the [documentation](https://github.com/pytorch/rl/tree/main/docs/) (**required**)
- [x] I have provided a minimal working example to reproduce the bug (**required**)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `torchrl/modules/distributions/truncated_normal.py`
Content:
```
1 # Copyright (c) Meta Platforms, Inc. and affiliates.
2 #
3 # This source code is licensed under the MIT license found in the
4 # LICENSE file in the root directory of this source tree.
5 
6 
7 # from https://github.com/toshas/torch_truncnorm
8 
9 import math
10 from numbers import Number
11 
12 import torch
13 from torch.distributions import constraints, Distribution
14 from torch.distributions.utils import broadcast_all
15 
16 CONST_SQRT_2 = math.sqrt(2)
17 CONST_INV_SQRT_2PI = 1 / math.sqrt(2 * math.pi)
18 CONST_INV_SQRT_2 = 1 / math.sqrt(2)
19 CONST_LOG_INV_SQRT_2PI = math.log(CONST_INV_SQRT_2PI)
20 CONST_LOG_SQRT_2PI_E = 0.5 * math.log(2 * math.pi * math.e)
21 
22 
23 class TruncatedStandardNormal(Distribution):
24     """Truncated Standard Normal distribution.
25 
26     Source: https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf
27     """
28 
29     arg_constraints = {
30         "a": constraints.real,
31         "b": constraints.real,
32     }
33     has_rsample = True
34     eps = 1e-6
35 
36     def __init__(self, a, b, validate_args=None):
37         self.a, self.b = broadcast_all(a, b)
38         if isinstance(a, Number) and isinstance(b, Number):
39             batch_shape = torch.Size()
40         else:
41             batch_shape = self.a.size()
42         super(TruncatedStandardNormal, self).__init__(
43             batch_shape, validate_args=validate_args
44         )
45         if self.a.dtype != self.b.dtype:
46             raise ValueError("Truncation bounds types are different")
47         if any(
48             (self.a >= self.b)
49             .view(
50                 -1,
51             )
52             .tolist()
53         ):
54             raise ValueError("Incorrect truncation range")
55         eps = self.eps
56         self._dtype_min_gt_0 = eps
57         self._dtype_max_lt_1 = 1 - eps
58         self._little_phi_a = self._little_phi(self.a)
59         self._little_phi_b = self._little_phi(self.b)
60         self._big_phi_a = self._big_phi(self.a)
61         self._big_phi_b = self._big_phi(self.b)
62         self._Z = (self._big_phi_b - self._big_phi_a).clamp(eps, 1 - eps)
63         self._log_Z = self._Z.log()
64         little_phi_coeff_a = torch.nan_to_num(self.a, nan=math.nan)
65         little_phi_coeff_b = torch.nan_to_num(self.b, nan=math.nan)
66         self._lpbb_m_lpaa_d_Z = (
67             self._little_phi_b * little_phi_coeff_b
68             - self._little_phi_a * little_phi_coeff_a
69         ) / self._Z
70         self._mean = -(self._little_phi_b - self._little_phi_a) / self._Z
71         self._variance = (
72             1
73             - self._lpbb_m_lpaa_d_Z
74             - ((self._little_phi_b - self._little_phi_a) / self._Z) ** 2
75         )
76         self._entropy = CONST_LOG_SQRT_2PI_E + self._log_Z - 0.5 * self._lpbb_m_lpaa_d_Z
77 
78     @constraints.dependent_property
79     def support(self):
80         return constraints.interval(self.a, self.b)
81 
82     @property
83     def mean(self):
84         return self._mean
85 
86     @property
87     def variance(self):
88         return self._variance
89 
90     @property
91     def entropy(self):
92         return self._entropy
93 
94     @property
95     def auc(self):
96         return self._Z
97 
98     @staticmethod
99     def _little_phi(x):
100         return (-(x**2) * 0.5).exp() * CONST_INV_SQRT_2PI
101 
102     def _big_phi(self, x):
103         phi = 0.5 * (1 + (x * CONST_INV_SQRT_2).erf())
104         return phi.clamp(self.eps, 1 - self.eps)
105 
106     @staticmethod
107     def _inv_big_phi(x):
108         return CONST_SQRT_2 * (2 * x - 1).erfinv()
109 
110     def cdf(self, value):
111         if self._validate_args:
112             self._validate_sample(value)
113         return ((self._big_phi(value) - self._big_phi_a) / self._Z).clamp(0, 1)
114 
115     def icdf(self, value):
116         y = self._big_phi_a + value * self._Z
117         y = y.clamp(self.eps, 1 - self.eps)
118         return self._inv_big_phi(y)
119 
120     def log_prob(self, value):
121         if self._validate_args:
122             self._validate_sample(value)
123         return CONST_LOG_INV_SQRT_2PI - self._log_Z - (value**2) * 0.5
124 
125     def rsample(self, sample_shape=None):
126         if sample_shape is None:
127             sample_shape = torch.Size([])
128         shape = self._extended_shape(sample_shape)
129         p = torch.empty(shape, device=self.a.device).uniform_(
130             self._dtype_min_gt_0, self._dtype_max_lt_1
131         )
132         return self.icdf(p)
133 
134 
135 class TruncatedNormal(TruncatedStandardNormal):
136     """Truncated Normal distribution.
137 
138     https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf
139     """
140 
141     has_rsample = True
142 
143     def __init__(self, loc, scale, a, b, validate_args=None):
144         scale = scale.clamp_min(self.eps)
145         self.loc, self.scale, a, b = broadcast_all(loc, scale, a, b)
146         self._non_std_a = a
147         self._non_std_b = b
148         a = (a - self.loc) / self.scale
149         b = (b - self.loc) / self.scale
150         super(TruncatedNormal, self).__init__(a, b, validate_args=validate_args)
151         self._log_scale = self.scale.log()
152         self._mean = self._mean * self.scale + self.loc
153         self._variance = self._variance * self.scale**2
154         self._entropy += self._log_scale
155 
156     def _to_std_rv(self, value):
157         return (value - self.loc) / self.scale
158 
159     def _from_std_rv(self, value):
160         return value * self.scale + self.loc
161 
162     def cdf(self, value):
163         return super(TruncatedNormal, self).cdf(self._to_std_rv(value))
164 
165     def icdf(self, value):
166         sample = self._from_std_rv(super().icdf(value))
167 
168         # clamp data but keep gradients
169         sample_clip = torch.stack(
170             [sample.detach(), self._non_std_a.detach().expand_as(sample)], 0
171         ).max(0)[0]
172         sample_clip = torch.stack(
173             [sample_clip, self._non_std_b.detach().expand_as(sample)], 0
174         ).min(0)[0]
175         sample.data.copy_(sample_clip)
176         return sample
177 
178     def log_prob(self, value):
179         value = self._to_std_rv(value)
180         return super(TruncatedNormal, self).log_prob(value) - self._log_scale
181
```
--- 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/torchrl/modules/distributions/truncated_normal.py b/torchrl/modules/distributions/truncated_normal.py
--- a/torchrl/modules/distributions/truncated_normal.py
+++ b/torchrl/modules/distributions/truncated_normal.py
@@ -87,7 +87,6 @@
     def variance(self):
         return self._variance
 
-    @property
     def entropy(self):
         return self._entropy

{"golden_diff": "diff --git a/torchrl/modules/distributions/truncated_normal.py b/torchrl/modules/distributions/truncated_normal.py\n--- a/torchrl/modules/distributions/truncated_normal.py\n+++ b/torchrl/modules/distributions/truncated_normal.py\n@@ -87,7 +87,6 @@\n     def variance(self):\n         return self._variance\n \n-    @property\n     def entropy(self):\n         return self._entropy\n", "issue": "[BUG] TruncatedNormal crashing when computing entropy\n## Describe the bug\r\n\r\nCalling `.entropy()` on a `TruncatedNormal` distribution causes the code to crash.\r\n\r\n## To Reproduce\r\n\r\nFirst crash happened using a PPO agent with entropy bonus turned on and actor parametrized with a `TruncatedNormal`.\r\nA simple snippet to reproduce is the following:\r\n\r\n```python\r\nimport torch\r\nfrom torchrl.modules.distributions import IndependentNormal, TruncatedNormal\r\n\r\nif __name__ == '__main__':\r\n\tloc, scale = torch.zeros(1), torch.ones(1)\r\n\td1 = IndependentNormal(loc, scale)\r\n\tprint(d1.entropy())\r\n\t\r\n\td2 = TruncatedNormal(loc, scale)\r\n\tprint(d2.entropy())\r\n```\r\n\r\n```bash\r\ntensor(1.4189)\r\nTraceback (most recent call last):\r\n  File \"/home/diego/Desktop/test.py\", line 10, in <module>\r\n    print(d2.entropy())\r\n  File \"/home/diego/miniconda3/envs/pytorch/lib/python3.10/site-packages/torch/distributions/independent.py\", line 103, in entropy\r\n    entropy = self.base_dist.entropy()\r\nTypeError: 'Tensor' object is not callable\r\n\r\n```\r\n\r\n## Expected behavior\r\n\r\nThe entropy value should be returned.\r\n\r\n## System info\r\n* Python 3.10.12\r\n* torch 2.0.1\r\n\r\n```python\r\nimport torchrl, numpy, sys\r\nprint(torchrl.__version__, numpy.__version__, sys.version, sys.platform)\r\n```\r\n```\r\n0.1.1 1.25.1 3.10.12 (main, Jul  5 2023, 18:54:27) [GCC 11.2.0] linux\r\n```\r\n## Reason and Possible fixes\r\n\r\nIn the `TruncatedStandardNormal` class, the `self._entropy` attribute is a constant tensor computed at initialization. For some reason, calling `TruncatedStandardNormal.entropy` returns the `self._entropy` attribute, rather than the `entropy()` property:\r\n\r\n```python\r\nimport torch\r\nfrom torchrl.modules.distributions.truncated_normal import TruncatedStandardNormal\r\nloc, scale = torch.zeros(1), torch.ones(1)\r\nprint(TruncatedStandardNormal(loc, scale).entropy)\r\nprint(TruncatedStandardNormal(loc, scale).entropy())\r\n```\r\n\r\n```bash\r\ntensor([-0.0104])\r\nTraceback (most recent call last):\r\n  File \"/home/diego/Desktop/test.py\", line 5, in <module>\r\n    print(TruncatedStandardNormal(loc, scale).entropy())\r\nTypeError: 'Tensor' object is not callable\r\n\r\n```\r\n\r\n## Checklist\r\n\r\n- [x] I have checked that there is no similar issue in the repo (**required**)\r\n- [x] I have read the [documentation](https://github.com/pytorch/rl/tree/main/docs/) (**required**)\r\n- [x] I have provided a minimal working example to reproduce the bug (**required**)\r\n\n", "before_files": [{"content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n\n# from https://github.com/toshas/torch_truncnorm\n\nimport math\nfrom numbers import Number\n\nimport torch\nfrom torch.distributions import constraints, Distribution\nfrom torch.distributions.utils import broadcast_all\n\nCONST_SQRT_2 = math.sqrt(2)\nCONST_INV_SQRT_2PI = 1 / math.sqrt(2 * math.pi)\nCONST_INV_SQRT_2 = 1 / math.sqrt(2)\nCONST_LOG_INV_SQRT_2PI = math.log(CONST_INV_SQRT_2PI)\nCONST_LOG_SQRT_2PI_E = 0.5 * math.log(2 * math.pi * math.e)\n\n\nclass TruncatedStandardNormal(Distribution):\n    \"\"\"Truncated Standard Normal distribution.\n\n    Source: https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf\n    \"\"\"\n\n    arg_constraints = {\n        \"a\": constraints.real,\n        \"b\": constraints.real,\n    }\n    has_rsample = True\n    eps = 1e-6\n\n    def __init__(self, a, b, validate_args=None):\n        self.a, self.b = broadcast_all(a, b)\n        if isinstance(a, Number) and isinstance(b, Number):\n            batch_shape = torch.Size()\n        else:\n            batch_shape = self.a.size()\n        super(TruncatedStandardNormal, self).__init__(\n            batch_shape, validate_args=validate_args\n        )\n        if self.a.dtype != self.b.dtype:\n            raise ValueError(\"Truncation bounds types are different\")\n        if any(\n            (self.a >= self.b)\n            .view(\n                -1,\n            )\n            .tolist()\n        ):\n            raise ValueError(\"Incorrect truncation range\")\n        eps = self.eps\n        self._dtype_min_gt_0 = eps\n        self._dtype_max_lt_1 = 1 - eps\n        self._little_phi_a = self._little_phi(self.a)\n        self._little_phi_b = self._little_phi(self.b)\n        self._big_phi_a = self._big_phi(self.a)\n        self._big_phi_b = self._big_phi(self.b)\n        self._Z = (self._big_phi_b - self._big_phi_a).clamp(eps, 1 - eps)\n        self._log_Z = self._Z.log()\n        little_phi_coeff_a = torch.nan_to_num(self.a, nan=math.nan)\n        little_phi_coeff_b = torch.nan_to_num(self.b, nan=math.nan)\n        self._lpbb_m_lpaa_d_Z = (\n            self._little_phi_b * little_phi_coeff_b\n            - self._little_phi_a * little_phi_coeff_a\n        ) / self._Z\n        self._mean = -(self._little_phi_b - self._little_phi_a) / self._Z\n        self._variance = (\n            1\n            - self._lpbb_m_lpaa_d_Z\n            - ((self._little_phi_b - self._little_phi_a) / self._Z) ** 2\n        )\n        self._entropy = CONST_LOG_SQRT_2PI_E + self._log_Z - 0.5 * self._lpbb_m_lpaa_d_Z\n\n    @constraints.dependent_property\n    def support(self):\n        return constraints.interval(self.a, self.b)\n\n    @property\n    def mean(self):\n        return self._mean\n\n    @property\n    def variance(self):\n        return self._variance\n\n    @property\n    def entropy(self):\n        return self._entropy\n\n    @property\n    def auc(self):\n        return self._Z\n\n    @staticmethod\n    def _little_phi(x):\n        return (-(x**2) * 0.5).exp() * CONST_INV_SQRT_2PI\n\n    def _big_phi(self, x):\n        phi = 0.5 * (1 + (x * CONST_INV_SQRT_2).erf())\n        return phi.clamp(self.eps, 1 - self.eps)\n\n    @staticmethod\n    def _inv_big_phi(x):\n        return CONST_SQRT_2 * (2 * x - 1).erfinv()\n\n    def cdf(self, value):\n        if self._validate_args:\n            self._validate_sample(value)\n        return ((self._big_phi(value) - self._big_phi_a) / self._Z).clamp(0, 1)\n\n    def icdf(self, value):\n        y = self._big_phi_a + value * self._Z\n        y = y.clamp(self.eps, 1 - self.eps)\n        return self._inv_big_phi(y)\n\n    def log_prob(self, value):\n        if self._validate_args:\n            self._validate_sample(value)\n        return CONST_LOG_INV_SQRT_2PI - self._log_Z - (value**2) * 0.5\n\n    def rsample(self, sample_shape=None):\n        if sample_shape is None:\n            sample_shape = torch.Size([])\n        shape = self._extended_shape(sample_shape)\n        p = torch.empty(shape, device=self.a.device).uniform_(\n            self._dtype_min_gt_0, self._dtype_max_lt_1\n        )\n        return self.icdf(p)\n\n\nclass TruncatedNormal(TruncatedStandardNormal):\n    \"\"\"Truncated Normal distribution.\n\n    https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf\n    \"\"\"\n\n    has_rsample = True\n\n    def __init__(self, loc, scale, a, b, validate_args=None):\n        scale = scale.clamp_min(self.eps)\n        self.loc, self.scale, a, b = broadcast_all(loc, scale, a, b)\n        self._non_std_a = a\n        self._non_std_b = b\n        a = (a - self.loc) / self.scale\n        b = (b - self.loc) / self.scale\n        super(TruncatedNormal, self).__init__(a, b, validate_args=validate_args)\n        self._log_scale = self.scale.log()\n        self._mean = self._mean * self.scale + self.loc\n        self._variance = self._variance * self.scale**2\n        self._entropy += self._log_scale\n\n    def _to_std_rv(self, value):\n        return (value - self.loc) / self.scale\n\n    def _from_std_rv(self, value):\n        return value * self.scale + self.loc\n\n    def cdf(self, value):\n        return super(TruncatedNormal, self).cdf(self._to_std_rv(value))\n\n    def icdf(self, value):\n        sample = self._from_std_rv(super().icdf(value))\n\n        # clamp data but keep gradients\n        sample_clip = torch.stack(\n            [sample.detach(), self._non_std_a.detach().expand_as(sample)], 0\n        ).max(0)[0]\n        sample_clip = torch.stack(\n            [sample_clip, self._non_std_b.detach().expand_as(sample)], 0\n        ).min(0)[0]\n        sample.data.copy_(sample_clip)\n        return sample\n\n    def log_prob(self, value):\n        value = self._to_std_rv(value)\n        return super(TruncatedNormal, self).log_prob(value) - self._log_scale\n", "path": "torchrl/modules/distributions/truncated_normal.py"}], "after_files": [{"content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n\n# from https://github.com/toshas/torch_truncnorm\n\nimport math\nfrom numbers import Number\n\nimport torch\nfrom torch.distributions import constraints, Distribution\nfrom torch.distributions.utils import broadcast_all\n\nCONST_SQRT_2 = math.sqrt(2)\nCONST_INV_SQRT_2PI = 1 / math.sqrt(2 * math.pi)\nCONST_INV_SQRT_2 = 1 / math.sqrt(2)\nCONST_LOG_INV_SQRT_2PI = math.log(CONST_INV_SQRT_2PI)\nCONST_LOG_SQRT_2PI_E = 0.5 * math.log(2 * math.pi * math.e)\n\n\nclass TruncatedStandardNormal(Distribution):\n    \"\"\"Truncated Standard Normal distribution.\n\n    Source: https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf\n    \"\"\"\n\n    arg_constraints = {\n        \"a\": constraints.real,\n        \"b\": constraints.real,\n    }\n    has_rsample = True\n    eps = 1e-6\n\n    def __init__(self, a, b, validate_args=None):\n        self.a, self.b = broadcast_all(a, b)\n        if isinstance(a, Number) and isinstance(b, Number):\n            batch_shape = torch.Size()\n        else:\n            batch_shape = self.a.size()\n        super(TruncatedStandardNormal, self).__init__(\n            batch_shape, validate_args=validate_args\n        )\n        if self.a.dtype != self.b.dtype:\n            raise ValueError(\"Truncation bounds types are different\")\n        if any(\n            (self.a >= self.b)\n            .view(\n                -1,\n            )\n            .tolist()\n        ):\n            raise ValueError(\"Incorrect truncation range\")\n        eps = self.eps\n        self._dtype_min_gt_0 = eps\n        self._dtype_max_lt_1 = 1 - eps\n        self._little_phi_a = self._little_phi(self.a)\n        self._little_phi_b = self._little_phi(self.b)\n        self._big_phi_a = self._big_phi(self.a)\n        self._big_phi_b = self._big_phi(self.b)\n        self._Z = (self._big_phi_b - self._big_phi_a).clamp(eps, 1 - eps)\n        self._log_Z = self._Z.log()\n        little_phi_coeff_a = torch.nan_to_num(self.a, nan=math.nan)\n        little_phi_coeff_b = torch.nan_to_num(self.b, nan=math.nan)\n        self._lpbb_m_lpaa_d_Z = (\n            self._little_phi_b * little_phi_coeff_b\n            - self._little_phi_a * little_phi_coeff_a\n        ) / self._Z\n        self._mean = -(self._little_phi_b - self._little_phi_a) / self._Z\n        self._variance = (\n            1\n            - self._lpbb_m_lpaa_d_Z\n            - ((self._little_phi_b - self._little_phi_a) / self._Z) ** 2\n        )\n        self._entropy = CONST_LOG_SQRT_2PI_E + self._log_Z - 0.5 * self._lpbb_m_lpaa_d_Z\n\n    @constraints.dependent_property\n    def support(self):\n        return constraints.interval(self.a, self.b)\n\n    @property\n    def mean(self):\n        return self._mean\n\n    @property\n    def variance(self):\n        return self._variance\n\n    def entropy(self):\n        return self._entropy\n\n    @property\n    def auc(self):\n        return self._Z\n\n    @staticmethod\n    def _little_phi(x):\n        return (-(x**2) * 0.5).exp() * CONST_INV_SQRT_2PI\n\n    def _big_phi(self, x):\n        phi = 0.5 * (1 + (x * CONST_INV_SQRT_2).erf())\n        return phi.clamp(self.eps, 1 - self.eps)\n\n    @staticmethod\n    def _inv_big_phi(x):\n        return CONST_SQRT_2 * (2 * x - 1).erfinv()\n\n    def cdf(self, value):\n        if self._validate_args:\n            self._validate_sample(value)\n        return ((self._big_phi(value) - self._big_phi_a) / self._Z).clamp(0, 1)\n\n    def icdf(self, value):\n        y = self._big_phi_a + value * self._Z\n        y = y.clamp(self.eps, 1 - self.eps)\n        return self._inv_big_phi(y)\n\n    def log_prob(self, value):\n        if self._validate_args:\n            self._validate_sample(value)\n        return CONST_LOG_INV_SQRT_2PI - self._log_Z - (value**2) * 0.5\n\n    def rsample(self, sample_shape=None):\n        if sample_shape is None:\n            sample_shape = torch.Size([])\n        shape = self._extended_shape(sample_shape)\n        p = torch.empty(shape, device=self.a.device).uniform_(\n            self._dtype_min_gt_0, self._dtype_max_lt_1\n        )\n        return self.icdf(p)\n\n\nclass TruncatedNormal(TruncatedStandardNormal):\n    \"\"\"Truncated Normal distribution.\n\n    https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf\n    \"\"\"\n\n    has_rsample = True\n\n    def __init__(self, loc, scale, a, b, validate_args=None):\n        scale = scale.clamp_min(self.eps)\n        self.loc, self.scale, a, b = broadcast_all(loc, scale, a, b)\n        self._non_std_a = a\n        self._non_std_b = b\n        a = (a - self.loc) / self.scale\n        b = (b - self.loc) / self.scale\n        super(TruncatedNormal, self).__init__(a, b, validate_args=validate_args)\n        self._log_scale = self.scale.log()\n        self._mean = self._mean * self.scale + self.loc\n        self._variance = self._variance * self.scale**2\n        self._entropy += self._log_scale\n\n    def _to_std_rv(self, value):\n        return (value - self.loc) / self.scale\n\n    def _from_std_rv(self, value):\n        return value * self.scale + self.loc\n\n    def cdf(self, value):\n        return super(TruncatedNormal, self).cdf(self._to_std_rv(value))\n\n    def icdf(self, value):\n        sample = self._from_std_rv(super().icdf(value))\n\n        # clamp data but keep gradients\n        sample_clip = torch.stack(\n            [sample.detach(), self._non_std_a.detach().expand_as(sample)], 0\n        ).max(0)[0]\n        sample_clip = torch.stack(\n            [sample_clip, self._non_std_b.detach().expand_as(sample)], 0\n        ).min(0)[0]\n        sample.data.copy_(sample_clip)\n        return sample\n\n    def log_prob(self, value):\n        value = self._to_std_rv(value)\n        return super(TruncatedNormal, self).log_prob(value) - self._log_scale\n", "path": "torchrl/modules/distributions/truncated_normal.py"}]}