env-ssl-wrapper 0.4.1__tar.gz → 0.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/PKG-INFO +5 -2
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/README.md +1 -1
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/action_transform_wrapper.py +8 -3
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/adapters.py +13 -41
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/done_tracker_wrapper.py +2 -20
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/episode_padding_wrapper.py +16 -19
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/helpers.py +56 -6
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/image_wrapper.py +39 -9
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/standardize_env_wrapper.py +6 -5
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/standardize_wrapper.py +1 -33
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/time_limit_wrapper.py +18 -3
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/utils.py +16 -18
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/vector.py +4 -6
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/pyproject.toml +4 -1
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/.gitignore +0 -0
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/LICENSE +0 -0
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/__init__.py +0 -0
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/memory_trace.py +0 -0
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/__init__.py +0 -0
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/auto_batched_wrapper.py +0 -0
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/flatten_obs_wrapper.py +0 -0
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/mocks.py +0 -0
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/spaces.py +0 -0
- {env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/tensor_wrapper.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: env-ssl-wrapper
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: One torch-native interface for any MDP environment
|
|
5
5
|
Project-URL: Homepage, https://pypi.org/project/env-ssl-wrapper/
|
|
6
6
|
Project-URL: Repository, https://codeberg.org/lucidrains/env-ssl-wrapper
|
|
@@ -47,14 +47,17 @@ Requires-Dist: fire; extra == 'examples'
|
|
|
47
47
|
Provides-Extra: test
|
|
48
48
|
Requires-Dist: box2d-py>=2.3.5; extra == 'test'
|
|
49
49
|
Requires-Dist: dm-control; extra == 'test'
|
|
50
|
+
Requires-Dist: gym-pusht; extra == 'test'
|
|
50
51
|
Requires-Dist: gymnasium-robotics; extra == 'test'
|
|
51
52
|
Requires-Dist: gymnasium>=0.29.1; extra == 'test'
|
|
52
53
|
Requires-Dist: imageio; extra == 'test'
|
|
54
|
+
Requires-Dist: mean-conc-beta; extra == 'test'
|
|
53
55
|
Requires-Dist: mujoco; extra == 'test'
|
|
54
56
|
Requires-Dist: pillow; extra == 'test'
|
|
55
57
|
Requires-Dist: populora; extra == 'test'
|
|
56
58
|
Requires-Dist: pybullet; extra == 'test'
|
|
57
59
|
Requires-Dist: pygame; extra == 'test'
|
|
60
|
+
Requires-Dist: pymunk<7; extra == 'test'
|
|
58
61
|
Requires-Dist: pytest; extra == 'test'
|
|
59
62
|
Description-Content-Type: text/markdown
|
|
60
63
|
|
|
@@ -81,7 +84,7 @@ env = StandardizeEnvWrapper(any_env)
|
|
|
81
84
|
obs, info = env.reset() # torch.float32, batched
|
|
82
85
|
|
|
83
86
|
while not env.all_done:
|
|
84
|
-
actions = torch.randint(0, 2, (
|
|
87
|
+
actions = torch.randint(0, 2, (1,))
|
|
85
88
|
obs, reward, terminated, truncated, info = env.step(actions)
|
|
86
89
|
```
|
|
87
90
|
|
|
@@ -21,7 +21,7 @@ env = StandardizeEnvWrapper(any_env)
|
|
|
21
21
|
obs, info = env.reset() # torch.float32, batched
|
|
22
22
|
|
|
23
23
|
while not env.all_done:
|
|
24
|
-
actions = torch.randint(0, 2, (
|
|
24
|
+
actions = torch.randint(0, 2, (1,))
|
|
25
25
|
obs, reward, terminated, truncated, info = env.step(actions)
|
|
26
26
|
```
|
|
27
27
|
|
|
@@ -97,9 +97,14 @@ class ActionTransformWrapper(EnvWrapper):
|
|
|
97
97
|
if was_scalar:
|
|
98
98
|
t = rearrange(t, '-> 1')
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
# bounds may be unbatched (single_action_space) or already batched
|
|
101
|
+
# (vector envs exposing only a batched action_space)
|
|
102
|
+
|
|
103
|
+
if low.shape != t.shape and not (low.ndim > 0 and t.shape[-low.ndim:] == low.shape):
|
|
104
|
+
dim = t.shape[-1]
|
|
105
|
+
low = np.broadcast_to(low, (dim,))
|
|
106
|
+
high = np.broadcast_to(high, (dim,))
|
|
107
|
+
|
|
103
108
|
valid = np.isfinite(low) & np.isfinite(high)
|
|
104
109
|
|
|
105
110
|
if is_tensor(t):
|
|
@@ -1,32 +1,20 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import numpy as np
|
|
4
|
-
import torch
|
|
5
|
-
from torch import is_tensor
|
|
6
|
-
|
|
7
3
|
from .helpers import (
|
|
8
4
|
EnvWrapper,
|
|
9
5
|
default,
|
|
10
6
|
exists,
|
|
11
7
|
first_existing,
|
|
12
8
|
get_attr,
|
|
9
|
+
is_time_step,
|
|
10
|
+
normalize_reset_out,
|
|
11
|
+
normalize_step_out,
|
|
13
12
|
safe_close,
|
|
14
13
|
truthy_attr,
|
|
14
|
+
zero_like,
|
|
15
15
|
)
|
|
16
16
|
from .spaces import space_from_action_spec
|
|
17
17
|
|
|
18
|
-
# helpers
|
|
19
|
-
|
|
20
|
-
def zero_like(x):
|
|
21
|
-
if is_tensor(x):
|
|
22
|
-
return torch.zeros_like(x, dtype = torch.bool)
|
|
23
|
-
|
|
24
|
-
arr = np.asarray(x)
|
|
25
|
-
return np.zeros_like(arr, dtype = bool) if arr.ndim > 0 else False
|
|
26
|
-
|
|
27
|
-
def is_time_step(out):
|
|
28
|
-
return exists(get_attr(out, 'step_type')) and exists(get_attr(out, 'observation'))
|
|
29
|
-
|
|
30
18
|
# base adapter
|
|
31
19
|
|
|
32
20
|
class BaseEnvAdapter:
|
|
@@ -38,27 +26,10 @@ class BaseEnvAdapter:
|
|
|
38
26
|
self.env = env
|
|
39
27
|
|
|
40
28
|
def step(self, action) -> tuple:
|
|
41
|
-
|
|
42
|
-
if is_time_step(out):
|
|
43
|
-
last = out.last() if callable(get_attr(out, 'last')) else out.step_type == 2
|
|
44
|
-
return out.observation, out.reward, last, False, dict(discount = out.discount)
|
|
45
|
-
if len(out) == 5:
|
|
46
|
-
obs, reward, term, trunc, info = out
|
|
47
|
-
return obs, reward, term, trunc, info if isinstance(info, dict) else {}
|
|
48
|
-
if len(out) in (3, 4):
|
|
49
|
-
obs, reward, done, *rest = out
|
|
50
|
-
info = rest[0] if rest and isinstance(rest[0], dict) else {}
|
|
51
|
-
return obs, reward, done, zero_like(done), info
|
|
52
|
-
raise ValueError(f'cannot standardize step output of length {len(out)}')
|
|
29
|
+
return normalize_step_out(self.env.step(action))
|
|
53
30
|
|
|
54
31
|
def reset(self, **kwargs) -> tuple:
|
|
55
|
-
|
|
56
|
-
if is_time_step(out):
|
|
57
|
-
return out.observation, {}
|
|
58
|
-
if isinstance(out, tuple) and len(out) == 2:
|
|
59
|
-
obs, info = out
|
|
60
|
-
return obs, {} if info is None else (info if isinstance(info, dict) else {})
|
|
61
|
-
return out, {}
|
|
32
|
+
return normalize_reset_out(self.env.reset(**kwargs))
|
|
62
33
|
|
|
63
34
|
def seed(self, seed: int):
|
|
64
35
|
if callable(get_attr(self.env, 'seed')):
|
|
@@ -381,15 +352,16 @@ class GymnasiumAdapter(BaseEnvAdapter):
|
|
|
381
352
|
|
|
382
353
|
@property
|
|
383
354
|
def autoresets(self) -> bool:
|
|
384
|
-
|
|
385
|
-
|
|
355
|
+
mode = first_existing(self.env, 'autoreset', 'autoresets', 'autoreset_mode')
|
|
356
|
+
|
|
357
|
+
if exists(mode):
|
|
358
|
+
return truthy_attr(mode)
|
|
359
|
+
|
|
386
360
|
try:
|
|
387
361
|
from gymnasium.vector import VectorEnv
|
|
388
|
-
|
|
389
|
-
return True
|
|
362
|
+
return isinstance(self.env, VectorEnv)
|
|
390
363
|
except ImportError:
|
|
391
|
-
|
|
392
|
-
return False
|
|
364
|
+
return False
|
|
393
365
|
|
|
394
366
|
def seed(self, seed: int):
|
|
395
367
|
try:
|
{env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/done_tracker_wrapper.py
RENAMED
|
@@ -2,8 +2,6 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
|
|
5
|
-
from torch.utils._pytree import tree_flatten
|
|
6
|
-
|
|
7
5
|
from .auto_batched_wrapper import AutoBatchedWrapper
|
|
8
6
|
from .helpers import (
|
|
9
7
|
EnvWrapper,
|
|
@@ -11,27 +9,11 @@ from .helpers import (
|
|
|
11
9
|
env_autoresets,
|
|
12
10
|
env_num_envs,
|
|
13
11
|
exists,
|
|
14
|
-
|
|
15
|
-
is_array,
|
|
12
|
+
get_batch_size,
|
|
16
13
|
is_vectorized,
|
|
17
14
|
to_numpy,
|
|
18
15
|
)
|
|
19
16
|
|
|
20
|
-
# helper functions
|
|
21
|
-
|
|
22
|
-
def get_batch_size(tree) -> int | None:
|
|
23
|
-
leaves, _ = tree_flatten(tree)
|
|
24
|
-
|
|
25
|
-
if not leaves:
|
|
26
|
-
return None
|
|
27
|
-
|
|
28
|
-
first = leaves[0]
|
|
29
|
-
|
|
30
|
-
if is_array(first):
|
|
31
|
-
return len(first) if first.ndim > 0 else None
|
|
32
|
-
|
|
33
|
-
return len(first) if exists(get_attr(first, '__len__')) else None
|
|
34
|
-
|
|
35
17
|
# classes
|
|
36
18
|
|
|
37
19
|
class DoneTrackerWrapper(EnvWrapper):
|
|
@@ -113,6 +95,6 @@ class DoneTrackerWrapper(EnvWrapper):
|
|
|
113
95
|
info['episode_lengths'] = self.episode_lengths.copy()
|
|
114
96
|
|
|
115
97
|
if self.all_done:
|
|
116
|
-
info.update(needs_reset =
|
|
98
|
+
info.update(needs_reset = self.needs_reset, all_done = True)
|
|
117
99
|
|
|
118
100
|
return obs, reward, terminated, truncated, info
|
|
@@ -21,15 +21,18 @@ from .helpers import (
|
|
|
21
21
|
|
|
22
22
|
# helpers
|
|
23
23
|
|
|
24
|
-
def
|
|
25
|
-
if is_tensor(x):
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
def broadcast_mask(mask, x):
|
|
25
|
+
if not is_tensor(x):
|
|
26
|
+
return mask
|
|
27
|
+
|
|
28
|
+
m = torch.as_tensor(mask, device = x.device, dtype = torch.bool)
|
|
29
|
+
diff = x.ndim - m.ndim
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
m = rearrange(m, f'... -> ... {" ".join(["1"] * diff)}')
|
|
31
|
+
return rearrange(m, f'... -> ... {" ".join(["1"] * diff)}') if diff > 0 else m
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
def zero_mask(x, mask, fill_scalar = None):
|
|
34
|
+
if is_tensor(x):
|
|
35
|
+
return torch.where(broadcast_mask(mask, x), torch.zeros_like(x), x)
|
|
33
36
|
|
|
34
37
|
arr = np.asarray(x)
|
|
35
38
|
|
|
@@ -49,13 +52,7 @@ def back_to_mask_type(dones, newly):
|
|
|
49
52
|
|
|
50
53
|
def merge_final(current, value, mask):
|
|
51
54
|
if is_tensor(current):
|
|
52
|
-
|
|
53
|
-
diff = current.ndim - m.ndim
|
|
54
|
-
|
|
55
|
-
if diff > 0:
|
|
56
|
-
m = rearrange(m, f'... -> ... {" ".join(["1"] * diff)}')
|
|
57
|
-
|
|
58
|
-
return torch.where(m, value, current)
|
|
55
|
+
return torch.where(broadcast_mask(mask, current), value, current)
|
|
59
56
|
|
|
60
57
|
curr = np.asarray(current)
|
|
61
58
|
|
|
@@ -97,15 +94,15 @@ class EpisodePaddingWrapper(EnvWrapper):
|
|
|
97
94
|
if self._is_done is None or len(self._is_done) != len(mask):
|
|
98
95
|
self._is_done = np.zeros(len(mask), dtype = bool)
|
|
99
96
|
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
# autoreset envs revive every done slot, so each done is a new
|
|
98
|
+
# terminal transition; non-autoreset slots stay done until reset
|
|
99
|
+
|
|
100
|
+
newly = mask if self.autoreset else mask & ~self._is_done
|
|
101
|
+
self._is_done |= mask
|
|
102
102
|
|
|
103
103
|
if mask.any():
|
|
104
104
|
assert exists(self._last_obs), 'environment needs reset before calling step. call env.reset() first'
|
|
105
105
|
|
|
106
|
-
newly = mask & ~self._is_done
|
|
107
|
-
self._is_done |= mask
|
|
108
|
-
|
|
109
106
|
if newly.any():
|
|
110
107
|
final_val = first_existing(info, 'final_observation', 'final_obs')
|
|
111
108
|
value = final_val if exists(final_val) else self._last_obs
|
|
@@ -22,10 +22,15 @@ def get_attr(obj, name, default = None):
|
|
|
22
22
|
return default
|
|
23
23
|
|
|
24
24
|
def truthy_attr(value):
|
|
25
|
-
# flags arrive as None, methods, numpy scalars — only honest truths count
|
|
25
|
+
# flags arrive as None, methods, numpy scalars — only honest truths count.
|
|
26
|
+
# gymnasium's AutoresetMode.DISABLED is a truthy enum that means "off"
|
|
27
|
+
|
|
26
28
|
if not exists(value) or callable(value):
|
|
27
29
|
return False
|
|
28
30
|
|
|
31
|
+
if getattr(value, 'name', None) == 'DISABLED':
|
|
32
|
+
return False
|
|
33
|
+
|
|
29
34
|
try:
|
|
30
35
|
return bool(value)
|
|
31
36
|
except Exception:
|
|
@@ -46,6 +51,19 @@ def first_existing(obj, *names):
|
|
|
46
51
|
def is_scalar(v):
|
|
47
52
|
return isinstance(v, (int, float, bool, np.number, np.bool_))
|
|
48
53
|
|
|
54
|
+
def get_batch_size(tree) -> int | None:
|
|
55
|
+
leaves, _ = tree_flatten(tree)
|
|
56
|
+
|
|
57
|
+
if not leaves:
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
first = leaves[0]
|
|
61
|
+
|
|
62
|
+
if is_array(first):
|
|
63
|
+
return len(first) if first.ndim > 0 else None
|
|
64
|
+
|
|
65
|
+
return len(first) if exists(get_attr(first, '__len__')) else None
|
|
66
|
+
|
|
49
67
|
def is_array(v):
|
|
50
68
|
return is_tensor(v) or isinstance(v, np.ndarray)
|
|
51
69
|
|
|
@@ -71,6 +89,43 @@ def dones_of(terminated, truncated):
|
|
|
71
89
|
return terminated | truncated
|
|
72
90
|
return tree_map(lambda a, b: a | b, terminated, truncated)
|
|
73
91
|
|
|
92
|
+
# sim step / reset normalization
|
|
93
|
+
|
|
94
|
+
def is_time_step(out):
|
|
95
|
+
return exists(get_attr(out, 'step_type')) and exists(get_attr(out, 'observation'))
|
|
96
|
+
|
|
97
|
+
def zero_like(x):
|
|
98
|
+
if is_tensor(x):
|
|
99
|
+
return torch.zeros_like(x, dtype = torch.bool)
|
|
100
|
+
|
|
101
|
+
arr = np.asarray(x)
|
|
102
|
+
return np.zeros_like(arr, dtype = bool) if arr.ndim > 0 else False
|
|
103
|
+
|
|
104
|
+
def normalize_reset_out(out):
|
|
105
|
+
if is_time_step(out):
|
|
106
|
+
return out.observation, {}
|
|
107
|
+
|
|
108
|
+
if isinstance(out, tuple) and len(out) == 2:
|
|
109
|
+
obs, info = out
|
|
110
|
+
return obs, {} if info is None else (info if isinstance(info, dict) else {})
|
|
111
|
+
|
|
112
|
+
return out, {}
|
|
113
|
+
|
|
114
|
+
def normalize_step_out(out):
|
|
115
|
+
if is_time_step(out):
|
|
116
|
+
last = out.last() if callable(get_attr(out, 'last')) else out.step_type == 2
|
|
117
|
+
return out.observation, out.reward, last, False, dict(discount = out.discount)
|
|
118
|
+
|
|
119
|
+
if len(out) == 5:
|
|
120
|
+
return out
|
|
121
|
+
|
|
122
|
+
if len(out) in (3, 4):
|
|
123
|
+
obs, reward, done, *rest = out
|
|
124
|
+
info = rest[0] if rest and isinstance(rest[0], dict) else {}
|
|
125
|
+
return obs, reward, done, zero_like(done), info
|
|
126
|
+
|
|
127
|
+
raise ValueError(f'could not standardize step output of length {len(out)}')
|
|
128
|
+
|
|
74
129
|
def _zero_leaf(x):
|
|
75
130
|
if is_tensor(x):
|
|
76
131
|
return torch.zeros_like(x)
|
|
@@ -272,16 +327,11 @@ class TransformObservationWrapper(EnvWrapper):
|
|
|
272
327
|
self.takes_done = accepts_done_param(self.transform_obs)
|
|
273
328
|
|
|
274
329
|
def transform_obs(self, obs, done = None):
|
|
275
|
-
if hasattr(self, 'observation'):
|
|
276
|
-
return self.observation(obs)
|
|
277
330
|
return obs
|
|
278
331
|
|
|
279
332
|
def transform(self, obs, done = None):
|
|
280
333
|
return self.transform_obs(obs, done = done) if self.takes_done else self.transform_obs(obs)
|
|
281
334
|
|
|
282
|
-
def observation(self, obs):
|
|
283
|
-
return self.transform_obs(obs)
|
|
284
|
-
|
|
285
335
|
def reset(self, **kwargs):
|
|
286
336
|
obs, info = self.env.reset(**kwargs)
|
|
287
337
|
obs = self.transform_obs(obs)
|
{env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/image_wrapper.py
RENAMED
|
@@ -6,8 +6,15 @@ import numpy as np
|
|
|
6
6
|
|
|
7
7
|
from einops import rearrange
|
|
8
8
|
|
|
9
|
-
from .helpers import
|
|
10
|
-
|
|
9
|
+
from .helpers import (
|
|
10
|
+
FINAL_OBSERVATION_KEYS,
|
|
11
|
+
TransformObservationWrapper,
|
|
12
|
+
env_render,
|
|
13
|
+
env_render_mode,
|
|
14
|
+
exists,
|
|
15
|
+
normalize_reset_out,
|
|
16
|
+
normalize_step_out,
|
|
17
|
+
)
|
|
11
18
|
|
|
12
19
|
# helper functions
|
|
13
20
|
|
|
@@ -32,12 +39,19 @@ def render_frame(env, image_size = (64, 64), camera = None):
|
|
|
32
39
|
|
|
33
40
|
img = env.render()
|
|
34
41
|
|
|
42
|
+
if isinstance(img, (list, tuple)):
|
|
43
|
+
img = np.ascontiguousarray(img)
|
|
44
|
+
|
|
35
45
|
if isinstance(img, torch.Tensor):
|
|
36
46
|
img = img.detach().cpu().numpy()
|
|
37
47
|
|
|
38
48
|
img = np.ascontiguousarray(img)
|
|
39
49
|
img = torch.from_numpy(img)
|
|
40
|
-
|
|
50
|
+
|
|
51
|
+
if img.ndim == 4:
|
|
52
|
+
return rearrange(img, 'b h w c -> b c h w'), True
|
|
53
|
+
|
|
54
|
+
return rearrange(img, 'h w c -> 1 c h w'), False
|
|
41
55
|
|
|
42
56
|
def process_image(
|
|
43
57
|
img,
|
|
@@ -61,7 +75,7 @@ def process_image(
|
|
|
61
75
|
|
|
62
76
|
# class
|
|
63
77
|
|
|
64
|
-
class ImageObservationWrapper(
|
|
78
|
+
class ImageObservationWrapper(TransformObservationWrapper):
|
|
65
79
|
def __init__(
|
|
66
80
|
self,
|
|
67
81
|
env,
|
|
@@ -81,18 +95,21 @@ class ImageObservationWrapper(EnvWrapper):
|
|
|
81
95
|
self.normalize_divisor = normalize_divisor
|
|
82
96
|
|
|
83
97
|
def render_frame(self):
|
|
84
|
-
img = render_frame(self.env, image_size = self.image_size, camera = self.camera)
|
|
85
|
-
|
|
98
|
+
img, is_batched = render_frame(self.env, image_size = self.image_size, camera = self.camera)
|
|
99
|
+
processed = process_image(
|
|
86
100
|
img,
|
|
87
101
|
image_size = self.image_size,
|
|
88
102
|
mode = self.mode,
|
|
89
103
|
normalize = self.normalize,
|
|
90
104
|
normalize_divisor = self.normalize_divisor
|
|
91
105
|
)
|
|
106
|
+
return processed, is_batched
|
|
92
107
|
|
|
93
108
|
def observation(self, obs):
|
|
94
|
-
img = self.render_frame()
|
|
95
|
-
|
|
109
|
+
img, is_batched = self.render_frame()
|
|
110
|
+
|
|
111
|
+
if not is_batched:
|
|
112
|
+
img = rearrange(img, '1 c h w -> c h w')
|
|
96
113
|
|
|
97
114
|
if not isinstance(obs, dict):
|
|
98
115
|
return dict(state = obs, **{self.image_key: img})
|
|
@@ -102,10 +119,23 @@ class ImageObservationWrapper(EnvWrapper):
|
|
|
102
119
|
|
|
103
120
|
return {**obs, self.image_key: img}
|
|
104
121
|
|
|
122
|
+
def transform_obs(self, obs, done = None):
|
|
123
|
+
return self.observation(obs)
|
|
124
|
+
|
|
105
125
|
def reset(self, **kwargs):
|
|
106
126
|
obs, info = normalize_reset_out(self.env.reset(**kwargs))
|
|
107
127
|
return self.observation(obs), info
|
|
108
128
|
|
|
109
129
|
def step(self, action):
|
|
110
130
|
obs, reward, terminated, truncated, info = normalize_step_out(self.env.step(action))
|
|
111
|
-
|
|
131
|
+
out = self.observation(obs)
|
|
132
|
+
|
|
133
|
+
# terminal obs are augmented in place, preserving whatever the sim
|
|
134
|
+
# or the padding wrapper froze as the true terminal observation
|
|
135
|
+
|
|
136
|
+
if isinstance(info, dict) and not self.autoresets:
|
|
137
|
+
for key in FINAL_OBSERVATION_KEYS:
|
|
138
|
+
if key in info and not (isinstance(info[key], dict) and self.image_key in info[key]):
|
|
139
|
+
info[key] = self.observation(info[key])
|
|
140
|
+
|
|
141
|
+
return out, reward, terminated, truncated, info
|
|
@@ -18,11 +18,11 @@ class StandardizeEnvWrapper(EnvWrapper):
|
|
|
18
18
|
pad_episodes: bool = True,
|
|
19
19
|
done_tracker: bool = True,
|
|
20
20
|
flatten_obs: bool = False,
|
|
21
|
-
action_transform: bool = False,
|
|
21
|
+
action_transform: bool | dict = False,
|
|
22
22
|
max_timesteps: int | None = None,
|
|
23
|
-
image_size: tuple[int, int] | None = None,
|
|
23
|
+
image_size: int | tuple[int, int] | None = None,
|
|
24
24
|
lambdas: tuple[float, ...] | list[float] | None = None,
|
|
25
|
-
|
|
25
|
+
keys: str | tuple[str, ...] | None = None,
|
|
26
26
|
):
|
|
27
27
|
wrappers = []
|
|
28
28
|
|
|
@@ -30,7 +30,8 @@ class StandardizeEnvWrapper(EnvWrapper):
|
|
|
30
30
|
wrappers.append(('image', dict(image_size = image_size)))
|
|
31
31
|
|
|
32
32
|
if action_transform:
|
|
33
|
-
|
|
33
|
+
transform_kwargs = dict(auto = True) if action_transform is True else action_transform
|
|
34
|
+
wrappers.append(('action_transform', transform_kwargs))
|
|
34
35
|
|
|
35
36
|
if exists(max_timesteps):
|
|
36
37
|
wrappers.append(('time_limit', dict(max_timesteps = max_timesteps)))
|
|
@@ -45,7 +46,7 @@ class StandardizeEnvWrapper(EnvWrapper):
|
|
|
45
46
|
wrappers.append('done_tracker')
|
|
46
47
|
|
|
47
48
|
if exists(lambdas):
|
|
48
|
-
wrappers.append(('memory_trace', dict(lambdas = lambdas,
|
|
49
|
+
wrappers.append(('memory_trace', dict(lambdas = lambdas, keys = keys)))
|
|
49
50
|
|
|
50
51
|
if flatten_obs:
|
|
51
52
|
wrappers.append('flatten_obs')
|
{env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/standardize_wrapper.py
RENAMED
|
@@ -1,48 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
import torch
|
|
5
|
-
from torch import is_tensor
|
|
6
|
-
|
|
7
|
-
from .adapters import get_adapter, is_time_step, zero_like
|
|
3
|
+
from .adapters import get_adapter
|
|
8
4
|
from .helpers import (
|
|
9
5
|
EnvWrapper,
|
|
10
6
|
default,
|
|
11
7
|
dones_of,
|
|
12
8
|
exists,
|
|
13
|
-
get_attr,
|
|
14
9
|
instantiate_env,
|
|
15
10
|
mark_terminal_obs,
|
|
16
11
|
)
|
|
17
12
|
from .spaces import infer_observation_space, space_from_action_spec
|
|
18
13
|
|
|
19
|
-
# helpers
|
|
20
|
-
|
|
21
|
-
def normalize_reset_out(out):
|
|
22
|
-
if is_time_step(out):
|
|
23
|
-
return out.observation, {}
|
|
24
|
-
|
|
25
|
-
if isinstance(out, tuple) and len(out) == 2:
|
|
26
|
-
obs, info = out
|
|
27
|
-
return obs, {} if info is None else (info if isinstance(info, dict) else {})
|
|
28
|
-
|
|
29
|
-
return out, {}
|
|
30
|
-
|
|
31
|
-
def normalize_step_out(out):
|
|
32
|
-
if is_time_step(out):
|
|
33
|
-
last = out.last() if callable(get_attr(out, 'last')) else out.step_type == 2
|
|
34
|
-
return out.observation, out.reward, last, False, dict(discount = out.discount)
|
|
35
|
-
|
|
36
|
-
if len(out) == 5:
|
|
37
|
-
return out
|
|
38
|
-
|
|
39
|
-
if len(out) in (3, 4):
|
|
40
|
-
obs, reward, done, *rest = out
|
|
41
|
-
info = rest[0] if rest and isinstance(rest[0], dict) else {}
|
|
42
|
-
return obs, reward, done, zero_like(done), info
|
|
43
|
-
|
|
44
|
-
raise ValueError(f'could not standardize step output of length {len(out)}')
|
|
45
|
-
|
|
46
14
|
# class
|
|
47
15
|
|
|
48
16
|
class StandardizeWrapper(EnvWrapper):
|
{env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/time_limit_wrapper.py
RENAMED
|
@@ -4,8 +4,18 @@ import numpy as np
|
|
|
4
4
|
import torch
|
|
5
5
|
from torch import is_tensor
|
|
6
6
|
|
|
7
|
-
from .helpers import
|
|
8
|
-
|
|
7
|
+
from .helpers import (
|
|
8
|
+
EnvWrapper,
|
|
9
|
+
dones_of,
|
|
10
|
+
env_num_envs,
|
|
11
|
+
exists,
|
|
12
|
+
get_batch_size,
|
|
13
|
+
is_vectorized,
|
|
14
|
+
mark_terminal_obs,
|
|
15
|
+
normalize_reset_out,
|
|
16
|
+
normalize_step_out,
|
|
17
|
+
to_numpy,
|
|
18
|
+
)
|
|
9
19
|
|
|
10
20
|
def back_to_like(t, numpy_arr):
|
|
11
21
|
if is_tensor(t):
|
|
@@ -44,8 +54,13 @@ class TimeLimitWrapper(EnvWrapper):
|
|
|
44
54
|
self.t = np.zeros(self.num_envs, dtype = int)
|
|
45
55
|
|
|
46
56
|
def reset(self, **kwargs):
|
|
57
|
+
obs, info = normalize_reset_out(self.env.reset(**kwargs))
|
|
58
|
+
|
|
59
|
+
if self.is_vector and exists(batch_size := get_batch_size(obs)):
|
|
60
|
+
self.num_envs = batch_size
|
|
61
|
+
|
|
47
62
|
self.t = np.zeros(self.num_envs, dtype = int)
|
|
48
|
-
return
|
|
63
|
+
return obs, info
|
|
49
64
|
|
|
50
65
|
def step(self, action):
|
|
51
66
|
obs, reward, terminated, truncated, info = normalize_step_out(self.env.step(action))
|
|
@@ -25,29 +25,27 @@ WRAPPERS = dict(
|
|
|
25
25
|
pad_episodes = EpisodePaddingWrapper
|
|
26
26
|
)
|
|
27
27
|
|
|
28
|
+
def get_wrapper(name):
|
|
29
|
+
if name in ('standardize_env', 'master'):
|
|
30
|
+
from .standardize_env_wrapper import StandardizeEnvWrapper
|
|
31
|
+
return StandardizeEnvWrapper
|
|
32
|
+
|
|
33
|
+
if name == 'memory_trace':
|
|
34
|
+
from ..memory_trace import MemoryTraceWrapper
|
|
35
|
+
return MemoryTraceWrapper
|
|
36
|
+
|
|
37
|
+
if name in WRAPPERS:
|
|
38
|
+
return WRAPPERS[name]
|
|
39
|
+
|
|
40
|
+
raise ValueError(f'unknown wrapper {name!r} — choose from {sorted([*WRAPPERS, "memory_trace", "standardize_env"])}')
|
|
41
|
+
|
|
28
42
|
def parse_wrapper(wrapper):
|
|
29
43
|
if isinstance(wrapper, str):
|
|
30
|
-
|
|
31
|
-
from ..memory_trace import MemoryTraceWrapper
|
|
32
|
-
wrapper = MemoryTraceWrapper
|
|
33
|
-
elif wrapper in ('standardize_env', 'master'):
|
|
34
|
-
from .standardize_env_wrapper import StandardizeEnvWrapper
|
|
35
|
-
wrapper = StandardizeEnvWrapper
|
|
36
|
-
elif wrapper not in WRAPPERS:
|
|
37
|
-
raise ValueError(f'unknown wrapper {wrapper!r} — choose from {sorted([*WRAPPERS, "memory_trace", "standardize_env"])}')
|
|
38
|
-
else:
|
|
39
|
-
wrapper = WRAPPERS[wrapper]
|
|
44
|
+
wrapper = get_wrapper(wrapper)
|
|
40
45
|
|
|
41
46
|
if isinstance(wrapper, tuple):
|
|
42
47
|
name, kwargs = wrapper
|
|
43
|
-
if name
|
|
44
|
-
from ..memory_trace import MemoryTraceWrapper
|
|
45
|
-
wrapper = partial(MemoryTraceWrapper, **kwargs)
|
|
46
|
-
elif name in ('standardize_env', 'master'):
|
|
47
|
-
from .standardize_env_wrapper import StandardizeEnvWrapper
|
|
48
|
-
wrapper = partial(StandardizeEnvWrapper, **kwargs)
|
|
49
|
-
else:
|
|
50
|
-
wrapper = partial(WRAPPERS.get(name, name), **kwargs)
|
|
48
|
+
wrapper = partial(get_wrapper(name) if isinstance(name, str) else name, **kwargs)
|
|
51
49
|
|
|
52
50
|
elif isinstance(wrapper, dict):
|
|
53
51
|
raise ValueError("wrapper kwargs must be passed as (name, kwargs), e.g. ('tensor', dict(device = 'cpu'))")
|
|
@@ -22,8 +22,6 @@ from .standardize_wrapper import StandardizeWrapper
|
|
|
22
22
|
|
|
23
23
|
# leaf helpers
|
|
24
24
|
|
|
25
|
-
_stack_trees = stack_trees
|
|
26
|
-
|
|
27
25
|
def _merge_infos(infos):
|
|
28
26
|
infos = [info if isinstance(info, dict) else {} for info in infos]
|
|
29
27
|
|
|
@@ -38,7 +36,7 @@ def _merge_infos(infos):
|
|
|
38
36
|
for key in keys:
|
|
39
37
|
vals = [info[key] for info in infos]
|
|
40
38
|
try:
|
|
41
|
-
out[key] =
|
|
39
|
+
out[key] = stack_trees(vals)
|
|
42
40
|
except Exception:
|
|
43
41
|
out[key] = np.array(vals, dtype = object)
|
|
44
42
|
|
|
@@ -276,7 +274,7 @@ class MultiprocessingVecEnv:
|
|
|
276
274
|
_safe_send(conn, ('reset', kwargs))
|
|
277
275
|
|
|
278
276
|
results = _recv_all(self._conns)
|
|
279
|
-
return
|
|
277
|
+
return stack_trees([obs for obs, _ in results]), _merge_infos([info for _, info in results])
|
|
280
278
|
|
|
281
279
|
def step(self, actions):
|
|
282
280
|
actions = _split_actions(actions, self.num_envs)
|
|
@@ -302,10 +300,10 @@ class MultiprocessingVecEnv:
|
|
|
302
300
|
for final, obs_i in zip(final_obs, obs)
|
|
303
301
|
]
|
|
304
302
|
|
|
305
|
-
info['final_observation'] =
|
|
303
|
+
info['final_observation'] = stack_trees(final_obs)
|
|
306
304
|
info['_final_observation'] = done
|
|
307
305
|
|
|
308
|
-
return
|
|
306
|
+
return stack_trees(obs), reward, terminated, truncated, info
|
|
309
307
|
|
|
310
308
|
def close(self):
|
|
311
309
|
_shutdown(self._conns, self._procs)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "env-ssl-wrapper"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.2"
|
|
4
4
|
description = "One torch-native interface for any MDP environment"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Phil Wang", email = "lucidrains@gmail.com" }
|
|
@@ -54,6 +54,9 @@ test = [
|
|
|
54
54
|
"pillow",
|
|
55
55
|
"imageio",
|
|
56
56
|
"populora",
|
|
57
|
+
"gym-pusht",
|
|
58
|
+
"pymunk<7",
|
|
59
|
+
"mean-conc-beta",
|
|
57
60
|
]
|
|
58
61
|
|
|
59
62
|
[tool.pytest.ini_options]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/auto_batched_wrapper.py
RENAMED
|
File without changes
|
{env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/flatten_obs_wrapper.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{env_ssl_wrapper-0.4.1 → env_ssl_wrapper-0.4.2}/env_ssl_wrapper/standardize/tensor_wrapper.py
RENAMED
|
File without changes
|