env-ssl-wrapper 0.0.3__tar.gz → 0.0.5__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: env-ssl-wrapper
3
- Version: 0.0.3
3
+ Version: 0.0.5
4
4
  Summary: An RL environment wrapper for learning SSL in the background
5
5
  Project-URL: Homepage, https://pypi.org/project/env-ssl-wrapper/
6
6
  Project-URL: Repository, https://codeberg.org/lucidrains/env-ssl-wrapper
@@ -1,5 +1,6 @@
1
1
  from .image_wrapper import ImageObservationWrapper
2
2
  from .auto_batched_wrapper import AutoBatchedWrapper
3
3
  from .tensor_wrapper import TensorWrapper
4
+ from .action_transform_wrapper import ActionTransformWrapper
4
5
 
5
6
  from .utils import wrap_env, compose_env
@@ -0,0 +1,99 @@
1
+ from __future__ import annotations
2
+
3
+ import numpy as np
4
+ import torch
5
+ from torch import is_tensor
6
+ from torch.utils._pytree import tree_map
7
+ from functools import partial
8
+
9
+ # helpers
10
+
11
+ def exists(v):
12
+ return v is not None
13
+
14
+ def default(v, d):
15
+ return v if exists(v) else d
16
+
17
+ def is_float_dtype(t):
18
+ if is_tensor(t):
19
+ return t.is_floating_point()
20
+ if isinstance(t, np.ndarray):
21
+ return np.issubdtype(t.dtype, np.floating)
22
+ return isinstance(t, float)
23
+
24
+ def copy(t):
25
+ if is_tensor(t):
26
+ return t.clone()
27
+ if isinstance(t, np.ndarray):
28
+ return np.copy(t)
29
+ return t
30
+
31
+ def clamp(t, min_val, max_val):
32
+ if is_tensor(t):
33
+ return torch.clamp(t, min_val, max_val)
34
+ if isinstance(t, np.ndarray):
35
+ return np.clip(t, min_val, max_val)
36
+ return max(min_val, min(t, max_val))
37
+
38
+ def rescale(
39
+ t,
40
+ from_range: tuple[float, float],
41
+ to_range: tuple[float, float]
42
+ ):
43
+ from_min, from_max = from_range
44
+ to_min, to_max = to_range
45
+ return (t - from_min) / (from_max - from_min) * (to_max - to_min) + to_min
46
+
47
+ # wrapper
48
+
49
+ class ActionTransformWrapper:
50
+ def __init__(
51
+ self,
52
+ env,
53
+ transforms = None,
54
+ clip = None
55
+ ):
56
+ self.env = env
57
+ self.clip = clip
58
+
59
+ if isinstance(transforms, dict):
60
+ transforms = [transforms]
61
+
62
+ self.transforms = default(transforms, [])
63
+
64
+ def __getattr__(self, name):
65
+ if name.startswith('_'):
66
+ raise AttributeError(f"attempted to get missing private attribute '{name}'")
67
+ return getattr(self.env, name)
68
+
69
+ def reset(self, **kwargs):
70
+ return self.env.reset(**kwargs)
71
+
72
+ def step(self, action):
73
+ def transform_action(t):
74
+ if not is_float_dtype(t):
75
+ return t
76
+
77
+ t = copy(t)
78
+
79
+ for ind, transform in enumerate(self.transforms):
80
+ indices = transform.get('indices', ind if len(self.transforms) > 1 else None)
81
+
82
+ if 'rescale_from_to' not in transform:
83
+ continue
84
+
85
+ from_range, to_range = transform['rescale_from_to']
86
+ fn = partial(rescale, from_range = from_range, to_range = to_range)
87
+
88
+ if exists(indices):
89
+ t[..., indices] = fn(t[..., indices])
90
+ else:
91
+ t = fn(t)
92
+
93
+ if exists(self.clip):
94
+ t = clamp(t, *self.clip)
95
+
96
+ return t
97
+
98
+ transformed_action = tree_map(transform_action, action)
99
+ return self.env.step(transformed_action)
@@ -51,8 +51,8 @@ class AutoBatchedWrapper:
51
51
  def step(self, action):
52
52
  action = maybe_squeeze_dim(action) if not self.is_vector else action
53
53
  out = self.env.step(action)
54
-
54
+
55
55
  if self.is_vector:
56
56
  return out
57
-
57
+
58
58
  return *maybe_expand_dim(out[:4]), out[4]
@@ -67,7 +67,7 @@ class TensorWrapper:
67
67
  def step(self, action):
68
68
  action = torch_to_numpy(action, self.cast_float64_to_float32) if self.convert_in else action
69
69
  out = self.env.step(action)
70
-
70
+
71
71
  if not self.convert_out:
72
72
  return out
73
73
 
@@ -0,0 +1,45 @@
1
+ from __future__ import annotations
2
+ from functools import partial
3
+
4
+ from .image_wrapper import ImageObservationWrapper
5
+ from .auto_batched_wrapper import AutoBatchedWrapper
6
+ from .tensor_wrapper import TensorWrapper
7
+ from .action_transform_wrapper import ActionTransformWrapper
8
+
9
+ WRAPPERS = dict(
10
+ image = ImageObservationWrapper,
11
+ auto_batch = AutoBatchedWrapper,
12
+ tensor = TensorWrapper,
13
+ action_transform = ActionTransformWrapper
14
+ )
15
+
16
+ def is_unique(arr):
17
+ return len(set(arr)) == len(arr)
18
+
19
+ def compose_env(env, *wrappers):
20
+ funcs = []
21
+ classes = []
22
+
23
+ for wrapper in wrappers:
24
+ if isinstance(wrapper, str):
25
+ wrapper = WRAPPERS[wrapper]
26
+
27
+ if isinstance(wrapper, tuple):
28
+ name, kwargs = wrapper
29
+ wrapper = partial(WRAPPERS.get(name, name), **kwargs)
30
+
31
+ cls = wrapper.func if isinstance(wrapper, partial) else wrapper
32
+
33
+ funcs.append(wrapper)
34
+ classes.append(cls)
35
+
36
+ assert is_unique(classes), 'duplicate wrappers found'
37
+
38
+ for func in funcs:
39
+ env = func(env)
40
+
41
+ return env
42
+
43
+ # alias
44
+
45
+ wrap_env = compose_env
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "env-ssl-wrapper"
3
- version = "0.0.3"
3
+ version = "0.0.5"
4
4
  description = "An RL environment wrapper for learning SSL in the background"
5
5
  authors = [
6
6
  { name = "Phil Wang", email = "lucidrains@gmail.com" }
@@ -1,30 +0,0 @@
1
- from __future__ import annotations
2
- from functools import partial
3
-
4
- from .image_wrapper import ImageObservationWrapper
5
- from .auto_batched_wrapper import AutoBatchedWrapper
6
- from .tensor_wrapper import TensorWrapper
7
-
8
- WRAPPERS = dict(
9
- image = ImageObservationWrapper,
10
- auto_batch = AutoBatchedWrapper,
11
- tensor = TensorWrapper
12
- )
13
-
14
- def compose_env(env, *wrappers):
15
- for wrapper in wrappers:
16
- if isinstance(wrapper, str):
17
- wrapper = WRAPPERS[wrapper]
18
-
19
- if isinstance(wrapper, tuple):
20
- name_or_fn, kwargs = wrapper
21
- fn = WRAPPERS.get(name_or_fn, name_or_fn)
22
- wrapper = partial(fn, **kwargs)
23
-
24
- env = wrapper(env)
25
-
26
- return env
27
-
28
- # alias
29
-
30
- wrap_env = compose_env
File without changes