env-ssl-wrapper 0.5.2__tar.gz → 0.5.3__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.5.2 → env_ssl_wrapper-0.5.3}/PKG-INFO +1 -1
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/evaluate.py +2 -2
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/__init__.py +32 -0
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/action_transform_wrapper.py +1 -1
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/adapters.py +62 -21
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/auto_batched_wrapper.py +6 -5
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/episode_padding_wrapper.py +5 -4
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/helpers.py +100 -19
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/image_wrapper.py +1 -1
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/mocks.py +249 -17
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/spaces.py +25 -8
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/tensor_wrapper.py +9 -5
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/time_limit_wrapper.py +4 -7
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/vector.py +2 -1
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/pyproject.toml +1 -1
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/.gitignore +0 -0
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/LICENSE +0 -0
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/README.md +0 -0
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/__init__.py +0 -0
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/action_chunk.py +0 -0
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/memory_trace.py +0 -0
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/done_tracker_wrapper.py +0 -0
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/flatten_obs_wrapper.py +0 -0
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/standardize_env_wrapper.py +0 -0
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/standardize_wrapper.py +0 -0
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: env-ssl-wrapper
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.3
|
|
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
|
|
@@ -19,7 +19,7 @@ from .standardize.helpers import (
|
|
|
19
19
|
first_existing,
|
|
20
20
|
get_adapter,
|
|
21
21
|
get_attr,
|
|
22
|
-
|
|
22
|
+
is_array_like,
|
|
23
23
|
is_vectorized,
|
|
24
24
|
normalize_reset_out,
|
|
25
25
|
normalize_step_out,
|
|
@@ -120,7 +120,7 @@ def to_action(env, out, deterministic = True):
|
|
|
120
120
|
|
|
121
121
|
def to_actor_obs(obs, device, batched):
|
|
122
122
|
def convert(x):
|
|
123
|
-
if not (
|
|
123
|
+
if not is_array_like(x):
|
|
124
124
|
return x
|
|
125
125
|
|
|
126
126
|
tensor = x if is_tensor(x) else torch.as_tensor(np.asarray(x))
|
|
@@ -77,6 +77,16 @@ from .mocks import (
|
|
|
77
77
|
TrifingerMockEnv,
|
|
78
78
|
HabitatMockEnv,
|
|
79
79
|
TupleObsMockEnv,
|
|
80
|
+
RLBenchObservation,
|
|
81
|
+
RLBenchMockEnv,
|
|
82
|
+
OmniGibsonMockEnv,
|
|
83
|
+
RoboMimicMockEnv,
|
|
84
|
+
MyoSuiteMockEnv,
|
|
85
|
+
GymnasiumRoboticsMockEnv,
|
|
86
|
+
LeRobotMockEnv,
|
|
87
|
+
CalvinMockEnv,
|
|
88
|
+
PushTMockEnv,
|
|
89
|
+
DroneAviaryMockEnv,
|
|
80
90
|
JaxArray,
|
|
81
91
|
)
|
|
82
92
|
|
|
@@ -84,6 +94,12 @@ from .utils import wrap_env, compose_env
|
|
|
84
94
|
from .helpers import (
|
|
85
95
|
TransformObservationWrapper,
|
|
86
96
|
ObservationWrapper,
|
|
97
|
+
is_array,
|
|
98
|
+
is_foreign_array,
|
|
99
|
+
is_array_like,
|
|
100
|
+
normalize_obs,
|
|
101
|
+
normalize_reset_out,
|
|
102
|
+
normalize_step_out,
|
|
87
103
|
has_final_observation,
|
|
88
104
|
maybe_get_final_observation,
|
|
89
105
|
get_final_observation,
|
|
@@ -143,6 +159,12 @@ __all__ = [
|
|
|
143
159
|
'action_space_bounds',
|
|
144
160
|
'action_dim_of',
|
|
145
161
|
'obs_dim_of',
|
|
162
|
+
'is_array',
|
|
163
|
+
'is_foreign_array',
|
|
164
|
+
'is_array_like',
|
|
165
|
+
'normalize_obs',
|
|
166
|
+
'normalize_reset_out',
|
|
167
|
+
'normalize_step_out',
|
|
146
168
|
'MockEnv',
|
|
147
169
|
'GymnasiumMockEnv',
|
|
148
170
|
'GymnasiumDiscreteMockEnv',
|
|
@@ -161,6 +183,16 @@ __all__ = [
|
|
|
161
183
|
'TrifingerMockEnv',
|
|
162
184
|
'HabitatMockEnv',
|
|
163
185
|
'TupleObsMockEnv',
|
|
186
|
+
'RLBenchObservation',
|
|
187
|
+
'RLBenchMockEnv',
|
|
188
|
+
'OmniGibsonMockEnv',
|
|
189
|
+
'RoboMimicMockEnv',
|
|
190
|
+
'MyoSuiteMockEnv',
|
|
191
|
+
'GymnasiumRoboticsMockEnv',
|
|
192
|
+
'LeRobotMockEnv',
|
|
193
|
+
'CalvinMockEnv',
|
|
194
|
+
'PushTMockEnv',
|
|
195
|
+
'DroneAviaryMockEnv',
|
|
164
196
|
'JaxArray',
|
|
165
197
|
'wrap_env',
|
|
166
198
|
'compose_env',
|
|
@@ -84,7 +84,7 @@ class ActionTransformWrapper(EnvWrapper):
|
|
|
84
84
|
# rescale canonical range (default (0, 1), beta-friendly) to per-env
|
|
85
85
|
# bounds, leaving unbounded dimensions untouched
|
|
86
86
|
|
|
87
|
-
if self.bounds
|
|
87
|
+
if not exists(self.bounds):
|
|
88
88
|
return t
|
|
89
89
|
|
|
90
90
|
low, high = self.bounds
|
|
@@ -13,6 +13,31 @@ from .helpers import (
|
|
|
13
13
|
)
|
|
14
14
|
from .spaces import space_from_action_spec
|
|
15
15
|
|
|
16
|
+
# helpers
|
|
17
|
+
|
|
18
|
+
def render_with_dims(render, height, width, camera = None):
|
|
19
|
+
# modern render(height, width, camera) vs legacy bare render()
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
return render(height = height, width = width, camera = camera)
|
|
23
|
+
except TypeError:
|
|
24
|
+
return render()
|
|
25
|
+
|
|
26
|
+
def render_sim(sim, height, width, camera = None):
|
|
27
|
+
# sims vary — robosuite-style (camera_name) vs bare no-kwargs render
|
|
28
|
+
|
|
29
|
+
render = get_attr(sim, 'render')
|
|
30
|
+
|
|
31
|
+
if not callable(render):
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
kwargs = dict(camera_name = camera) if exists(camera) else {}
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
return render(height = height, width = width, **kwargs)
|
|
38
|
+
except TypeError:
|
|
39
|
+
return None
|
|
40
|
+
|
|
16
41
|
# base adapter
|
|
17
42
|
|
|
18
43
|
class BaseEnvAdapter:
|
|
@@ -58,13 +83,14 @@ class BaseEnvAdapter:
|
|
|
58
83
|
_, _, rgba, _, _ = client.getCameraImage(width, height, renderer = renderer)
|
|
59
84
|
return rgba[..., :3]
|
|
60
85
|
|
|
61
|
-
|
|
62
|
-
if exists(
|
|
63
|
-
|
|
64
|
-
return sim.render(height = height, width = width, **kwargs)
|
|
86
|
+
frame = render_sim(get_attr(self.env, 'sim'), height, width, camera)
|
|
87
|
+
if exists(frame):
|
|
88
|
+
return frame
|
|
65
89
|
|
|
66
|
-
|
|
67
|
-
|
|
90
|
+
render = get_attr(self.env, 'render')
|
|
91
|
+
|
|
92
|
+
if callable(render):
|
|
93
|
+
return render_with_dims(render, height, width, camera)
|
|
68
94
|
|
|
69
95
|
return None
|
|
70
96
|
|
|
@@ -127,8 +153,11 @@ class WrapperAdapter(BaseEnvAdapter):
|
|
|
127
153
|
self.inner_adapter.seed(seed)
|
|
128
154
|
|
|
129
155
|
def render(self, height: int, width: int, camera = None):
|
|
130
|
-
|
|
131
|
-
|
|
156
|
+
render = get_attr(self.env, 'render')
|
|
157
|
+
|
|
158
|
+
if callable(render):
|
|
159
|
+
return render_with_dims(render, height, width, camera)
|
|
160
|
+
|
|
132
161
|
return self.inner_adapter.render(height, width, camera)
|
|
133
162
|
|
|
134
163
|
def close(self):
|
|
@@ -296,25 +325,37 @@ class PufferLibAdapter(BaseEnvAdapter):
|
|
|
296
325
|
def autoresets(self) -> bool:
|
|
297
326
|
return truthy_attr(first_existing(self.env, 'autoreset', 'autoresets', 'autoreset_mode'))
|
|
298
327
|
|
|
299
|
-
# robotics adapter (robosuite, maniskill, metaworld, trifinger, habitat
|
|
328
|
+
# robotics adapter (robosuite, maniskill, metaworld, trifinger, habitat, rlbench,
|
|
329
|
+
# omnigibson, robomimic, myosuite, gymnasium-robotics, lerobot, ...)
|
|
330
|
+
# sits before dm_control — several robotics sims expose `physics` but are not dm_control
|
|
331
|
+
|
|
332
|
+
ROBOTICS_KEYWORDS = (
|
|
333
|
+
'robosuite', 'mani_skill', 'maniskill', 'metaworld', 'trifinger', 'habitat',
|
|
334
|
+
'omnigibson', 'igibson', 'robomimic', 'myosuite', 'gymnasiumrobotics',
|
|
335
|
+
'gymnasium_robotics', 'gym_robotics', 'goalenv', 'lerobot',
|
|
336
|
+
'gym_aloha', 'gym_pusht', 'aloha', 'pusht', 'sapien', 'rlbench', 'pyrep',
|
|
337
|
+
'calvin', 'language_table', 'drone', 'aviary', 'softgym', 'plasticinelab'
|
|
338
|
+
)
|
|
300
339
|
|
|
301
340
|
class RoboticsAdapter(BaseEnvAdapter):
|
|
302
341
|
@classmethod
|
|
303
342
|
def matches(cls, env):
|
|
304
|
-
mod = getattr(type(env), '__module__', '')
|
|
305
|
-
name = type(env).__name__
|
|
306
|
-
robotics_names = ('Robosuite', 'ManiSkill', 'MetaWorld', 'Trifinger', 'Habitat')
|
|
307
|
-
robotics_mods = ('robosuite', 'mani_skill', 'maniskill', 'metaworld', 'habitat')
|
|
343
|
+
mod = getattr(type(env), '__module__', '').lower()
|
|
344
|
+
name = type(env).__name__.lower()
|
|
308
345
|
has_sim_render = exists(get_attr(env, 'sim')) and callable(get_attr(get_attr(env, 'sim'), 'render'))
|
|
309
|
-
return any(
|
|
346
|
+
return any(k in name or k in mod for k in ROBOTICS_KEYWORDS) or has_sim_render
|
|
310
347
|
|
|
311
348
|
def render(self, height: int, width: int, camera = None):
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
return
|
|
316
|
-
|
|
317
|
-
|
|
349
|
+
frame = render_sim(get_attr(self.env, 'sim'), height, width, camera)
|
|
350
|
+
|
|
351
|
+
if exists(frame):
|
|
352
|
+
return frame
|
|
353
|
+
|
|
354
|
+
render = get_attr(self.env, 'render')
|
|
355
|
+
|
|
356
|
+
if callable(render):
|
|
357
|
+
return render_with_dims(render, height, width, camera)
|
|
358
|
+
|
|
318
359
|
return super().render(height, width, camera)
|
|
319
360
|
|
|
320
361
|
@property
|
|
@@ -426,9 +467,9 @@ ADAPTER_REGISTRY: list[type[BaseEnvAdapter]] = [
|
|
|
426
467
|
MujocoWarpAdapter,
|
|
427
468
|
IsaacAdapter,
|
|
428
469
|
PyBulletAdapter,
|
|
470
|
+
RoboticsAdapter,
|
|
429
471
|
DMControlAdapter,
|
|
430
472
|
PufferLibAdapter,
|
|
431
|
-
RoboticsAdapter,
|
|
432
473
|
GymnasiumAdapter,
|
|
433
474
|
LegacyGymAdapter,
|
|
434
475
|
DefaultAdapter,
|
{env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/auto_batched_wrapper.py
RENAMED
|
@@ -65,17 +65,18 @@ def get_action_space(env):
|
|
|
65
65
|
return get_adapter(env).action_space, False
|
|
66
66
|
|
|
67
67
|
def action_shape_tree(space):
|
|
68
|
-
# canonical shapes parallel to action structure
|
|
68
|
+
# canonical shapes parallel to action structure — raw dict / list / tuple
|
|
69
|
+
# action spaces are handled as well as gymnasium's composite spaces
|
|
69
70
|
|
|
70
71
|
if not exists(space):
|
|
71
72
|
return None
|
|
72
73
|
|
|
73
|
-
subspaces = get_attr(space, 'spaces')
|
|
74
|
+
subspaces = space if isinstance(space, (dict, list, tuple)) else get_attr(space, 'spaces')
|
|
74
75
|
|
|
75
|
-
if
|
|
76
|
-
|
|
77
|
-
return {key: action_shape_tree(subspace) for key, subspace in subspaces.items()}
|
|
76
|
+
if isinstance(subspaces, dict):
|
|
77
|
+
return {key: action_shape_tree(subspace) for key, subspace in subspaces.items()}
|
|
78
78
|
|
|
79
|
+
if exists(subspaces):
|
|
79
80
|
return [action_shape_tree(subspace) for subspace in subspaces]
|
|
80
81
|
|
|
81
82
|
return get_attr(space, 'shape')
|
|
@@ -36,7 +36,7 @@ def zero_mask(x, mask, fill_scalar = None):
|
|
|
36
36
|
arr = np.asarray(x)
|
|
37
37
|
|
|
38
38
|
if arr.ndim == 0:
|
|
39
|
-
if fill_scalar
|
|
39
|
+
if exists(fill_scalar) and bool(np.asarray(mask).any()):
|
|
40
40
|
return fill_scalar
|
|
41
41
|
return x
|
|
42
42
|
|
|
@@ -51,6 +51,7 @@ def back_to_mask_type(dones, newly):
|
|
|
51
51
|
|
|
52
52
|
def merge_final(current, value, mask):
|
|
53
53
|
if is_tensor(current):
|
|
54
|
+
value = torch.as_tensor(value, device = current.device, dtype = current.dtype)
|
|
54
55
|
return torch.where(broadcast_mask(mask, current), value, current)
|
|
55
56
|
|
|
56
57
|
curr = np.asarray(current)
|
|
@@ -59,7 +60,7 @@ def merge_final(current, value, mask):
|
|
|
59
60
|
return current
|
|
60
61
|
|
|
61
62
|
out = curr.copy()
|
|
62
|
-
out[mask] =
|
|
63
|
+
out[mask] = to_numpy(value)[mask]
|
|
63
64
|
return out
|
|
64
65
|
|
|
65
66
|
# class
|
|
@@ -90,7 +91,7 @@ class EpisodePaddingWrapper(EnvWrapper):
|
|
|
90
91
|
dones = dones_of(terminated, truncated)
|
|
91
92
|
mask = to_numpy(dones).astype(bool)
|
|
92
93
|
|
|
93
|
-
if self._is_done
|
|
94
|
+
if not exists(self._is_done) or len(self._is_done) != len(mask):
|
|
94
95
|
self._is_done = np.zeros(len(mask), dtype = bool)
|
|
95
96
|
|
|
96
97
|
# autoreset envs revive every done slot, so each done is a new
|
|
@@ -106,7 +107,7 @@ class EpisodePaddingWrapper(EnvWrapper):
|
|
|
106
107
|
final_val = first_existing(info, 'final_observation', 'final_obs')
|
|
107
108
|
value = final_val if exists(final_val) else self._last_obs
|
|
108
109
|
|
|
109
|
-
if self._final_obs
|
|
110
|
+
if not exists(self._final_obs):
|
|
110
111
|
self._final_obs = tree_map(copy_leaf, value)
|
|
111
112
|
else:
|
|
112
113
|
self._final_obs = tree_map(partial(merge_final, mask = newly), self._final_obs, value)
|
|
@@ -59,14 +59,27 @@ def get_batch_size(tree) -> int | None:
|
|
|
59
59
|
|
|
60
60
|
first = leaves[0]
|
|
61
61
|
|
|
62
|
-
if
|
|
63
|
-
return
|
|
62
|
+
if isinstance(first, (str, bytes)):
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
ndim = get_attr(first, 'ndim')
|
|
66
|
+
|
|
67
|
+
if exists(ndim):
|
|
68
|
+
return len(first) if ndim > 0 else None
|
|
64
69
|
|
|
65
70
|
return len(first) if exists(get_attr(first, '__len__')) else None
|
|
66
71
|
|
|
67
72
|
def is_array(v):
|
|
68
73
|
return is_tensor(v) or isinstance(v, np.ndarray)
|
|
69
74
|
|
|
75
|
+
def is_foreign_array(v):
|
|
76
|
+
# array-like types from foreign frameworks (e.g. jax.Array, pil Image)
|
|
77
|
+
# that implement numpy's __array__ conversion protocol
|
|
78
|
+
return not is_array(v) and callable(get_attr(v, '__array__'))
|
|
79
|
+
|
|
80
|
+
def is_array_like(v):
|
|
81
|
+
return is_array(v) or is_foreign_array(v)
|
|
82
|
+
|
|
70
83
|
def to_numpy(t):
|
|
71
84
|
return t.detach().cpu().numpy() if is_tensor(t) else np.asarray(t)
|
|
72
85
|
|
|
@@ -101,28 +114,97 @@ def zero_like(x):
|
|
|
101
114
|
arr = np.asarray(x)
|
|
102
115
|
return np.zeros_like(arr, dtype = bool) if arr.ndim > 0 else False
|
|
103
116
|
|
|
117
|
+
def normalize_obs(obs):
|
|
118
|
+
# raw arrays / scalars / text / containers pass through — custom observation
|
|
119
|
+
# objects (rlbench, ...) flatten via get_low_dim_data or unpack their
|
|
120
|
+
# public array, scalar, or container attributes
|
|
121
|
+
|
|
122
|
+
if is_array_like(obs) or is_scalar(obs) or isinstance(obs, (str, bytes, dict, list, tuple)):
|
|
123
|
+
return obs
|
|
124
|
+
|
|
125
|
+
low_dim = get_attr(obs, 'get_low_dim_data')
|
|
126
|
+
|
|
127
|
+
if callable(low_dim):
|
|
128
|
+
try:
|
|
129
|
+
flattened = low_dim()
|
|
130
|
+
if exists(flattened):
|
|
131
|
+
return flattened
|
|
132
|
+
except Exception:
|
|
133
|
+
pass
|
|
134
|
+
|
|
135
|
+
out = {}
|
|
136
|
+
for key in dir(obs):
|
|
137
|
+
if key.startswith('_'):
|
|
138
|
+
continue
|
|
139
|
+
|
|
140
|
+
value = get_attr(obs, key)
|
|
141
|
+
|
|
142
|
+
if callable(value):
|
|
143
|
+
continue
|
|
144
|
+
|
|
145
|
+
if is_array_like(value) or is_scalar(value) or isinstance(value, (dict, list, tuple)):
|
|
146
|
+
out[key] = value
|
|
147
|
+
|
|
148
|
+
return out if out else obs
|
|
149
|
+
|
|
150
|
+
def contains_array(x):
|
|
151
|
+
# deep array-leaf check over nested containers — used to tell an obs tree
|
|
152
|
+
# apart from a plain info dict when language instructions come first
|
|
153
|
+
|
|
154
|
+
if isinstance(x, dict):
|
|
155
|
+
return any(contains_array(v) for v in x.values())
|
|
156
|
+
|
|
157
|
+
if isinstance(x, (list, tuple)):
|
|
158
|
+
return any(contains_array(v) for v in x)
|
|
159
|
+
|
|
160
|
+
return is_array_like(x)
|
|
161
|
+
|
|
162
|
+
def is_instruction(x):
|
|
163
|
+
if isinstance(x, str):
|
|
164
|
+
return True
|
|
165
|
+
|
|
166
|
+
return isinstance(x, (list, tuple)) and len(x) > 0 and all(isinstance(d, str) for d in x)
|
|
167
|
+
|
|
168
|
+
def as_descriptions(x):
|
|
169
|
+
return [x] if isinstance(x, str) else list(x)
|
|
170
|
+
|
|
104
171
|
def normalize_reset_out(out):
|
|
105
172
|
if is_time_step(out):
|
|
106
|
-
return out.observation, {}
|
|
173
|
+
return normalize_obs(out.observation), {}
|
|
174
|
+
|
|
175
|
+
if isinstance(out, tuple):
|
|
176
|
+
if len(out) == 2:
|
|
177
|
+
first, second = out
|
|
178
|
+
|
|
179
|
+
# language-conditioned robotics (e.g. rlbench, calvin, language-table):
|
|
180
|
+
# (instruction(s), obs) — a plain info dict in second means text observation
|
|
107
181
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return obs, {} if info is None else (info if isinstance(info, dict) else {})
|
|
182
|
+
if is_instruction(first) and (not isinstance(second, dict) or contains_array(second)):
|
|
183
|
+
return normalize_obs(second), dict(descriptions = as_descriptions(first))
|
|
111
184
|
|
|
112
|
-
|
|
185
|
+
info = second if isinstance(second, dict) else {}
|
|
186
|
+
return normalize_obs(first), info
|
|
187
|
+
|
|
188
|
+
if len(out) == 3 and is_instruction(out[0]):
|
|
189
|
+
obs = normalize_obs(out[1])
|
|
190
|
+
info = out[2] if isinstance(out[2], dict) else {}
|
|
191
|
+
return obs, {**info, 'descriptions': as_descriptions(out[0])}
|
|
192
|
+
|
|
193
|
+
return normalize_obs(out), {}
|
|
113
194
|
|
|
114
195
|
def normalize_step_out(out):
|
|
115
196
|
if is_time_step(out):
|
|
116
197
|
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)
|
|
198
|
+
return normalize_obs(out.observation), out.reward, last, False, dict(discount = out.discount)
|
|
118
199
|
|
|
119
200
|
if len(out) == 5:
|
|
120
|
-
|
|
201
|
+
obs, reward, terminated, truncated, info = out
|
|
202
|
+
return normalize_obs(obs), reward, terminated, truncated, info if isinstance(info, dict) else {}
|
|
121
203
|
|
|
122
204
|
if len(out) in (3, 4):
|
|
123
205
|
obs, reward, done, *rest = out
|
|
124
206
|
info = rest[0] if rest and isinstance(rest[0], dict) else {}
|
|
125
|
-
return obs, reward, done, zero_like(done), info
|
|
207
|
+
return normalize_obs(obs), reward, done, zero_like(done), info
|
|
126
208
|
|
|
127
209
|
raise ValueError(f'could not standardize step output of length {len(out)}')
|
|
128
210
|
|
|
@@ -135,7 +217,7 @@ def _zero_leaf(x):
|
|
|
135
217
|
return False
|
|
136
218
|
if isinstance(x, (int, float, np.number)):
|
|
137
219
|
return np.zeros_like(x)
|
|
138
|
-
if
|
|
220
|
+
if is_foreign_array(x):
|
|
139
221
|
return np.zeros_like(np.asarray(x))
|
|
140
222
|
return 0
|
|
141
223
|
|
|
@@ -173,11 +255,11 @@ def unpack_vector_observations(arr):
|
|
|
173
255
|
if isinstance(arr, np.ndarray) and arr.dtype != object:
|
|
174
256
|
return arr
|
|
175
257
|
|
|
176
|
-
sample = next((x for x in arr if x
|
|
177
|
-
if sample
|
|
258
|
+
sample = next((x for x in arr if exists(x)), None)
|
|
259
|
+
if not exists(sample):
|
|
178
260
|
return arr
|
|
179
261
|
|
|
180
|
-
trees = [tree_map(_zero_leaf, sample) if x
|
|
262
|
+
trees = [tree_map(_zero_leaf, sample) if not exists(x) else x for x in arr]
|
|
181
263
|
return stack_trees(trees)
|
|
182
264
|
|
|
183
265
|
# environment probes
|
|
@@ -336,12 +418,12 @@ class TransformObservationWrapper(EnvWrapper):
|
|
|
336
418
|
|
|
337
419
|
def reset(self, **kwargs):
|
|
338
420
|
obs, info = self.env.reset(**kwargs)
|
|
339
|
-
obs = self.
|
|
421
|
+
obs = self.transform(obs, done = None)
|
|
340
422
|
|
|
341
423
|
if isinstance(info, dict):
|
|
342
424
|
for key in FINAL_OBSERVATION_KEYS:
|
|
343
425
|
if key in info:
|
|
344
|
-
info[key] = self.
|
|
426
|
+
info[key] = self.transform(info[key], done = None)
|
|
345
427
|
|
|
346
428
|
return obs, info
|
|
347
429
|
|
|
@@ -349,17 +431,16 @@ class TransformObservationWrapper(EnvWrapper):
|
|
|
349
431
|
obs, reward, terminated, truncated, info = self.env.step(action)
|
|
350
432
|
done = dones_of(terminated, truncated) if self.autoresets else None
|
|
351
433
|
|
|
352
|
-
out = self.
|
|
434
|
+
out = self.transform(obs, done = done)
|
|
353
435
|
|
|
354
436
|
if isinstance(info, dict):
|
|
355
437
|
for key in FINAL_OBSERVATION_KEYS:
|
|
356
438
|
if key in info:
|
|
357
439
|
if not self.takes_done:
|
|
358
|
-
info[key] = self.
|
|
440
|
+
info[key] = self.transform(info[key])
|
|
359
441
|
elif not self.autoresets:
|
|
360
442
|
info[key] = out
|
|
361
443
|
|
|
362
444
|
return out, reward, terminated, truncated, info
|
|
363
445
|
|
|
364
446
|
ObservationWrapper = TransformObservationWrapper
|
|
365
|
-
|
{env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/image_wrapper.py
RENAMED
|
@@ -31,7 +31,7 @@ def render_frame(env, image_size = (64, 64), camera = None):
|
|
|
31
31
|
img = env_render(env, height, width, camera)
|
|
32
32
|
|
|
33
33
|
if not exists(img):
|
|
34
|
-
if env_render_mode(env)
|
|
34
|
+
if not exists(env_render_mode(env)):
|
|
35
35
|
raise ValueError(
|
|
36
36
|
'env must be created with render_mode = "rgb_array", '
|
|
37
37
|
'e.g. gym.make(id, render_mode = "rgb_array")'
|
|
@@ -7,6 +7,8 @@ import numpy as np
|
|
|
7
7
|
import torch
|
|
8
8
|
from torch import is_tensor
|
|
9
9
|
|
|
10
|
+
from .helpers import default, exists
|
|
11
|
+
|
|
10
12
|
# mock sims — stand-ins for real-world environments with varying MDP interfaces
|
|
11
13
|
# all share the underlying dynamics; each emulates the quirks of a real sim
|
|
12
14
|
|
|
@@ -60,6 +62,10 @@ class DiscreteSpace:
|
|
|
60
62
|
def sample(self):
|
|
61
63
|
return np.random.randint(self.n)
|
|
62
64
|
|
|
65
|
+
class DictSpace:
|
|
66
|
+
def __init__(self, **spaces):
|
|
67
|
+
self.spaces = spaces
|
|
68
|
+
|
|
63
69
|
# sim-specific render surfaces — emulating how each sim actually produces images
|
|
64
70
|
|
|
65
71
|
class ActionSpec:
|
|
@@ -125,16 +131,20 @@ class MockEnv:
|
|
|
125
131
|
return False
|
|
126
132
|
|
|
127
133
|
def seed(self, seed = 0):
|
|
134
|
+
seed = default(seed, 0)
|
|
128
135
|
self.rng = np.random.default_rng(seed)
|
|
129
136
|
torch.manual_seed(seed)
|
|
130
137
|
self.reset_state()
|
|
131
138
|
|
|
132
139
|
def reset(self, seed = None, **kwargs):
|
|
133
|
-
|
|
140
|
+
self.restart(seed)
|
|
141
|
+
return self.obs(), {}
|
|
142
|
+
|
|
143
|
+
def restart(self, seed = None):
|
|
144
|
+
if exists(seed):
|
|
134
145
|
self.seed(seed)
|
|
135
146
|
else:
|
|
136
147
|
self.reset_state()
|
|
137
|
-
return self.obs(), {}
|
|
138
148
|
|
|
139
149
|
def reset_state(self):
|
|
140
150
|
self.t = np.zeros(self.num_envs, dtype = int) if self.is_vector else 0
|
|
@@ -406,10 +416,7 @@ class MjlabMockEnv(MockEnv):
|
|
|
406
416
|
super().__init__(seed)
|
|
407
417
|
|
|
408
418
|
def reset(self, *, seed = None, env_ids = None, options = None):
|
|
409
|
-
|
|
410
|
-
self.seed(seed)
|
|
411
|
-
else:
|
|
412
|
-
self.reset_state()
|
|
419
|
+
self.restart(seed)
|
|
413
420
|
return self.obs(), {}
|
|
414
421
|
|
|
415
422
|
def obs(self):
|
|
@@ -487,10 +494,7 @@ class ManiSkillMockEnv(MockEnv):
|
|
|
487
494
|
return dict(state = state) if self.obs_mode == 'state_dict' else state
|
|
488
495
|
|
|
489
496
|
def reset(self, seed = None, options = None):
|
|
490
|
-
|
|
491
|
-
self.seed(seed)
|
|
492
|
-
else:
|
|
493
|
-
self.reset_state()
|
|
497
|
+
self.restart(seed)
|
|
494
498
|
return self.obs(), dict(reconfigure = False)
|
|
495
499
|
|
|
496
500
|
def step(self, action):
|
|
@@ -515,6 +519,9 @@ class JaxArray:
|
|
|
515
519
|
def __array__(self, dtype = None):
|
|
516
520
|
return np.asarray(self._arr, dtype = dtype)
|
|
517
521
|
|
|
522
|
+
def __getitem__(self, item):
|
|
523
|
+
return JaxArray(self._arr[item])
|
|
524
|
+
|
|
518
525
|
@property
|
|
519
526
|
def dtype(self):
|
|
520
527
|
return self._arr.dtype
|
|
@@ -550,19 +557,15 @@ class BraxMockEnv(MockEnv):
|
|
|
550
557
|
self.advance(action)
|
|
551
558
|
return JaxArray(self.obs()), JaxArray(np.ones(self.num_envs)), JaxArray(self.is_done()), {}
|
|
552
559
|
|
|
553
|
-
# mujoco-mjx — gymnasium-style 5-tuple step, (obs, info) reset, single env,
|
|
554
|
-
# every stream (obs / reward / dones)
|
|
555
|
-
# on the mjx jax physics backend
|
|
560
|
+
# mujoco-mjx — gymnasium-style 5-tuple step, (obs, info) reset, single env,
|
|
561
|
+
# every stream (obs / reward / dones) a jax array
|
|
556
562
|
|
|
557
563
|
class MjxMockEnv(MockEnv):
|
|
558
564
|
obs_dim = 8
|
|
559
565
|
action_dim = 6
|
|
560
566
|
|
|
561
567
|
def reset(self, seed = None, **kwargs):
|
|
562
|
-
|
|
563
|
-
self.seed(seed)
|
|
564
|
-
else:
|
|
565
|
-
self.reset_state()
|
|
568
|
+
self.restart(seed)
|
|
566
569
|
return JaxArray(self.obs()), {}
|
|
567
570
|
|
|
568
571
|
def step(self, action):
|
|
@@ -652,3 +655,232 @@ class TupleObsMockEnv(MockEnv):
|
|
|
652
655
|
def step(self, action):
|
|
653
656
|
self.advance(action)
|
|
654
657
|
return self.obs(), np.ones(self.num_envs), self.is_done(), np.zeros(self.num_envs, dtype = bool), {}
|
|
658
|
+
|
|
659
|
+
# rlbench — language-conditioned manipulation: reset -> (descriptions, obs),
|
|
660
|
+
# step -> (obs, reward, terminate); obs unpacks via get_low_dim_data()
|
|
661
|
+
|
|
662
|
+
class RLBenchObservation:
|
|
663
|
+
def __init__(self, joint_pos, gripper, wrist_rgb):
|
|
664
|
+
self.joint_positions = joint_pos
|
|
665
|
+
self.gripper_open = gripper
|
|
666
|
+
self.wrist_rgb = wrist_rgb
|
|
667
|
+
|
|
668
|
+
def get_low_dim_data(self):
|
|
669
|
+
return np.concatenate([self.joint_positions, np.array([self.gripper_open])])
|
|
670
|
+
|
|
671
|
+
class RLBenchMockEnv(MockEnv):
|
|
672
|
+
obs_dim = 8
|
|
673
|
+
action_dim = 8
|
|
674
|
+
max_steps = 40
|
|
675
|
+
|
|
676
|
+
def __init__(self, task_name = 'open_drawer', seed = 0):
|
|
677
|
+
self.task_name = task_name
|
|
678
|
+
self.descriptions = [f'{task_name.replace("_", " ")} task']
|
|
679
|
+
super().__init__(seed)
|
|
680
|
+
|
|
681
|
+
def rlbench_obs(self):
|
|
682
|
+
return RLBenchObservation(
|
|
683
|
+
joint_pos = self.obs()[:7],
|
|
684
|
+
gripper = 1.0,
|
|
685
|
+
wrist_rgb = np.zeros((64, 64, 3), dtype = np.uint8)
|
|
686
|
+
)
|
|
687
|
+
|
|
688
|
+
def reset(self, seed = None, **kwargs):
|
|
689
|
+
self.restart(seed)
|
|
690
|
+
return self.descriptions, self.rlbench_obs()
|
|
691
|
+
|
|
692
|
+
def step(self, action):
|
|
693
|
+
self.advance(action)
|
|
694
|
+
return self.rlbench_obs(), 1.0, self.is_done()
|
|
695
|
+
|
|
696
|
+
# omnigibson (behavior-1k) — obs-dict-only reset, 5-tuple step, sim render
|
|
697
|
+
|
|
698
|
+
class OmniGibsonMockEnv(MockEnv):
|
|
699
|
+
obs_dim = 14
|
|
700
|
+
action_dim = 6
|
|
701
|
+
max_steps = 100
|
|
702
|
+
|
|
703
|
+
def __init__(self, seed = 0):
|
|
704
|
+
super().__init__(seed)
|
|
705
|
+
self.sim = FakeSim()
|
|
706
|
+
|
|
707
|
+
def obs_dict(self):
|
|
708
|
+
return dict(
|
|
709
|
+
robot0_proprio = self.obs(),
|
|
710
|
+
rgb = np.zeros((64, 64, 3), dtype = np.uint8)
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
def reset(self, seed = None, **kwargs):
|
|
714
|
+
self.restart(seed)
|
|
715
|
+
return self.obs_dict()
|
|
716
|
+
|
|
717
|
+
def step(self, action):
|
|
718
|
+
self.advance(action)
|
|
719
|
+
return self.obs_dict(), 1.0, self.is_done(), False, {}
|
|
720
|
+
|
|
721
|
+
# robomimic — structured obs dict, obs-only reset, 4-tuple step
|
|
722
|
+
|
|
723
|
+
class RoboMimicMockEnv(MockEnv):
|
|
724
|
+
obs_dim = 10
|
|
725
|
+
action_dim = 7
|
|
726
|
+
max_steps = 50
|
|
727
|
+
|
|
728
|
+
def obs_dict(self):
|
|
729
|
+
return dict(
|
|
730
|
+
robot0_eef_pos = self.obs()[:3],
|
|
731
|
+
robot0_eef_quat = np.array([0., 0., 0., 1.]),
|
|
732
|
+
object = self.obs()[3:]
|
|
733
|
+
)
|
|
734
|
+
|
|
735
|
+
def reset(self, seed = None, **kwargs):
|
|
736
|
+
self.restart(seed)
|
|
737
|
+
return self.obs_dict()
|
|
738
|
+
|
|
739
|
+
def step(self, action):
|
|
740
|
+
self.advance(action)
|
|
741
|
+
return self.obs_dict(), 1.0, self.is_done(), {}
|
|
742
|
+
|
|
743
|
+
# myosuite — musculoskeletal mujoco robotics: high-dim muscle states,
|
|
744
|
+
# physics render, 5-tuple step
|
|
745
|
+
|
|
746
|
+
class MyoSuiteMockEnv(MockEnv):
|
|
747
|
+
obs_dim = 36
|
|
748
|
+
action_dim = 12
|
|
749
|
+
max_steps = 80
|
|
750
|
+
|
|
751
|
+
def __init__(self, seed = 0):
|
|
752
|
+
super().__init__(seed)
|
|
753
|
+
self.physics = FakePhysics()
|
|
754
|
+
|
|
755
|
+
def step(self, action):
|
|
756
|
+
self.advance(action)
|
|
757
|
+
return self.obs(), 1.0, self.is_done(), False, {}
|
|
758
|
+
|
|
759
|
+
# gymnasium-robotics (farama goal envs: fetch, shadow hand, franka kitchen) —
|
|
760
|
+
# dict obs (observation / achieved_goal / desired_goal), exposes compute_reward
|
|
761
|
+
|
|
762
|
+
class GymnasiumRoboticsMockEnv(MockEnv):
|
|
763
|
+
obs_dim = 10
|
|
764
|
+
goal_dim = 3
|
|
765
|
+
action_dim = 4
|
|
766
|
+
max_steps = 50
|
|
767
|
+
|
|
768
|
+
@property
|
|
769
|
+
def observation_space(self):
|
|
770
|
+
return DictSpace(
|
|
771
|
+
observation = Space((self.obs_dim,)),
|
|
772
|
+
achieved_goal = Space((self.goal_dim,)),
|
|
773
|
+
desired_goal = Space((self.goal_dim,)),
|
|
774
|
+
)
|
|
775
|
+
|
|
776
|
+
def goal_dict(self):
|
|
777
|
+
return dict(
|
|
778
|
+
observation = self.obs(),
|
|
779
|
+
achieved_goal = self.obs()[:self.goal_dim],
|
|
780
|
+
desired_goal = np.zeros(self.goal_dim)
|
|
781
|
+
)
|
|
782
|
+
|
|
783
|
+
def reset(self, seed = None, **kwargs):
|
|
784
|
+
self.restart(seed)
|
|
785
|
+
return self.goal_dict(), {}
|
|
786
|
+
|
|
787
|
+
def compute_reward(self, achieved_goal, desired_goal, info):
|
|
788
|
+
return -np.linalg.norm(achieved_goal - desired_goal, axis = -1)
|
|
789
|
+
|
|
790
|
+
def step(self, action):
|
|
791
|
+
self.advance(action)
|
|
792
|
+
obs = self.goal_dict()
|
|
793
|
+
reward = float(self.compute_reward(obs['achieved_goal'], obs['desired_goal'], {}))
|
|
794
|
+
return obs, reward, self.is_done(), False, {}
|
|
795
|
+
|
|
796
|
+
# lerobot (hugging face aloha / pusht) — bimanual multi-camera robotics:
|
|
797
|
+
# dict obs with visual pixels + robot state, 5-tuple step
|
|
798
|
+
|
|
799
|
+
class LeRobotMockEnv(MockEnv):
|
|
800
|
+
obs_dim = 14
|
|
801
|
+
action_dim = 14
|
|
802
|
+
max_steps = 60
|
|
803
|
+
|
|
804
|
+
def obs_dict(self):
|
|
805
|
+
return dict(
|
|
806
|
+
agent_pos = self.obs(),
|
|
807
|
+
pixels = dict(
|
|
808
|
+
top = np.zeros((64, 64, 3), dtype = np.uint8),
|
|
809
|
+
wrist = np.zeros((64, 64, 3), dtype = np.uint8)
|
|
810
|
+
)
|
|
811
|
+
)
|
|
812
|
+
|
|
813
|
+
def reset(self, seed = None, **kwargs):
|
|
814
|
+
self.restart(seed)
|
|
815
|
+
return self.obs_dict(), {}
|
|
816
|
+
|
|
817
|
+
def step(self, action):
|
|
818
|
+
self.advance(action)
|
|
819
|
+
return self.obs_dict(), 1.0, self.is_done(), False, {}
|
|
820
|
+
|
|
821
|
+
# calvin (language-conditioned multi-task 3d manipulation) — string instruction
|
|
822
|
+
# reset with structured vision + proprio dict, 5-tuple step
|
|
823
|
+
|
|
824
|
+
class CalvinMockEnv(MockEnv):
|
|
825
|
+
obs_dim = 15
|
|
826
|
+
action_dim = 7
|
|
827
|
+
max_steps = 40
|
|
828
|
+
|
|
829
|
+
def __init__(self, task_instruction = 'open drawer and push red block', seed = 0):
|
|
830
|
+
super().__init__(seed = seed)
|
|
831
|
+
self.task_instruction = task_instruction
|
|
832
|
+
|
|
833
|
+
def obs_dict(self):
|
|
834
|
+
return dict(
|
|
835
|
+
robot_obs = self.obs(),
|
|
836
|
+
rgb_obs = dict(
|
|
837
|
+
rgb_static = np.zeros((64, 64, 3), dtype = np.uint8),
|
|
838
|
+
rgb_gripper = np.zeros((64, 64, 3), dtype = np.uint8)
|
|
839
|
+
)
|
|
840
|
+
)
|
|
841
|
+
|
|
842
|
+
def reset(self, seed = None, **kwargs):
|
|
843
|
+
self.restart(seed)
|
|
844
|
+
return self.task_instruction, self.obs_dict()
|
|
845
|
+
|
|
846
|
+
def step(self, action):
|
|
847
|
+
self.advance(action)
|
|
848
|
+
return self.obs_dict(), 1.0, self.is_done(), False, {}
|
|
849
|
+
|
|
850
|
+
# pusht (diffusion policy planar pushing benchmark) — 2-dim eef actions in (0, 512),
|
|
851
|
+
# dict observation with agent_pos and block_pose
|
|
852
|
+
|
|
853
|
+
class PushTMockEnv(MockEnv):
|
|
854
|
+
obs_dim = 2
|
|
855
|
+
action_dim = 2
|
|
856
|
+
max_steps = 50
|
|
857
|
+
|
|
858
|
+
@property
|
|
859
|
+
def action_space(self):
|
|
860
|
+
return Space((self.action_dim,), 0., 512.)
|
|
861
|
+
|
|
862
|
+
def obs_dict(self):
|
|
863
|
+
return dict(
|
|
864
|
+
agent_pos = self.obs(),
|
|
865
|
+
block_pose = np.zeros(3)
|
|
866
|
+
)
|
|
867
|
+
|
|
868
|
+
def reset(self, seed = None, **kwargs):
|
|
869
|
+
self.restart(seed)
|
|
870
|
+
return self.obs_dict(), {}
|
|
871
|
+
|
|
872
|
+
def step(self, action):
|
|
873
|
+
self.advance(action)
|
|
874
|
+
return self.obs_dict(), 1.0, self.is_done(), False, {}
|
|
875
|
+
|
|
876
|
+
# gym-pybullet-drones / aerial robotics — 4-dim motor rpm action space [-1, 1],
|
|
877
|
+
# 12-dim kinematics observation (pos, rpy, vel, ang_vel)
|
|
878
|
+
|
|
879
|
+
class DroneAviaryMockEnv(MockEnv):
|
|
880
|
+
obs_dim = 12
|
|
881
|
+
action_dim = 4
|
|
882
|
+
max_steps = 60
|
|
883
|
+
|
|
884
|
+
def step(self, action):
|
|
885
|
+
self.advance(action)
|
|
886
|
+
return self.obs(), 1.0, self.is_done(), False, {}
|
|
@@ -43,6 +43,9 @@ def space_from_action_spec(env):
|
|
|
43
43
|
|
|
44
44
|
# action-space duck-typing across gymnasium, dm_control, and custom envs
|
|
45
45
|
|
|
46
|
+
def shape_dim(shape) -> int:
|
|
47
|
+
return int(np.prod(shape)) if len(shape) > 0 else 1
|
|
48
|
+
|
|
46
49
|
def action_space_dim(space) -> int | None:
|
|
47
50
|
if not exists(space):
|
|
48
51
|
return None
|
|
@@ -55,11 +58,30 @@ def action_space_dim(space) -> int | None:
|
|
|
55
58
|
if exists(nvec):
|
|
56
59
|
return int(np.prod(nvec))
|
|
57
60
|
|
|
61
|
+
shape = get_attr(space, 'shape')
|
|
62
|
+
return shape_dim(shape) if exists(shape) else None
|
|
63
|
+
|
|
64
|
+
def space_dim(space) -> int | None:
|
|
65
|
+
# flat dim of an observation space tree — composite spaces sum their children
|
|
66
|
+
|
|
67
|
+
if not exists(space):
|
|
68
|
+
return None
|
|
69
|
+
|
|
58
70
|
shape = get_attr(space, 'shape')
|
|
59
71
|
if exists(shape):
|
|
60
|
-
return
|
|
72
|
+
return shape_dim(shape)
|
|
73
|
+
|
|
74
|
+
subspaces = get_attr(space, 'spaces')
|
|
75
|
+
if not exists(subspaces):
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
child_spaces = subspaces.values() if isinstance(subspaces, dict) else subspaces
|
|
79
|
+
child_dims = [space_dim(child) for child in child_spaces]
|
|
80
|
+
|
|
81
|
+
if not child_dims or not all(map(exists, child_dims)):
|
|
82
|
+
return None
|
|
61
83
|
|
|
62
|
-
return
|
|
84
|
+
return sum(child_dims)
|
|
63
85
|
|
|
64
86
|
def action_space_is_discrete(space) -> bool:
|
|
65
87
|
return exists(get_attr(space, 'n'))
|
|
@@ -92,9 +114,4 @@ def action_dim_of(env) -> int | None:
|
|
|
92
114
|
def obs_dim_of(env) -> int | None:
|
|
93
115
|
from .helpers import get_adapter
|
|
94
116
|
space = get_adapter(env).observation_space
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if exists(shape):
|
|
98
|
-
return int(np.prod(shape)) if len(shape) > 0 else 1
|
|
99
|
-
|
|
100
|
-
return get_attr(env, 'obs_dim')
|
|
117
|
+
return default(space_dim(space), get_attr(env, 'obs_dim'))
|
{env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/tensor_wrapper.py
RENAMED
|
@@ -13,7 +13,7 @@ from .helpers import (
|
|
|
13
13
|
FINAL_OBSERVATION_MASK_KEYS,
|
|
14
14
|
exists,
|
|
15
15
|
get_adapter,
|
|
16
|
-
|
|
16
|
+
is_foreign_array,
|
|
17
17
|
is_scalar,
|
|
18
18
|
unpack_vector_observations,
|
|
19
19
|
)
|
|
@@ -26,7 +26,7 @@ def to_torch_leaf(t, device, cast_obs_to_float = True):
|
|
|
26
26
|
t = from_numpy(t.copy())
|
|
27
27
|
elif is_scalar(t):
|
|
28
28
|
t = tensor(t)
|
|
29
|
-
elif
|
|
29
|
+
elif is_foreign_array(t):
|
|
30
30
|
t = from_numpy(np.asarray(t))
|
|
31
31
|
else:
|
|
32
32
|
return t
|
|
@@ -98,7 +98,7 @@ class TensorWrapper(EnvWrapper):
|
|
|
98
98
|
|
|
99
99
|
# deprecated alias, kept for backwards compatibility
|
|
100
100
|
|
|
101
|
-
if cast_float64_to_float32
|
|
101
|
+
if exists(cast_float64_to_float32):
|
|
102
102
|
cast_obs_to_float = cast_float64_to_float32
|
|
103
103
|
|
|
104
104
|
self.device = torch_device(device)
|
|
@@ -144,8 +144,12 @@ class TensorWrapper(EnvWrapper):
|
|
|
144
144
|
return tree_map(convert, t)
|
|
145
145
|
|
|
146
146
|
def step(self, action):
|
|
147
|
-
if self.convert_in
|
|
148
|
-
|
|
147
|
+
if self.convert_in:
|
|
148
|
+
if not self.torch_native:
|
|
149
|
+
action = torch_to_numpy(action)
|
|
150
|
+
elif not is_tensor(action):
|
|
151
|
+
# torch-native sims take torch on device — lift numpy / foreign actions
|
|
152
|
+
action = numpy_to_torch(action, device = self.device, cast_obs_to_float = False)
|
|
149
153
|
|
|
150
154
|
obs, reward, terminated, truncated, info = self.env.step(action)
|
|
151
155
|
|
{env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/time_limit_wrapper.py
RENAMED
|
@@ -18,18 +18,15 @@ from .helpers import (
|
|
|
18
18
|
)
|
|
19
19
|
|
|
20
20
|
def back_to_like(t, numpy_arr):
|
|
21
|
-
|
|
22
|
-
return torch.from_numpy(numpy_arr).to(t.device)
|
|
21
|
+
# scalar dones stay scalar
|
|
23
22
|
|
|
24
|
-
if isinstance(t, np.ndarray):
|
|
25
|
-
|
|
23
|
+
if is_tensor(t) or isinstance(t, np.ndarray):
|
|
24
|
+
res = torch.from_numpy(numpy_arr).to(t.device) if is_tensor(t) else numpy_arr
|
|
25
|
+
return res.squeeze() if t.ndim == 0 else res
|
|
26
26
|
|
|
27
27
|
# python scalars (single-env dones) collapse to a scalar; foreign
|
|
28
28
|
# array-likes (jax) keep the numpy array so the batch dim survives
|
|
29
29
|
|
|
30
|
-
if numpy_arr.ndim == 0:
|
|
31
|
-
return bool(numpy_arr)
|
|
32
|
-
|
|
33
30
|
if numpy_arr.size == 1:
|
|
34
31
|
return bool(numpy_arr.reshape(-1)[0])
|
|
35
32
|
|
|
@@ -16,7 +16,7 @@ from .helpers import (
|
|
|
16
16
|
truthy_attr,
|
|
17
17
|
_zero_leaf,
|
|
18
18
|
)
|
|
19
|
-
from .spaces import action_dim_of
|
|
19
|
+
from .spaces import action_dim_of, obs_dim_of
|
|
20
20
|
from .standardize_wrapper import StandardizeWrapper
|
|
21
21
|
|
|
22
22
|
# leaf helpers
|
|
@@ -216,6 +216,7 @@ class MultiprocessingVecEnv:
|
|
|
216
216
|
self.action_space = self._get_attr('action_space')
|
|
217
217
|
self.observation_space = self._get_attr('observation_space')
|
|
218
218
|
self.action_dim = action_dim_of(self)
|
|
219
|
+
self.obs_dim = obs_dim_of(self)
|
|
219
220
|
|
|
220
221
|
self.seed(seed)
|
|
221
222
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/done_tracker_wrapper.py
RENAMED
|
File without changes
|
{env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/flatten_obs_wrapper.py
RENAMED
|
File without changes
|
|
File without changes
|
{env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.3}/env_ssl_wrapper/standardize/standardize_wrapper.py
RENAMED
|
File without changes
|
|
File without changes
|