env-ssl-wrapper 0.5.2__tar.gz → 0.5.4__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.4}/PKG-INFO +1 -1
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/action_chunk.py +8 -3
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/evaluate.py +60 -48
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/memory_trace.py +21 -9
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/standardize/__init__.py +32 -0
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/standardize/action_transform_wrapper.py +3 -1
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/standardize/adapters.py +62 -21
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/standardize/auto_batched_wrapper.py +22 -24
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/standardize/done_tracker_wrapper.py +2 -0
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/standardize/episode_padding_wrapper.py +7 -4
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/standardize/flatten_obs_wrapper.py +5 -1
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/standardize/helpers.py +121 -33
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/standardize/image_wrapper.py +4 -4
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/standardize/mocks.py +249 -17
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/standardize/spaces.py +25 -8
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/standardize/standardize_wrapper.py +2 -0
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/standardize/tensor_wrapper.py +12 -14
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/standardize/time_limit_wrapper.py +6 -7
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/standardize/utils.py +10 -31
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/standardize/vector.py +29 -16
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/pyproject.toml +1 -1
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/.gitignore +0 -0
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/LICENSE +0 -0
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/README.md +0 -0
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/__init__.py +0 -0
- {env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/standardize/standardize_env_wrapper.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.4
|
|
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
|
|
@@ -11,6 +11,7 @@ from .standardize.helpers import (
|
|
|
11
11
|
default,
|
|
12
12
|
any_true,
|
|
13
13
|
dones_of,
|
|
14
|
+
env_takes_torch,
|
|
14
15
|
first_existing,
|
|
15
16
|
get_adapter,
|
|
16
17
|
get_attr,
|
|
@@ -29,16 +30,20 @@ def chunk_steps(actions, axis):
|
|
|
29
30
|
def unbatch_step(action):
|
|
30
31
|
# chunks always carry a num_envs axis - drop it for plain single envs
|
|
31
32
|
if is_tensor(action):
|
|
32
|
-
|
|
33
|
+
action = action.squeeze(0)
|
|
34
|
+
return action.item() if action.ndim == 0 and not action.is_floating_point() else action
|
|
33
35
|
|
|
34
36
|
if isinstance(action, np.ndarray):
|
|
35
|
-
|
|
37
|
+
action = np.squeeze(action, axis = 0) if action.ndim > 0 else action
|
|
38
|
+
return action.item() if action.ndim == 0 and np.issubdtype(action.dtype, np.integer) else action
|
|
36
39
|
|
|
37
40
|
return action
|
|
38
41
|
|
|
39
42
|
# wrapper
|
|
40
43
|
|
|
41
44
|
class ActionChunkWrapper(EnvWrapper):
|
|
45
|
+
priority = 100
|
|
46
|
+
|
|
42
47
|
"""
|
|
43
48
|
Open-loop action chunking - executes a chunk of actions in one step,
|
|
44
49
|
temporally compressing the environment by the chunk length.
|
|
@@ -71,7 +76,7 @@ class ActionChunkWrapper(EnvWrapper):
|
|
|
71
76
|
self.action_shape = tuple(get_attr(action_space, 'shape', ()) or ())
|
|
72
77
|
|
|
73
78
|
self.expects_batch = is_vectorized(env)
|
|
74
|
-
self.convert_in = not
|
|
79
|
+
self.convert_in = not env_takes_torch(env)
|
|
75
80
|
|
|
76
81
|
@property
|
|
77
82
|
def chunk_action_shape(self):
|
|
@@ -12,14 +12,16 @@ from torch.utils._pytree import tree_flatten, tree_map
|
|
|
12
12
|
from .standardize.spaces import action_space_is_discrete
|
|
13
13
|
from .standardize.helpers import (
|
|
14
14
|
EnvWrapper,
|
|
15
|
+
dones_of,
|
|
15
16
|
env_autoresets,
|
|
16
17
|
env_num_envs,
|
|
17
18
|
env_render,
|
|
19
|
+
env_takes_torch,
|
|
18
20
|
exists,
|
|
19
21
|
first_existing,
|
|
20
22
|
get_adapter,
|
|
21
23
|
get_attr,
|
|
22
|
-
|
|
24
|
+
is_array_like,
|
|
23
25
|
is_vectorized,
|
|
24
26
|
normalize_reset_out,
|
|
25
27
|
normalize_step_out,
|
|
@@ -90,37 +92,40 @@ def is_distribution(out):
|
|
|
90
92
|
def to_action(env, out, deterministic = True):
|
|
91
93
|
# distributions collapse to their mode, raw floating logits over a discrete space to their argmax
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
if
|
|
95
|
-
|
|
95
|
+
def convert_leaf(val):
|
|
96
|
+
if is_distribution(val):
|
|
97
|
+
if not deterministic:
|
|
98
|
+
return val.sample()
|
|
96
99
|
|
|
97
|
-
|
|
100
|
+
probs = get_attr(val, 'probs')
|
|
98
101
|
|
|
99
|
-
|
|
102
|
+
# categorical-style probs carry a trailing action axis to argmax over
|
|
100
103
|
|
|
101
|
-
|
|
102
|
-
|
|
104
|
+
if exists(probs) and probs.ndim >= 2 and probs.shape[-1] > 1:
|
|
105
|
+
return probs.argmax(dim = -1)
|
|
103
106
|
|
|
104
|
-
|
|
107
|
+
# bernoulli-style probabilities threshold at 0.5
|
|
105
108
|
|
|
106
|
-
|
|
107
|
-
|
|
109
|
+
if exists(probs):
|
|
110
|
+
return (probs > 0.5).long()
|
|
108
111
|
|
|
109
|
-
|
|
112
|
+
return val.mean
|
|
110
113
|
|
|
111
|
-
|
|
112
|
-
|
|
114
|
+
if is_tensor(val) and val.is_floating_point() and val.ndim > 0:
|
|
115
|
+
num_actions = get_attr(get_adapter(env).action_space, 'n')
|
|
113
116
|
|
|
114
|
-
|
|
115
|
-
|
|
117
|
+
if exists(num_actions) and val.shape[-1] == num_actions:
|
|
118
|
+
return val.argmax(dim = -1) if deterministic else Categorical(logits = val).sample()
|
|
116
119
|
|
|
117
|
-
|
|
120
|
+
return val
|
|
121
|
+
|
|
122
|
+
return tree_map(convert_leaf, out)
|
|
118
123
|
|
|
119
124
|
# states and actions
|
|
120
125
|
|
|
121
126
|
def to_actor_obs(obs, device, batched):
|
|
122
127
|
def convert(x):
|
|
123
|
-
if not (
|
|
128
|
+
if not is_array_like(x):
|
|
124
129
|
return x
|
|
125
130
|
|
|
126
131
|
tensor = x if is_tensor(x) else torch.as_tensor(np.asarray(x))
|
|
@@ -135,48 +140,55 @@ def to_actor_obs(obs, device, batched):
|
|
|
135
140
|
return tree_map(convert, obs)
|
|
136
141
|
|
|
137
142
|
def to_env_action(env, action):
|
|
138
|
-
if not is_tensor(action):
|
|
139
|
-
action = torch.as_tensor(action)
|
|
140
|
-
|
|
141
143
|
device = get_attr(env, 'device')
|
|
142
|
-
action = action.to(device) if exists(device) else action
|
|
143
144
|
|
|
144
|
-
|
|
145
|
+
def to_torch(a):
|
|
146
|
+
if not is_tensor(a):
|
|
147
|
+
a = torch.as_tensor(a)
|
|
148
|
+
return a.to(device) if exists(device) else a
|
|
145
149
|
|
|
146
|
-
|
|
147
|
-
if get_adapter(env).torch_native:
|
|
148
|
-
return action
|
|
150
|
+
action = tree_map(to_torch, action)
|
|
149
151
|
|
|
150
|
-
|
|
152
|
+
chunk_shape = get_attr(env, 'chunk_action_shape')
|
|
151
153
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
+
if exists(chunk_shape):
|
|
155
|
+
rank = len(chunk_shape) + 1
|
|
156
|
+
else:
|
|
157
|
+
space = first_existing(env, 'single_action_space', 'action_space')
|
|
158
|
+
shape = get_attr(space, 'shape')
|
|
159
|
+
rank = len(shape) + 1 if exists(shape) else None
|
|
154
160
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
161
|
+
if exists(rank):
|
|
162
|
+
def pad_rank(a):
|
|
163
|
+
while a.ndim < rank:
|
|
164
|
+
a = a[None]
|
|
165
|
+
return a
|
|
158
166
|
|
|
159
|
-
|
|
160
|
-
if action.ndim > 0 and action.shape[0] == 1:
|
|
161
|
-
action = action[0]
|
|
167
|
+
action = tree_map(pad_rank, action)
|
|
162
168
|
|
|
163
|
-
|
|
164
|
-
action = action.item()
|
|
169
|
+
# raw simulators take unbatched numpy — torch-native sims take torch as-is (zero-copy)
|
|
165
170
|
|
|
166
|
-
|
|
171
|
+
if not env_takes_torch(env):
|
|
172
|
+
action = tree_map(to_numpy, action)
|
|
167
173
|
|
|
168
|
-
|
|
174
|
+
# single raw sims get the leading batch dim stripped, while discrete
|
|
175
|
+
# actions additionally collapse to scalars
|
|
169
176
|
|
|
170
|
-
|
|
177
|
+
if not is_vectorized(env) and not exists(chunk_shape):
|
|
178
|
+
def strip_leaf(a):
|
|
179
|
+
if a.ndim > 1 and a.shape[0] == 1:
|
|
180
|
+
a = a[0]
|
|
171
181
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
182
|
+
if action_space_is_discrete(get_adapter(env).action_space):
|
|
183
|
+
if a.ndim > 0 and a.shape[0] == 1:
|
|
184
|
+
a = a[0]
|
|
185
|
+
|
|
186
|
+
if a.ndim == 0:
|
|
187
|
+
a = a.item()
|
|
188
|
+
|
|
189
|
+
return a
|
|
177
190
|
|
|
178
|
-
|
|
179
|
-
action = action[None]
|
|
191
|
+
action = tree_map(strip_leaf, action)
|
|
180
192
|
|
|
181
193
|
return action
|
|
182
194
|
|
|
@@ -319,7 +331,7 @@ def evaluate_actor(
|
|
|
319
331
|
lengths += info.get('chunk_length', 1) if isinstance(info, dict) else 1
|
|
320
332
|
steps += 1
|
|
321
333
|
|
|
322
|
-
done = to_numpy(terminated
|
|
334
|
+
done = to_numpy(dones_of(terminated, truncated)).astype(bool).reshape(-1)
|
|
323
335
|
|
|
324
336
|
if exists(max_steps):
|
|
325
337
|
done |= to_numpy(steps >= max_steps).reshape(-1)
|
|
@@ -6,16 +6,16 @@ from torch import is_tensor
|
|
|
6
6
|
|
|
7
7
|
from .standardize.helpers import (
|
|
8
8
|
TransformObservationWrapper,
|
|
9
|
-
exists,
|
|
10
|
-
default,
|
|
11
9
|
any_true,
|
|
10
|
+
cast_tuple,
|
|
11
|
+
default,
|
|
12
|
+
exists,
|
|
13
|
+
is_array_like,
|
|
14
|
+
is_scalar,
|
|
12
15
|
)
|
|
13
16
|
|
|
14
17
|
# helper functions
|
|
15
18
|
|
|
16
|
-
def cast_tuple(val):
|
|
17
|
-
return val if isinstance(val, (tuple, list)) else (val,)
|
|
18
|
-
|
|
19
19
|
def calc_lerp_weight(lam, done, x):
|
|
20
20
|
if not exists(done) or not any_true(done):
|
|
21
21
|
return 1. - lam
|
|
@@ -23,7 +23,9 @@ def calc_lerp_weight(lam, done, x):
|
|
|
23
23
|
# reset trace on done, else decay
|
|
24
24
|
|
|
25
25
|
if not is_tensor(done):
|
|
26
|
-
done = torch.as_tensor(done, device = x.device)
|
|
26
|
+
done = torch.as_tensor(done, device = x.device, dtype = torch.bool)
|
|
27
|
+
else:
|
|
28
|
+
done = done.to(device = x.device, dtype = torch.bool)
|
|
27
29
|
|
|
28
30
|
weight = torch.where(done, 1., 1. - lam).to(x)
|
|
29
31
|
|
|
@@ -36,6 +38,7 @@ def calc_lerp_weight(lam, done, x):
|
|
|
36
38
|
# https://arxiv.org/abs/2503.15200
|
|
37
39
|
|
|
38
40
|
class MemoryTraceWrapper(TransformObservationWrapper):
|
|
41
|
+
priority = 80
|
|
39
42
|
|
|
40
43
|
def __init__(
|
|
41
44
|
self,
|
|
@@ -61,17 +64,26 @@ class MemoryTraceWrapper(TransformObservationWrapper):
|
|
|
61
64
|
return f'{prefix}_{lam}' if len(self.lambdas) > 1 else prefix
|
|
62
65
|
|
|
63
66
|
def transform_obs(self, obs, done = None):
|
|
64
|
-
|
|
67
|
+
if isinstance(obs, (tuple, list)):
|
|
68
|
+
out = {f'{self.obs_key}_{i}': elem for i, elem in enumerate(obs)}
|
|
69
|
+
elif isinstance(obs, dict):
|
|
70
|
+
out = dict(obs)
|
|
71
|
+
else:
|
|
72
|
+
out = {self.obs_key: obs}
|
|
73
|
+
|
|
65
74
|
target_keys = tuple(k for k in default(self.keys, tuple(out.keys())) if k in out)
|
|
66
75
|
|
|
67
76
|
for key in target_keys:
|
|
68
77
|
val = out.get(key)
|
|
69
78
|
|
|
70
|
-
if not exists(val):
|
|
79
|
+
if not exists(val) or not (is_array_like(val) or is_scalar(val)):
|
|
71
80
|
continue
|
|
72
81
|
|
|
73
82
|
if not is_tensor(val):
|
|
74
|
-
|
|
83
|
+
try:
|
|
84
|
+
val = torch.as_tensor(val)
|
|
85
|
+
except (TypeError, ValueError):
|
|
86
|
+
continue
|
|
75
87
|
out[key] = val
|
|
76
88
|
|
|
77
89
|
val_float = val.float() if not val.is_floating_point() else val
|
|
@@ -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',
|
|
@@ -57,6 +57,8 @@ def action_bounds(env):
|
|
|
57
57
|
# wrapper
|
|
58
58
|
|
|
59
59
|
class ActionTransformWrapper(EnvWrapper):
|
|
60
|
+
priority = 30
|
|
61
|
+
|
|
60
62
|
def __init__(
|
|
61
63
|
self,
|
|
62
64
|
env,
|
|
@@ -84,7 +86,7 @@ class ActionTransformWrapper(EnvWrapper):
|
|
|
84
86
|
# rescale canonical range (default (0, 1), beta-friendly) to per-env
|
|
85
87
|
# bounds, leaving unbounded dimensions untouched
|
|
86
88
|
|
|
87
|
-
if self.bounds
|
|
89
|
+
if not exists(self.bounds):
|
|
88
90
|
return t
|
|
89
91
|
|
|
90
92
|
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.4}/env_ssl_wrapper/standardize/auto_batched_wrapper.py
RENAMED
|
@@ -4,7 +4,7 @@ from functools import partial
|
|
|
4
4
|
|
|
5
5
|
import numpy as np
|
|
6
6
|
import torch
|
|
7
|
-
from torch.utils._pytree import tree_map
|
|
7
|
+
from torch.utils._pytree import tree_flatten, tree_map, tree_structure, tree_unflatten
|
|
8
8
|
|
|
9
9
|
from .helpers import (
|
|
10
10
|
FINAL_OBSERVATION_KEYS,
|
|
@@ -35,16 +35,11 @@ def to_numeric_array(t):
|
|
|
35
35
|
return arr if arr.dtype.kind in 'biufc' else t
|
|
36
36
|
|
|
37
37
|
def maybe_expand_dim(x):
|
|
38
|
-
# add leading batch dim to every numeric leaf
|
|
39
|
-
|
|
40
38
|
def _expand(t):
|
|
41
39
|
arr = to_numeric_array(t)
|
|
42
40
|
return arr[None] if is_array(arr) else t
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
return x[None]
|
|
46
|
-
|
|
47
|
-
return tree_map(_expand, x) if isinstance(x, (dict, list, tuple)) else _expand(x)
|
|
42
|
+
return tree_map(_expand, x)
|
|
48
43
|
|
|
49
44
|
def is_integer_dtype(t):
|
|
50
45
|
if is_tensor(t):
|
|
@@ -65,17 +60,18 @@ def get_action_space(env):
|
|
|
65
60
|
return get_adapter(env).action_space, False
|
|
66
61
|
|
|
67
62
|
def action_shape_tree(space):
|
|
68
|
-
# canonical shapes parallel to action structure
|
|
63
|
+
# canonical shapes parallel to action structure — raw dict / list / tuple
|
|
64
|
+
# action spaces are handled as well as gymnasium's composite spaces
|
|
69
65
|
|
|
70
66
|
if not exists(space):
|
|
71
67
|
return None
|
|
72
68
|
|
|
73
|
-
subspaces = get_attr(space, 'spaces')
|
|
69
|
+
subspaces = space if isinstance(space, (dict, list, tuple)) else get_attr(space, 'spaces')
|
|
74
70
|
|
|
75
|
-
if
|
|
76
|
-
|
|
77
|
-
return {key: action_shape_tree(subspace) for key, subspace in subspaces.items()}
|
|
71
|
+
if isinstance(subspaces, dict):
|
|
72
|
+
return {key: action_shape_tree(subspace) for key, subspace in subspaces.items()}
|
|
78
73
|
|
|
74
|
+
if exists(subspaces):
|
|
79
75
|
return [action_shape_tree(subspace) for subspace in subspaces]
|
|
80
76
|
|
|
81
77
|
return get_attr(space, 'shape')
|
|
@@ -121,20 +117,12 @@ def heuristic_leaf(t, is_vector = False):
|
|
|
121
117
|
return arr.item() if arr.ndim == 0 else arr
|
|
122
118
|
|
|
123
119
|
def rebuild_container(x, leaves):
|
|
124
|
-
|
|
125
|
-
if isinstance(x, list):
|
|
126
|
-
return leaves
|
|
127
|
-
|
|
128
|
-
if exists(get_attr(type(x), '_fields')):
|
|
129
|
-
return type(x)(*leaves)
|
|
130
|
-
|
|
131
|
-
return type(x)(leaves)
|
|
120
|
+
return tree_unflatten(leaves, tree_structure(x))
|
|
132
121
|
|
|
133
122
|
def is_numeric_container(x):
|
|
134
|
-
# purely numeric nested sequences count as one leaf when no space declares structure
|
|
135
|
-
|
|
136
123
|
if isinstance(x, (list, tuple)):
|
|
137
|
-
|
|
124
|
+
leaves, _ = tree_flatten(x)
|
|
125
|
+
return len(leaves) > 0 and all(map(is_scalar, leaves))
|
|
138
126
|
|
|
139
127
|
return is_scalar(x)
|
|
140
128
|
|
|
@@ -167,6 +155,7 @@ def maybe_squeeze_dim(x, shape_tree = None, is_vector = False, prepend_batch = F
|
|
|
167
155
|
class AutoBatchedWrapper(EnvWrapper):
|
|
168
156
|
|
|
169
157
|
is_auto_batched = True
|
|
158
|
+
priority = 50
|
|
170
159
|
|
|
171
160
|
def __init__(self, env, is_vector: bool | None = None):
|
|
172
161
|
super().__init__(env)
|
|
@@ -186,7 +175,16 @@ class AutoBatchedWrapper(EnvWrapper):
|
|
|
186
175
|
if not exists(self.action_shape_tree):
|
|
187
176
|
self.refresh_action_space()
|
|
188
177
|
|
|
189
|
-
|
|
178
|
+
if self.is_vector:
|
|
179
|
+
return obs, info
|
|
180
|
+
|
|
181
|
+
obs = maybe_expand_dim(obs)
|
|
182
|
+
if isinstance(info, dict):
|
|
183
|
+
for key in FINAL_OBSERVATION_KEYS:
|
|
184
|
+
if key in info:
|
|
185
|
+
info[key] = maybe_expand_dim(info[key])
|
|
186
|
+
|
|
187
|
+
return obs, info
|
|
190
188
|
|
|
191
189
|
def step(self, action):
|
|
192
190
|
action = maybe_squeeze_dim(action, shape_tree = self.action_shape_tree, is_vector = self.is_vector, prepend_batch = self.prepend_batch)
|
|
@@ -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,12 +60,14 @@ 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
|
|
66
67
|
|
|
67
68
|
class EpisodePaddingWrapper(EnvWrapper):
|
|
69
|
+
priority = 10
|
|
70
|
+
|
|
68
71
|
def __init__(self, env, pad_autoreset: bool = True):
|
|
69
72
|
super().__init__(env)
|
|
70
73
|
self.is_vector = is_vectorized(env)
|
|
@@ -90,7 +93,7 @@ class EpisodePaddingWrapper(EnvWrapper):
|
|
|
90
93
|
dones = dones_of(terminated, truncated)
|
|
91
94
|
mask = to_numpy(dones).astype(bool)
|
|
92
95
|
|
|
93
|
-
if self._is_done
|
|
96
|
+
if not exists(self._is_done) or len(self._is_done) != len(mask):
|
|
94
97
|
self._is_done = np.zeros(len(mask), dtype = bool)
|
|
95
98
|
|
|
96
99
|
# autoreset envs revive every done slot, so each done is a new
|
|
@@ -106,7 +109,7 @@ class EpisodePaddingWrapper(EnvWrapper):
|
|
|
106
109
|
final_val = first_existing(info, 'final_observation', 'final_obs')
|
|
107
110
|
value = final_val if exists(final_val) else self._last_obs
|
|
108
111
|
|
|
109
|
-
if self._final_obs
|
|
112
|
+
if not exists(self._final_obs):
|
|
110
113
|
self._final_obs = tree_map(copy_leaf, value)
|
|
111
114
|
else:
|
|
112
115
|
self._final_obs = tree_map(partial(merge_final, mask = newly), self._final_obs, value)
|
{env_ssl_wrapper-0.5.2 → env_ssl_wrapper-0.5.4}/env_ssl_wrapper/standardize/flatten_obs_wrapper.py
RENAMED
|
@@ -37,7 +37,9 @@ def concat_leaves(leaves, is_vector = False):
|
|
|
37
37
|
axis = -1 if is_vector else 0
|
|
38
38
|
leaves = [flatten_leaf(t, is_vector = is_vector) for t in leaves]
|
|
39
39
|
|
|
40
|
-
if is_tensor
|
|
40
|
+
if any(map(is_tensor, leaves)):
|
|
41
|
+
device = next(t.device for t in leaves if is_tensor(t))
|
|
42
|
+
leaves = [torch.as_tensor(t, device = device) if not is_tensor(t) else t.to(device) for t in leaves]
|
|
41
43
|
return torch.cat(leaves, dim = axis)
|
|
42
44
|
|
|
43
45
|
return np.concatenate(leaves, axis = axis)
|
|
@@ -45,6 +47,8 @@ def concat_leaves(leaves, is_vector = False):
|
|
|
45
47
|
# class
|
|
46
48
|
|
|
47
49
|
class FlattenObsWrapper(TransformObservationWrapper):
|
|
50
|
+
priority = 90
|
|
51
|
+
|
|
48
52
|
def __init__(self, env, is_vector: bool | None = None):
|
|
49
53
|
super().__init__(env)
|
|
50
54
|
self.is_vector = default(is_vector, is_vectorized(env))
|