ducido commited on
Commit
987ed1b
·
verified ·
1 Parent(s): 6adcea0

Initial commit

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +16 -0
  2. codecs/imagecodecs_numcodecs.py +1386 -0
  3. common/checkpoint_util.py +59 -0
  4. common/compute_all_loss.py +483 -0
  5. common/cv2_util.py +150 -0
  6. common/env_util.py +23 -0
  7. common/json_logger.py +117 -0
  8. common/nested_dict_util.py +32 -0
  9. common/normalize_util.py +223 -0
  10. common/pose_trajectory_interpolator.py +208 -0
  11. common/precise_sleep.py +25 -0
  12. common/pref_replay_buffer.py +365 -0
  13. common/pref_sampler.py +74 -0
  14. common/prior_utils_confidence.py +723 -0
  15. common/pymunk_override.py +248 -0
  16. common/pymunk_util.py +52 -0
  17. common/pytorch_util.py +82 -0
  18. common/replay_buffer.py +615 -0
  19. common/robomimic_config_util.py +47 -0
  20. common/robomimic_util.py +177 -0
  21. common/sampler.py +154 -0
  22. common/timestamp_accumulator.py +222 -0
  23. config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_can_mh_abs.yaml +29 -0
  24. config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_kitchen_abs.yaml +29 -0
  25. config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_lift_mh_abs.yaml +29 -0
  26. config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_pusht.yaml +29 -0
  27. config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_square_mh_abs.yaml +29 -0
  28. config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_transport_mh_abs.yaml +29 -0
  29. config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_can_mh_abs.yaml +29 -0
  30. config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_kitchen_abs.yaml +29 -0
  31. config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_lift_mh_abs.yaml +29 -0
  32. config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_pusht.yaml +29 -0
  33. config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_square_mh_abs.yaml +29 -0
  34. config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_tool_hang_ph_abs.yaml +29 -0
  35. config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_transport_mh_abs.yaml +29 -0
  36. config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_can_mh_abs.yaml +163 -0
  37. config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_lift_mh_abs.yaml +163 -0
  38. config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_square_mh_abs.yaml +163 -0
  39. config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_tool_hang_ph_abs.yaml +163 -0
  40. config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_transport_mh_abs.yaml +163 -0
  41. config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_can_mh_abs.yaml +145 -0
  42. config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_lift_mh_abs.yaml +145 -0
  43. config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_square_mh_abs.yaml +145 -0
  44. config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_tool_hang_ph_abs.yaml +145 -0
  45. config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_transport_mh_abs.yaml +145 -0
  46. config/task/aloha.yaml +58 -0
  47. config/task/blockpush_lowdim_seed.yaml +34 -0
  48. config/task/blockpush_lowdim_seed_abs.yaml +34 -0
  49. config/task/can_image.yaml +64 -0
  50. config/task/can_image_abs.yaml +64 -0
.gitattributes CHANGED
@@ -33,3 +33,19 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ env/kitchen/relay_policy_learning/adept_models/kitchen/textures/marble1.png filter=lfs diff=lfs merge=lfs -text
37
+ env/kitchen/relay_policy_learning/adept_models/kitchen/textures/metal1.png filter=lfs diff=lfs merge=lfs -text
38
+ env/kitchen/relay_policy_learning/adept_models/kitchen/textures/tile1.png filter=lfs diff=lfs merge=lfs -text
39
+ env/kitchen/relay_policy_learning/adept_models/kitchen/textures/wood1.png filter=lfs diff=lfs merge=lfs -text
40
+ env/kitchen/relay_policy_learning/adept_models/scenes/textures/white_marble_tile.png filter=lfs diff=lfs merge=lfs -text
41
+ env/kitchen/relay_policy_learning/adept_models/scenes/textures/white_marble_tile2.png filter=lfs diff=lfs merge=lfs -text
42
+ env/kitchen/relay_policy_learning/third_party/franka/franka_panda.png filter=lfs diff=lfs merge=lfs -text
43
+ env/kitchen/relay_policy_learning/third_party/franka/meshes/visual/hand.stl filter=lfs diff=lfs merge=lfs -text
44
+ env/kitchen/relay_policy_learning/third_party/franka/meshes/visual/link0.stl filter=lfs diff=lfs merge=lfs -text
45
+ env/kitchen/relay_policy_learning/third_party/franka/meshes/visual/link1.stl filter=lfs diff=lfs merge=lfs -text
46
+ env/kitchen/relay_policy_learning/third_party/franka/meshes/visual/link2.stl filter=lfs diff=lfs merge=lfs -text
47
+ env/kitchen/relay_policy_learning/third_party/franka/meshes/visual/link3.stl filter=lfs diff=lfs merge=lfs -text
48
+ env/kitchen/relay_policy_learning/third_party/franka/meshes/visual/link4.stl filter=lfs diff=lfs merge=lfs -text
49
+ env/kitchen/relay_policy_learning/third_party/franka/meshes/visual/link5.stl filter=lfs diff=lfs merge=lfs -text
50
+ env/kitchen/relay_policy_learning/third_party/franka/meshes/visual/link6.stl filter=lfs diff=lfs merge=lfs -text
51
+ env/kitchen/relay_policy_learning/third_party/franka/meshes/visual/link7.stl filter=lfs diff=lfs merge=lfs -text
codecs/imagecodecs_numcodecs.py ADDED
@@ -0,0 +1,1386 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # imagecodecs/numcodecs.py
3
+
4
+ # Copyright (c) 2021-2022, Christoph Gohlke
5
+ # All rights reserved.
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions are met:
9
+ #
10
+ # 1. Redistributions of source code must retain the above copyright notice,
11
+ # this list of conditions and the following disclaimer.
12
+ #
13
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
14
+ # this list of conditions and the following disclaimer in the documentation
15
+ # and/or other materials provided with the distribution.
16
+ #
17
+ # 3. Neither the name of the copyright holder nor the names of its
18
+ # contributors may be used to endorse or promote products derived from
19
+ # this software without specific prior written permission.
20
+ #
21
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31
+ # POSSIBILITY OF SUCH DAMAGE.
32
+
33
+ """Additional numcodecs implemented using imagecodecs."""
34
+
35
+ __version__ = '2022.9.26'
36
+
37
+ __all__ = ('register_codecs',)
38
+
39
+ import numpy
40
+ from numcodecs.abc import Codec
41
+ from numcodecs.registry import register_codec, get_codec
42
+
43
+ import imagecodecs
44
+
45
+
46
+ def protective_squeeze(x: numpy.ndarray):
47
+ """
48
+ Squeeze dim only if it's not the last dim.
49
+ Image dim expected to be *, H, W, C
50
+ """
51
+ img_shape = x.shape[-3:]
52
+ if len(x.shape) > 3:
53
+ n_imgs = numpy.prod(x.shape[:-3])
54
+ if n_imgs > 1:
55
+ img_shape = (-1,) + img_shape
56
+ return x.reshape(img_shape)
57
+
58
+ def get_default_image_compressor(**kwargs):
59
+ if imagecodecs.JPEGXL:
60
+ # has JPEGXL
61
+ this_kwargs = {
62
+ 'effort': 3,
63
+ 'distance': 0.3,
64
+ # bug in libjxl, invalid codestream for non-lossless
65
+ # when decoding speed > 1
66
+ 'decodingspeed': 1
67
+ }
68
+ this_kwargs.update(kwargs)
69
+ return JpegXl(**this_kwargs)
70
+ else:
71
+ this_kwargs = {
72
+ 'level': 50
73
+ }
74
+ this_kwargs.update(kwargs)
75
+ return Jpeg2k(**this_kwargs)
76
+
77
+ class Aec(Codec):
78
+ """AEC codec for numcodecs."""
79
+
80
+ codec_id = 'imagecodecs_aec'
81
+
82
+ def __init__(
83
+ self, bitspersample=None, flags=None, blocksize=None, rsi=None
84
+ ):
85
+ self.bitspersample = bitspersample
86
+ self.flags = flags
87
+ self.blocksize = blocksize
88
+ self.rsi = rsi
89
+
90
+ def encode(self, buf):
91
+ return imagecodecs.aec_encode(
92
+ buf,
93
+ bitspersample=self.bitspersample,
94
+ flags=self.flags,
95
+ blocksize=self.blocksize,
96
+ rsi=self.rsi,
97
+ )
98
+
99
+ def decode(self, buf, out=None):
100
+ return imagecodecs.aec_decode(
101
+ buf,
102
+ bitspersample=self.bitspersample,
103
+ flags=self.flags,
104
+ blocksize=self.blocksize,
105
+ rsi=self.rsi,
106
+ out=_flat(out),
107
+ )
108
+
109
+
110
+ class Apng(Codec):
111
+ """APNG codec for numcodecs."""
112
+
113
+ codec_id = 'imagecodecs_apng'
114
+
115
+ def __init__(self, level=None, photometric=None, delay=None):
116
+ self.level = level
117
+ self.photometric = photometric
118
+ self.delay = delay
119
+
120
+ def encode(self, buf):
121
+ buf = protective_squeeze(numpy.asarray(buf))
122
+ return imagecodecs.apng_encode(
123
+ buf,
124
+ level=self.level,
125
+ photometric=self.photometric,
126
+ delay=self.delay,
127
+ )
128
+
129
+ def decode(self, buf, out=None):
130
+ return imagecodecs.apng_decode(buf, out=out)
131
+
132
+
133
+ class Avif(Codec):
134
+ """AVIF codec for numcodecs."""
135
+
136
+ codec_id = 'imagecodecs_avif'
137
+
138
+ def __init__(
139
+ self,
140
+ level=None,
141
+ speed=None,
142
+ tilelog2=None,
143
+ bitspersample=None,
144
+ pixelformat=None,
145
+ numthreads=None,
146
+ index=None,
147
+ ):
148
+ self.level = level
149
+ self.speed = speed
150
+ self.tilelog2 = tilelog2
151
+ self.bitspersample = bitspersample
152
+ self.pixelformat = pixelformat
153
+ self.numthreads = numthreads
154
+ self.index = index
155
+
156
+ def encode(self, buf):
157
+ buf = protective_squeeze(numpy.asarray(buf))
158
+ return imagecodecs.avif_encode(
159
+ buf,
160
+ level=self.level,
161
+ speed=self.speed,
162
+ tilelog2=self.tilelog2,
163
+ bitspersample=self.bitspersample,
164
+ pixelformat=self.pixelformat,
165
+ numthreads=self.numthreads,
166
+ )
167
+
168
+ def decode(self, buf, out=None):
169
+ return imagecodecs.avif_decode(
170
+ buf, index=self.index, numthreads=self.numthreads, out=out
171
+ )
172
+
173
+
174
+ class Bitorder(Codec):
175
+ """Bitorder codec for numcodecs."""
176
+
177
+ codec_id = 'imagecodecs_bitorder'
178
+
179
+ def encode(self, buf):
180
+ return imagecodecs.bitorder_encode(buf)
181
+
182
+ def decode(self, buf, out=None):
183
+ return imagecodecs.bitorder_decode(buf, out=_flat(out))
184
+
185
+
186
+ class Bitshuffle(Codec):
187
+ """Bitshuffle codec for numcodecs."""
188
+
189
+ codec_id = 'imagecodecs_bitshuffle'
190
+
191
+ def __init__(self, itemsize=1, blocksize=0):
192
+ self.itemsize = itemsize
193
+ self.blocksize = blocksize
194
+
195
+ def encode(self, buf):
196
+ return imagecodecs.bitshuffle_encode(
197
+ buf, itemsize=self.itemsize, blocksize=self.blocksize
198
+ ).tobytes()
199
+
200
+ def decode(self, buf, out=None):
201
+ return imagecodecs.bitshuffle_decode(
202
+ buf,
203
+ itemsize=self.itemsize,
204
+ blocksize=self.blocksize,
205
+ out=_flat(out),
206
+ )
207
+
208
+
209
+ class Blosc(Codec):
210
+ """Blosc codec for numcodecs."""
211
+
212
+ codec_id = 'imagecodecs_blosc'
213
+
214
+ def __init__(
215
+ self,
216
+ level=None,
217
+ compressor=None,
218
+ typesize=None,
219
+ blocksize=None,
220
+ shuffle=None,
221
+ numthreads=None,
222
+ ):
223
+ self.level = level
224
+ self.compressor = compressor
225
+ self.typesize = typesize
226
+ self.blocksize = blocksize
227
+ self.shuffle = shuffle
228
+ self.numthreads = numthreads
229
+
230
+ def encode(self, buf):
231
+ buf = protective_squeeze(numpy.asarray(buf))
232
+ return imagecodecs.blosc_encode(
233
+ buf,
234
+ level=self.level,
235
+ compressor=self.compressor,
236
+ typesize=self.typesize,
237
+ blocksize=self.blocksize,
238
+ shuffle=self.shuffle,
239
+ numthreads=self.numthreads,
240
+ )
241
+
242
+ def decode(self, buf, out=None):
243
+ return imagecodecs.blosc_decode(
244
+ buf, numthreads=self.numthreads, out=_flat(out)
245
+ )
246
+
247
+
248
+ class Blosc2(Codec):
249
+ """Blosc2 codec for numcodecs."""
250
+
251
+ codec_id = 'imagecodecs_blosc2'
252
+
253
+ def __init__(
254
+ self,
255
+ level=None,
256
+ compressor=None,
257
+ typesize=None,
258
+ blocksize=None,
259
+ shuffle=None,
260
+ numthreads=None,
261
+ ):
262
+ self.level = level
263
+ self.compressor = compressor
264
+ self.typesize = typesize
265
+ self.blocksize = blocksize
266
+ self.shuffle = shuffle
267
+ self.numthreads = numthreads
268
+
269
+ def encode(self, buf):
270
+ buf = protective_squeeze(numpy.asarray(buf))
271
+ return imagecodecs.blosc2_encode(
272
+ buf,
273
+ level=self.level,
274
+ compressor=self.compressor,
275
+ typesize=self.typesize,
276
+ blocksize=self.blocksize,
277
+ shuffle=self.shuffle,
278
+ numthreads=self.numthreads,
279
+ )
280
+
281
+ def decode(self, buf, out=None):
282
+ return imagecodecs.blosc2_decode(
283
+ buf, numthreads=self.numthreads, out=_flat(out)
284
+ )
285
+
286
+
287
+ class Brotli(Codec):
288
+ """Brotli codec for numcodecs."""
289
+
290
+ codec_id = 'imagecodecs_brotli'
291
+
292
+ def __init__(self, level=None, mode=None, lgwin=None):
293
+ self.level = level
294
+ self.mode = mode
295
+ self.lgwin = lgwin
296
+
297
+ def encode(self, buf):
298
+ return imagecodecs.brotli_encode(
299
+ buf, level=self.level, mode=self.mode, lgwin=self.lgwin
300
+ )
301
+
302
+ def decode(self, buf, out=None):
303
+ return imagecodecs.brotli_decode(buf, out=_flat(out))
304
+
305
+
306
+ class ByteShuffle(Codec):
307
+ """ByteShuffle codec for numcodecs."""
308
+
309
+ codec_id = 'imagecodecs_byteshuffle'
310
+
311
+ def __init__(
312
+ self, shape, dtype, axis=-1, dist=1, delta=False, reorder=False
313
+ ):
314
+ self.shape = tuple(shape)
315
+ self.dtype = numpy.dtype(dtype).str
316
+ self.axis = axis
317
+ self.dist = dist
318
+ self.delta = bool(delta)
319
+ self.reorder = bool(reorder)
320
+
321
+ def encode(self, buf):
322
+ buf = protective_squeeze(numpy.asarray(buf))
323
+ assert buf.shape == self.shape
324
+ assert buf.dtype == self.dtype
325
+ return imagecodecs.byteshuffle_encode(
326
+ buf,
327
+ axis=self.axis,
328
+ dist=self.dist,
329
+ delta=self.delta,
330
+ reorder=self.reorder,
331
+ ).tobytes()
332
+
333
+ def decode(self, buf, out=None):
334
+ if not isinstance(buf, numpy.ndarray):
335
+ buf = numpy.frombuffer(buf, dtype=self.dtype).reshape(*self.shape)
336
+ return imagecodecs.byteshuffle_decode(
337
+ buf,
338
+ axis=self.axis,
339
+ dist=self.dist,
340
+ delta=self.delta,
341
+ reorder=self.reorder,
342
+ out=out,
343
+ )
344
+
345
+
346
+ class Bz2(Codec):
347
+ """Bz2 codec for numcodecs."""
348
+
349
+ codec_id = 'imagecodecs_bz2'
350
+
351
+ def __init__(self, level=None):
352
+ self.level = level
353
+
354
+ def encode(self, buf):
355
+ return imagecodecs.bz2_encode(buf, level=self.level)
356
+
357
+ def decode(self, buf, out=None):
358
+ return imagecodecs.bz2_decode(buf, out=_flat(out))
359
+
360
+
361
+ class Cms(Codec):
362
+ """CMS codec for numcodecs."""
363
+
364
+ codec_id = 'imagecodecs_cms'
365
+
366
+ def __init__(self, *args, **kwargs):
367
+ pass
368
+
369
+ def encode(self, buf, out=None):
370
+ # return imagecodecs.cms_transform(buf)
371
+ raise NotImplementedError
372
+
373
+ def decode(self, buf, out=None):
374
+ # return imagecodecs.cms_transform(buf)
375
+ raise NotImplementedError
376
+
377
+
378
+ class Deflate(Codec):
379
+ """Deflate codec for numcodecs."""
380
+
381
+ codec_id = 'imagecodecs_deflate'
382
+
383
+ def __init__(self, level=None, raw=False):
384
+ self.level = level
385
+ self.raw = bool(raw)
386
+
387
+ def encode(self, buf):
388
+ return imagecodecs.deflate_encode(buf, level=self.level, raw=self.raw)
389
+
390
+ def decode(self, buf, out=None):
391
+ return imagecodecs.deflate_decode(buf, out=_flat(out), raw=self.raw)
392
+
393
+
394
+ class Delta(Codec):
395
+ """Delta codec for numcodecs."""
396
+
397
+ codec_id = 'imagecodecs_delta'
398
+
399
+ def __init__(self, shape=None, dtype=None, axis=-1, dist=1):
400
+ self.shape = None if shape is None else tuple(shape)
401
+ self.dtype = None if dtype is None else numpy.dtype(dtype).str
402
+ self.axis = axis
403
+ self.dist = dist
404
+
405
+ def encode(self, buf):
406
+ if self.shape is not None or self.dtype is not None:
407
+ buf = protective_squeeze(numpy.asarray(buf))
408
+ assert buf.shape == self.shape
409
+ assert buf.dtype == self.dtype
410
+ return imagecodecs.delta_encode(
411
+ buf, axis=self.axis, dist=self.dist
412
+ ).tobytes()
413
+
414
+ def decode(self, buf, out=None):
415
+ if self.shape is not None or self.dtype is not None:
416
+ buf = numpy.frombuffer(buf, dtype=self.dtype).reshape(*self.shape)
417
+ return imagecodecs.delta_decode(
418
+ buf, axis=self.axis, dist=self.dist, out=out
419
+ )
420
+
421
+
422
+ class Float24(Codec):
423
+ """Float24 codec for numcodecs."""
424
+
425
+ codec_id = 'imagecodecs_float24'
426
+
427
+ def __init__(self, byteorder=None, rounding=None):
428
+ self.byteorder = byteorder
429
+ self.rounding = rounding
430
+
431
+ def encode(self, buf):
432
+ buf = protective_squeeze(numpy.asarray(buf))
433
+ return imagecodecs.float24_encode(
434
+ buf, byteorder=self.byteorder, rounding=self.rounding
435
+ )
436
+
437
+ def decode(self, buf, out=None):
438
+ return imagecodecs.float24_decode(
439
+ buf, byteorder=self.byteorder, out=out
440
+ )
441
+
442
+
443
+ class FloatPred(Codec):
444
+ """Floating Point Predictor codec for numcodecs."""
445
+
446
+ codec_id = 'imagecodecs_floatpred'
447
+
448
+ def __init__(self, shape, dtype, axis=-1, dist=1):
449
+ self.shape = tuple(shape)
450
+ self.dtype = numpy.dtype(dtype).str
451
+ self.axis = axis
452
+ self.dist = dist
453
+
454
+ def encode(self, buf):
455
+ buf = protective_squeeze(numpy.asarray(buf))
456
+ assert buf.shape == self.shape
457
+ assert buf.dtype == self.dtype
458
+ return imagecodecs.floatpred_encode(
459
+ buf, axis=self.axis, dist=self.dist
460
+ ).tobytes()
461
+
462
+ def decode(self, buf, out=None):
463
+ if not isinstance(buf, numpy.ndarray):
464
+ buf = numpy.frombuffer(buf, dtype=self.dtype).reshape(*self.shape)
465
+ return imagecodecs.floatpred_decode(
466
+ buf, axis=self.axis, dist=self.dist, out=out
467
+ )
468
+
469
+
470
+ class Gif(Codec):
471
+ """GIF codec for numcodecs."""
472
+
473
+ codec_id = 'imagecodecs_gif'
474
+
475
+ def encode(self, buf):
476
+ buf = protective_squeeze(numpy.asarray(buf))
477
+ return imagecodecs.gif_encode(buf)
478
+
479
+ def decode(self, buf, out=None):
480
+ return imagecodecs.gif_decode(buf, asrgb=False, out=out)
481
+
482
+
483
+ class Heif(Codec):
484
+ """HEIF codec for numcodecs."""
485
+
486
+ codec_id = 'imagecodecs_heif'
487
+
488
+ def __init__(
489
+ self,
490
+ level=None,
491
+ bitspersample=None,
492
+ photometric=None,
493
+ compression=None,
494
+ numthreads=None,
495
+ index=None,
496
+ ):
497
+ self.level = level
498
+ self.bitspersample = bitspersample
499
+ self.photometric = photometric
500
+ self.compression = compression
501
+ self.numthreads = numthreads
502
+ self.index = index
503
+
504
+ def encode(self, buf):
505
+ buf = protective_squeeze(numpy.asarray(buf))
506
+ return imagecodecs.heif_encode(
507
+ buf,
508
+ level=self.level,
509
+ bitspersample=self.bitspersample,
510
+ photometric=self.photometric,
511
+ compression=self.compression,
512
+ numthreads=self.numthreads,
513
+ )
514
+
515
+ def decode(self, buf, out=None):
516
+ return imagecodecs.heif_decode(
517
+ buf,
518
+ index=self.index,
519
+ photometric=self.photometric,
520
+ numthreads=self.numthreads,
521
+ out=out,
522
+ )
523
+
524
+
525
+ class Jetraw(Codec):
526
+ """Jetraw codec for numcodecs."""
527
+
528
+ codec_id = 'imagecodecs_jetraw'
529
+
530
+ def __init__(
531
+ self,
532
+ shape,
533
+ identifier,
534
+ parameters=None,
535
+ verbosity=None,
536
+ errorbound=None,
537
+ ):
538
+ self.shape = shape
539
+ self.identifier = identifier
540
+ self.errorbound = errorbound
541
+ imagecodecs.jetraw_init(parameters, verbosity)
542
+
543
+ def encode(self, buf):
544
+ return imagecodecs.jetraw_encode(
545
+ buf, identifier=self.identifier, errorbound=self.errorbound
546
+ )
547
+
548
+ def decode(self, buf, out=None):
549
+ if out is None:
550
+ out = numpy.empty(self.shape, numpy.uint16)
551
+ return imagecodecs.jetraw_decode(buf, out=out)
552
+
553
+
554
+ class Jpeg(Codec):
555
+ """JPEG codec for numcodecs."""
556
+
557
+ codec_id = 'imagecodecs_jpeg'
558
+
559
+ def __init__(
560
+ self,
561
+ bitspersample=None,
562
+ tables=None,
563
+ header=None,
564
+ colorspace_data=None,
565
+ colorspace_jpeg=None,
566
+ level=None,
567
+ subsampling=None,
568
+ optimize=None,
569
+ smoothing=None,
570
+ ):
571
+ self.tables = tables
572
+ self.header = header
573
+ self.bitspersample = bitspersample
574
+ self.colorspace_data = colorspace_data
575
+ self.colorspace_jpeg = colorspace_jpeg
576
+ self.level = level
577
+ self.subsampling = subsampling
578
+ self.optimize = optimize
579
+ self.smoothing = smoothing
580
+
581
+ def encode(self, buf):
582
+ buf = protective_squeeze(numpy.asarray(buf))
583
+ return imagecodecs.jpeg_encode(
584
+ buf,
585
+ level=self.level,
586
+ colorspace=self.colorspace_data,
587
+ outcolorspace=self.colorspace_jpeg,
588
+ subsampling=self.subsampling,
589
+ optimize=self.optimize,
590
+ smoothing=self.smoothing,
591
+ )
592
+
593
+ def decode(self, buf, out=None):
594
+ out_shape = None
595
+ if out is not None:
596
+ out_shape = out.shape
597
+ out = protective_squeeze(out)
598
+ img = imagecodecs.jpeg_decode(
599
+ buf,
600
+ bitspersample=self.bitspersample,
601
+ tables=self.tables,
602
+ header=self.header,
603
+ colorspace=self.colorspace_jpeg,
604
+ outcolorspace=self.colorspace_data,
605
+ out=out,
606
+ )
607
+ if out_shape is not None:
608
+ img = img.reshape(out_shape)
609
+ return img
610
+
611
+ def get_config(self):
612
+ """Return dictionary holding configuration parameters."""
613
+ config = dict(id=self.codec_id)
614
+ for key in self.__dict__:
615
+ if not key.startswith('_'):
616
+ value = getattr(self, key)
617
+ if value is not None and key in ('header', 'tables'):
618
+ import base64
619
+
620
+ value = base64.b64encode(value).decode()
621
+ config[key] = value
622
+ return config
623
+
624
+ @classmethod
625
+ def from_config(cls, config):
626
+ """Instantiate codec from configuration object."""
627
+ for key in ('header', 'tables'):
628
+ value = config.get(key, None)
629
+ if value is not None and isinstance(value, str):
630
+ import base64
631
+
632
+ config[key] = base64.b64decode(value.encode())
633
+ return cls(**config)
634
+
635
+
636
+ class Jpeg2k(Codec):
637
+ """JPEG 2000 codec for numcodecs."""
638
+
639
+ codec_id = 'imagecodecs_jpeg2k'
640
+
641
+ def __init__(
642
+ self,
643
+ level=None,
644
+ codecformat=None,
645
+ colorspace=None,
646
+ tile=None,
647
+ reversible=None,
648
+ bitspersample=None,
649
+ resolutions=None,
650
+ numthreads=None,
651
+ verbose=0,
652
+ ):
653
+ self.level = level
654
+ self.codecformat = codecformat
655
+ self.colorspace = colorspace
656
+ self.tile = None if tile is None else tuple(tile)
657
+ self.reversible = reversible
658
+ self.bitspersample = bitspersample
659
+ self.resolutions = resolutions
660
+ self.numthreads = numthreads
661
+ self.verbose = verbose
662
+
663
+ def encode(self, buf):
664
+ buf = protective_squeeze(numpy.asarray(buf))
665
+ return imagecodecs.jpeg2k_encode(
666
+ buf,
667
+ level=self.level,
668
+ codecformat=self.codecformat,
669
+ colorspace=self.colorspace,
670
+ tile=self.tile,
671
+ reversible=self.reversible,
672
+ bitspersample=self.bitspersample,
673
+ resolutions=self.resolutions,
674
+ numthreads=self.numthreads,
675
+ verbose=self.verbose,
676
+ )
677
+
678
+ def decode(self, buf, out=None):
679
+ return imagecodecs.jpeg2k_decode(
680
+ buf, verbose=self.verbose, numthreads=self.numthreads, out=out
681
+ )
682
+
683
+
684
+ class JpegLs(Codec):
685
+ """JPEG LS codec for numcodecs."""
686
+
687
+ codec_id = 'imagecodecs_jpegls'
688
+
689
+ def __init__(self, level=None):
690
+ self.level = level
691
+
692
+ def encode(self, buf):
693
+ buf = protective_squeeze(numpy.asarray(buf))
694
+ return imagecodecs.jpegls_encode(buf, level=self.level)
695
+
696
+ def decode(self, buf, out=None):
697
+ return imagecodecs.jpegls_decode(buf, out=out)
698
+
699
+
700
+ class JpegXl(Codec):
701
+ """JPEG XL codec for numcodecs."""
702
+
703
+ codec_id = 'imagecodecs_jpegxl'
704
+
705
+ def __init__(
706
+ self,
707
+ # encode
708
+ level=None,
709
+ effort=None,
710
+ distance=None,
711
+ lossless=None,
712
+ decodingspeed=None,
713
+ photometric=None,
714
+ planar=None,
715
+ usecontainer=None,
716
+ # decode
717
+ index=None,
718
+ keeporientation=None,
719
+ # both
720
+ numthreads=None,
721
+ ):
722
+ """
723
+ Return JPEG XL image from numpy array.
724
+ Float must be in nominal range 0..1.
725
+
726
+ Currently L, LA, RGB, RGBA images are supported in contig mode.
727
+ Extra channels are only supported for grayscale images in planar mode.
728
+
729
+ Parameters
730
+ ----------
731
+ level : Default to None, i.e. not overwriting lossess and decodingspeed options.
732
+ When < 0: Use lossless compression
733
+ When in [0,1,2,3,4]: Sets the decoding speed tier for the provided options.
734
+ Minimum is 0 (slowest to decode, best quality/density), and maximum
735
+ is 4 (fastest to decode, at the cost of some quality/density).
736
+ effort : Default to 3.
737
+ Sets encoder effort/speed level without affecting decoding speed.
738
+ Valid values are, from faster to slower speed: 1:lightning 2:thunder
739
+ 3:falcon 4:cheetah 5:hare 6:wombat 7:squirrel 8:kitten 9:tortoise.
740
+ Speed: lightning, thunder, falcon, cheetah, hare, wombat, squirrel, kitten, tortoise
741
+ control the encoder effort in ascending order.
742
+ This also affects memory usage: using lower effort will typically reduce memory
743
+ consumption during encoding.
744
+ lightning and thunder are fast modes useful for lossless mode (modular).
745
+ falcon disables all of the following tools.
746
+ cheetah enables coefficient reordering, context clustering, and heuristics for selecting DCT sizes and quantization steps.
747
+ hare enables Gaborish filtering, chroma from luma, and an initial estimate of quantization steps.
748
+ wombat enables error diffusion quantization and full DCT size selection heuristics.
749
+ squirrel (default) enables dots, patches, and spline detection, and full context clustering.
750
+ kitten optimizes the adaptive quantization for a psychovisual metric.
751
+ tortoise enables a more thorough adaptive quantization search.
752
+ distance : Default to 1.0
753
+ Sets the distance level for lossy compression: target max butteraugli distance,
754
+ lower = higher quality. Range: 0 .. 15. 0.0 = mathematically lossless
755
+ (however, use JxlEncoderSetFrameLossless instead to use true lossless,
756
+ as setting distance to 0 alone is not the only requirement).
757
+ 1.0 = visually lossless. Recommended range: 0.5 .. 3.0.
758
+ lossess : Default to False.
759
+ Use lossess encoding.
760
+ decodingspeed : Default to 0.
761
+ Duplicate to level. [0,4]
762
+ photometric : Return JxlColorSpace value.
763
+ Default logic is quite complicated but works most of the time.
764
+ Accepted value:
765
+ int: [-1,3]
766
+ str: ['RGB',
767
+ 'WHITEISZERO', 'MINISWHITE',
768
+ 'BLACKISZERO', 'MINISBLACK', 'GRAY',
769
+ 'XYB', 'KNOWN']
770
+ planar : Enable multi-channel mode.
771
+ Default to false.
772
+ usecontainer :
773
+ Forces the encoder to use the box-based container format (BMFF)
774
+ even when not necessary.
775
+ When using JxlEncoderUseBoxes, JxlEncoderStoreJPEGMetadata or
776
+ JxlEncoderSetCodestreamLevel with level 10, the encoder will
777
+ automatically also use the container format, it is not necessary
778
+ to use JxlEncoderUseContainer for those use cases.
779
+ By default this setting is disabled.
780
+ index : Selectively decode frames for animation.
781
+ Default to 0, decode all frames.
782
+ When set to > 0, decode that frame index only.
783
+ keeporientation :
784
+ Enables or disables preserving of as-in-bitstream pixeldata orientation.
785
+ Some images are encoded with an Orientation tag indicating that the
786
+ decoder must perform a rotation and/or mirroring to the encoded image data.
787
+
788
+ If skip_reorientation is JXL_FALSE (the default): the decoder will apply
789
+ the transformation from the orientation setting, hence rendering the image
790
+ according to its specified intent. When producing a JxlBasicInfo, the decoder
791
+ will always set the orientation field to JXL_ORIENT_IDENTITY (matching the
792
+ returned pixel data) and also align xsize and ysize so that they correspond
793
+ to the width and the height of the returned pixel data.
794
+
795
+ If skip_reorientation is JXL_TRUE: the decoder will skip applying the
796
+ transformation from the orientation setting, returning the image in
797
+ the as-in-bitstream pixeldata orientation. This may be faster to decode
798
+ since the decoder doesnt have to apply the transformation, but can
799
+ cause wrong display of the image if the orientation tag is not correctly
800
+ taken into account by the user.
801
+
802
+ By default, this option is disabled, and the returned pixel data is
803
+ re-oriented according to the images Orientation setting.
804
+ threads : Default to 1.
805
+ If <= 0, use all cores.
806
+ If > 32, clipped to 32.
807
+ """
808
+
809
+ self.level = level
810
+ self.effort = effort
811
+ self.distance = distance
812
+ self.lossless = bool(lossless)
813
+ self.decodingspeed = decodingspeed
814
+ self.photometric = photometric
815
+ self.planar = planar
816
+ self.usecontainer = usecontainer
817
+ self.index = index
818
+ self.keeporientation = keeporientation
819
+ self.numthreads = numthreads
820
+
821
+ def encode(self, buf):
822
+ # TODO: only squeeze all but last dim
823
+ buf = protective_squeeze(numpy.asarray(buf))
824
+ return imagecodecs.jpegxl_encode(
825
+ buf,
826
+ level=self.level,
827
+ effort=self.effort,
828
+ distance=self.distance,
829
+ lossless=self.lossless,
830
+ decodingspeed=self.decodingspeed,
831
+ photometric=self.photometric,
832
+ planar=self.planar,
833
+ usecontainer=self.usecontainer,
834
+ numthreads=self.numthreads,
835
+ )
836
+
837
+ def decode(self, buf, out=None):
838
+ return imagecodecs.jpegxl_decode(
839
+ buf,
840
+ index=self.index,
841
+ keeporientation=self.keeporientation,
842
+ numthreads=self.numthreads,
843
+ out=out,
844
+ )
845
+
846
+
847
+ class JpegXr(Codec):
848
+ """JPEG XR codec for numcodecs."""
849
+
850
+ codec_id = 'imagecodecs_jpegxr'
851
+
852
+ def __init__(
853
+ self,
854
+ level=None,
855
+ photometric=None,
856
+ hasalpha=None,
857
+ resolution=None,
858
+ fp2int=None,
859
+ ):
860
+ self.level = level
861
+ self.photometric = photometric
862
+ self.hasalpha = hasalpha
863
+ self.resolution = resolution
864
+ self.fp2int = fp2int
865
+
866
+ def encode(self, buf):
867
+ buf = protective_squeeze(numpy.asarray(buf))
868
+ return imagecodecs.jpegxr_encode(
869
+ buf,
870
+ level=self.level,
871
+ photometric=self.photometric,
872
+ hasalpha=self.hasalpha,
873
+ resolution=self.resolution,
874
+ )
875
+
876
+ def decode(self, buf, out=None):
877
+ return imagecodecs.jpegxr_decode(buf, fp2int=self.fp2int, out=out)
878
+
879
+
880
+ class Lerc(Codec):
881
+ """LERC codec for numcodecs."""
882
+
883
+ codec_id = 'imagecodecs_lerc'
884
+
885
+ def __init__(self, level=None, version=None, planar=None):
886
+ self.level = level
887
+ self.version = version
888
+ self.planar = bool(planar)
889
+ # TODO: support mask?
890
+ # self.mask = None
891
+
892
+ def encode(self, buf):
893
+ buf = protective_squeeze(numpy.asarray(buf))
894
+ return imagecodecs.lerc_encode(
895
+ buf,
896
+ level=self.level,
897
+ version=self.version,
898
+ planar=self.planar,
899
+ )
900
+
901
+ def decode(self, buf, out=None):
902
+ return imagecodecs.lerc_decode(buf, out=out)
903
+
904
+
905
+ class Ljpeg(Codec):
906
+ """LJPEG codec for numcodecs."""
907
+
908
+ codec_id = 'imagecodecs_ljpeg'
909
+
910
+ def __init__(self, bitspersample=None):
911
+ self.bitspersample = bitspersample
912
+
913
+ def encode(self, buf):
914
+ buf = protective_squeeze(numpy.asarray(buf))
915
+ return imagecodecs.ljpeg_encode(buf, bitspersample=self.bitspersample)
916
+
917
+ def decode(self, buf, out=None):
918
+ return imagecodecs.ljpeg_decode(buf, out=out)
919
+
920
+
921
+ class Lz4(Codec):
922
+ """LZ4 codec for numcodecs."""
923
+
924
+ codec_id = 'imagecodecs_lz4'
925
+
926
+ def __init__(self, level=None, hc=False, header=True):
927
+ self.level = level
928
+ self.hc = hc
929
+ self.header = bool(header)
930
+
931
+ def encode(self, buf):
932
+ return imagecodecs.lz4_encode(
933
+ buf, level=self.level, hc=self.hc, header=self.header
934
+ )
935
+
936
+ def decode(self, buf, out=None):
937
+ return imagecodecs.lz4_decode(buf, header=self.header, out=_flat(out))
938
+
939
+
940
+ class Lz4f(Codec):
941
+ """LZ4F codec for numcodecs."""
942
+
943
+ codec_id = 'imagecodecs_lz4f'
944
+
945
+ def __init__(
946
+ self,
947
+ level=None,
948
+ blocksizeid=False,
949
+ contentchecksum=None,
950
+ blockchecksum=None,
951
+ ):
952
+ self.level = level
953
+ self.blocksizeid = blocksizeid
954
+ self.contentchecksum = contentchecksum
955
+ self.blockchecksum = blockchecksum
956
+
957
+ def encode(self, buf):
958
+ return imagecodecs.lz4f_encode(
959
+ buf,
960
+ level=self.level,
961
+ blocksizeid=self.blocksizeid,
962
+ contentchecksum=self.contentchecksum,
963
+ blockchecksum=self.blockchecksum,
964
+ )
965
+
966
+ def decode(self, buf, out=None):
967
+ return imagecodecs.lz4f_decode(buf, out=_flat(out))
968
+
969
+
970
+ class Lzf(Codec):
971
+ """LZF codec for numcodecs."""
972
+
973
+ codec_id = 'imagecodecs_lzf'
974
+
975
+ def __init__(self, header=True):
976
+ self.header = bool(header)
977
+
978
+ def encode(self, buf):
979
+ return imagecodecs.lzf_encode(buf, header=self.header)
980
+
981
+ def decode(self, buf, out=None):
982
+ return imagecodecs.lzf_decode(buf, header=self.header, out=_flat(out))
983
+
984
+
985
+ class Lzma(Codec):
986
+ """LZMA codec for numcodecs."""
987
+
988
+ codec_id = 'imagecodecs_lzma'
989
+
990
+ def __init__(self, level=None):
991
+ self.level = level
992
+
993
+ def encode(self, buf):
994
+ return imagecodecs.lzma_encode(buf, level=self.level)
995
+
996
+ def decode(self, buf, out=None):
997
+ return imagecodecs.lzma_decode(buf, out=_flat(out))
998
+
999
+
1000
+ class Lzw(Codec):
1001
+ """LZW codec for numcodecs."""
1002
+
1003
+ codec_id = 'imagecodecs_lzw'
1004
+
1005
+ def encode(self, buf):
1006
+ return imagecodecs.lzw_encode(buf)
1007
+
1008
+ def decode(self, buf, out=None):
1009
+ return imagecodecs.lzw_decode(buf, out=_flat(out))
1010
+
1011
+
1012
+ class PackBits(Codec):
1013
+ """PackBits codec for numcodecs."""
1014
+
1015
+ codec_id = 'imagecodecs_packbits'
1016
+
1017
+ def __init__(self, axis=None):
1018
+ self.axis = axis
1019
+
1020
+ def encode(self, buf):
1021
+ if not isinstance(buf, (bytes, bytearray)):
1022
+ buf = protective_squeeze(numpy.asarray(buf))
1023
+ return imagecodecs.packbits_encode(buf, axis=self.axis)
1024
+
1025
+ def decode(self, buf, out=None):
1026
+ return imagecodecs.packbits_decode(buf, out=_flat(out))
1027
+
1028
+
1029
+ class Pglz(Codec):
1030
+ """PGLZ codec for numcodecs."""
1031
+
1032
+ codec_id = 'imagecodecs_pglz'
1033
+
1034
+ def __init__(self, header=True, strategy=None):
1035
+ self.header = bool(header)
1036
+ self.strategy = strategy
1037
+
1038
+ def encode(self, buf):
1039
+ return imagecodecs.pglz_encode(
1040
+ buf, strategy=self.strategy, header=self.header
1041
+ )
1042
+
1043
+ def decode(self, buf, out=None):
1044
+ return imagecodecs.pglz_decode(buf, header=self.header, out=_flat(out))
1045
+
1046
+
1047
+ class Png(Codec):
1048
+ """PNG codec for numcodecs."""
1049
+
1050
+ codec_id = 'imagecodecs_png'
1051
+
1052
+ def __init__(self, level=None):
1053
+ self.level = level
1054
+
1055
+ def encode(self, buf):
1056
+ buf = protective_squeeze(numpy.asarray(buf))
1057
+ return imagecodecs.png_encode(buf, level=self.level)
1058
+
1059
+ def decode(self, buf, out=None):
1060
+ return imagecodecs.png_decode(buf, out=out)
1061
+
1062
+
1063
+ class Qoi(Codec):
1064
+ """QOI codec for numcodecs."""
1065
+
1066
+ codec_id = 'imagecodecs_qoi'
1067
+
1068
+ def __init__(self):
1069
+ pass
1070
+
1071
+ def encode(self, buf):
1072
+ buf = protective_squeeze(numpy.asarray(buf))
1073
+ return imagecodecs.qoi_encode(buf)
1074
+
1075
+ def decode(self, buf, out=None):
1076
+ return imagecodecs.qoi_decode(buf, out=out)
1077
+
1078
+
1079
+ class Rgbe(Codec):
1080
+ """RGBE codec for numcodecs."""
1081
+
1082
+ codec_id = 'imagecodecs_rgbe'
1083
+
1084
+ def __init__(self, header=False, shape=None, rle=None):
1085
+ if not header and shape is None:
1086
+ raise ValueError('must specify data shape if no header')
1087
+ if shape and shape[-1] != 3:
1088
+ raise ValueError('invalid shape')
1089
+ self.shape = shape
1090
+ self.header = bool(header)
1091
+ self.rle = None if rle is None else bool(rle)
1092
+
1093
+ def encode(self, buf):
1094
+ buf = protective_squeeze(numpy.asarray(buf))
1095
+ return imagecodecs.rgbe_encode(buf, header=self.header, rle=self.rle)
1096
+
1097
+ def decode(self, buf, out=None):
1098
+ if out is None and not self.header:
1099
+ out = numpy.empty(self.shape, numpy.float32)
1100
+ return imagecodecs.rgbe_decode(
1101
+ buf, header=self.header, rle=self.rle, out=out
1102
+ )
1103
+
1104
+
1105
+ class Rcomp(Codec):
1106
+ """Rcomp codec for numcodecs."""
1107
+
1108
+ codec_id = 'imagecodecs_rcomp'
1109
+
1110
+ def __init__(self, shape, dtype, nblock=None):
1111
+ self.shape = tuple(shape)
1112
+ self.dtype = numpy.dtype(dtype).str
1113
+ self.nblock = nblock
1114
+
1115
+ def encode(self, buf):
1116
+ return imagecodecs.rcomp_encode(buf, nblock=self.nblock)
1117
+
1118
+ def decode(self, buf, out=None):
1119
+ return imagecodecs.rcomp_decode(
1120
+ buf,
1121
+ shape=self.shape,
1122
+ dtype=self.dtype,
1123
+ nblock=self.nblock,
1124
+ out=out,
1125
+ )
1126
+
1127
+
1128
+ class Snappy(Codec):
1129
+ """Snappy codec for numcodecs."""
1130
+
1131
+ codec_id = 'imagecodecs_snappy'
1132
+
1133
+ def encode(self, buf):
1134
+ return imagecodecs.snappy_encode(buf)
1135
+
1136
+ def decode(self, buf, out=None):
1137
+ return imagecodecs.snappy_decode(buf, out=_flat(out))
1138
+
1139
+
1140
+ class Spng(Codec):
1141
+ """SPNG codec for numcodecs."""
1142
+
1143
+ codec_id = 'imagecodecs_spng'
1144
+
1145
+ def __init__(self, level=None):
1146
+ self.level = level
1147
+
1148
+ def encode(self, buf):
1149
+ buf = protective_squeeze(numpy.asarray(buf))
1150
+ return imagecodecs.spng_encode(buf, level=self.level)
1151
+
1152
+ def decode(self, buf, out=None):
1153
+ return imagecodecs.spng_decode(buf, out=out)
1154
+
1155
+
1156
+ class Tiff(Codec):
1157
+ """TIFF codec for numcodecs."""
1158
+
1159
+ codec_id = 'imagecodecs_tiff'
1160
+
1161
+ def __init__(self, index=None, asrgb=None, verbose=0):
1162
+ self.index = index
1163
+ self.asrgb = bool(asrgb)
1164
+ self.verbose = verbose
1165
+
1166
+ def encode(self, buf):
1167
+ # TODO: not implemented
1168
+ buf = protective_squeeze(numpy.asarray(buf))
1169
+ return imagecodecs.tiff_encode(buf)
1170
+
1171
+ def decode(self, buf, out=None):
1172
+ return imagecodecs.tiff_decode(
1173
+ buf,
1174
+ index=self.index,
1175
+ asrgb=self.asrgb,
1176
+ verbose=self.verbose,
1177
+ out=out,
1178
+ )
1179
+
1180
+
1181
+ class Webp(Codec):
1182
+ """WebP codec for numcodecs."""
1183
+
1184
+ codec_id = 'imagecodecs_webp'
1185
+
1186
+ def __init__(self, level=None, lossless=None, method=None, hasalpha=None):
1187
+ self.level = level
1188
+ self.hasalpha = bool(hasalpha)
1189
+ self.method = method
1190
+ self.lossless = lossless
1191
+
1192
+ def encode(self, buf):
1193
+ buf = protective_squeeze(numpy.asarray(buf))
1194
+ return imagecodecs.webp_encode(
1195
+ buf, level=self.level, lossless=self.lossless, method=self.method
1196
+ )
1197
+
1198
+ def decode(self, buf, out=None):
1199
+ return imagecodecs.webp_decode(buf, hasalpha=self.hasalpha, out=out)
1200
+
1201
+
1202
+ class Xor(Codec):
1203
+ """XOR codec for numcodecs."""
1204
+
1205
+ codec_id = 'imagecodecs_xor'
1206
+
1207
+ def __init__(self, shape=None, dtype=None, axis=-1):
1208
+ self.shape = None if shape is None else tuple(shape)
1209
+ self.dtype = None if dtype is None else numpy.dtype(dtype).str
1210
+ self.axis = axis
1211
+
1212
+ def encode(self, buf):
1213
+ if self.shape is not None or self.dtype is not None:
1214
+ buf = protective_squeeze(numpy.asarray(buf))
1215
+ assert buf.shape == self.shape
1216
+ assert buf.dtype == self.dtype
1217
+ return imagecodecs.xor_encode(buf, axis=self.axis).tobytes()
1218
+
1219
+ def decode(self, buf, out=None):
1220
+ if self.shape is not None or self.dtype is not None:
1221
+ buf = numpy.frombuffer(buf, dtype=self.dtype).reshape(*self.shape)
1222
+ return imagecodecs.xor_decode(buf, axis=self.axis, out=_flat(out))
1223
+
1224
+
1225
+ class Zfp(Codec):
1226
+ """ZFP codec for numcodecs."""
1227
+
1228
+ codec_id = 'imagecodecs_zfp'
1229
+
1230
+ def __init__(
1231
+ self,
1232
+ shape=None,
1233
+ dtype=None,
1234
+ strides=None,
1235
+ level=None,
1236
+ mode=None,
1237
+ execution=None,
1238
+ numthreads=None,
1239
+ chunksize=None,
1240
+ header=True,
1241
+ ):
1242
+ if header:
1243
+ self.shape = None
1244
+ self.dtype = None
1245
+ self.strides = None
1246
+ elif shape is None or dtype is None:
1247
+ raise ValueError('invalid shape or dtype')
1248
+ else:
1249
+ self.shape = tuple(shape)
1250
+ self.dtype = numpy.dtype(dtype).str
1251
+ self.strides = None if strides is None else tuple(strides)
1252
+ self.level = level
1253
+ self.mode = mode
1254
+ self.execution = execution
1255
+ self.numthreads = numthreads
1256
+ self.chunksize = chunksize
1257
+ self.header = bool(header)
1258
+
1259
+ def encode(self, buf):
1260
+ buf = protective_squeeze(numpy.asarray(buf))
1261
+ if not self.header:
1262
+ assert buf.shape == self.shape
1263
+ assert buf.dtype == self.dtype
1264
+ return imagecodecs.zfp_encode(
1265
+ buf,
1266
+ level=self.level,
1267
+ mode=self.mode,
1268
+ execution=self.execution,
1269
+ header=self.header,
1270
+ numthreads=self.numthreads,
1271
+ chunksize=self.chunksize,
1272
+ )
1273
+
1274
+ def decode(self, buf, out=None):
1275
+ if self.header:
1276
+ return imagecodecs.zfp_decode(buf, out=out)
1277
+ return imagecodecs.zfp_decode(
1278
+ buf,
1279
+ shape=self.shape,
1280
+ dtype=numpy.dtype(self.dtype),
1281
+ strides=self.strides,
1282
+ numthreads=self.numthreads,
1283
+ out=out,
1284
+ )
1285
+
1286
+
1287
+ class Zlib(Codec):
1288
+ """Zlib codec for numcodecs."""
1289
+
1290
+ codec_id = 'imagecodecs_zlib'
1291
+
1292
+ def __init__(self, level=None):
1293
+ self.level = level
1294
+
1295
+ def encode(self, buf):
1296
+ return imagecodecs.zlib_encode(buf, level=self.level)
1297
+
1298
+ def decode(self, buf, out=None):
1299
+ return imagecodecs.zlib_decode(buf, out=_flat(out))
1300
+
1301
+
1302
+ class Zlibng(Codec):
1303
+ """Zlibng codec for numcodecs."""
1304
+
1305
+ codec_id = 'imagecodecs_zlibng'
1306
+
1307
+ def __init__(self, level=None):
1308
+ self.level = level
1309
+
1310
+ def encode(self, buf):
1311
+ return imagecodecs.zlibng_encode(buf, level=self.level)
1312
+
1313
+ def decode(self, buf, out=None):
1314
+ return imagecodecs.zlibng_decode(buf, out=_flat(out))
1315
+
1316
+
1317
+ class Zopfli(Codec):
1318
+ """Zopfli codec for numcodecs."""
1319
+
1320
+ codec_id = 'imagecodecs_zopfli'
1321
+
1322
+ def encode(self, buf):
1323
+ return imagecodecs.zopfli_encode(buf)
1324
+
1325
+ def decode(self, buf, out=None):
1326
+ return imagecodecs.zopfli_decode(buf, out=_flat(out))
1327
+
1328
+
1329
+ class Zstd(Codec):
1330
+ """ZStandard codec for numcodecs."""
1331
+
1332
+ codec_id = 'imagecodecs_zstd'
1333
+
1334
+ def __init__(self, level=None):
1335
+ self.level = level
1336
+
1337
+ def encode(self, buf):
1338
+ return imagecodecs.zstd_encode(buf, level=self.level)
1339
+
1340
+ def decode(self, buf, out=None):
1341
+ return imagecodecs.zstd_decode(buf, out=_flat(out))
1342
+
1343
+
1344
+ def _flat(out):
1345
+ """Return numpy array as contiguous view of bytes if possible."""
1346
+ if out is None:
1347
+ return None
1348
+ view = memoryview(out)
1349
+ if view.readonly or not view.contiguous:
1350
+ return None
1351
+ return view.cast('B')
1352
+
1353
+
1354
+ def register_codecs(codecs=None, force=False, verbose=True):
1355
+ """Register codecs in this module with numcodecs."""
1356
+ for name, cls in globals().items():
1357
+ if not hasattr(cls, 'codec_id') or name == 'Codec':
1358
+ continue
1359
+ if codecs is not None and cls.codec_id not in codecs:
1360
+ continue
1361
+ try:
1362
+ try:
1363
+ get_codec({'id': cls.codec_id})
1364
+ except TypeError:
1365
+ # registered, but failed
1366
+ pass
1367
+ except ValueError:
1368
+ # not registered yet
1369
+ pass
1370
+ else:
1371
+ if not force:
1372
+ if verbose:
1373
+ log_warning(
1374
+ f'numcodec {cls.codec_id!r} already registered'
1375
+ )
1376
+ continue
1377
+ if verbose:
1378
+ log_warning(f'replacing registered numcodec {cls.codec_id!r}')
1379
+ register_codec(cls)
1380
+
1381
+
1382
+ def log_warning(msg, *args, **kwargs):
1383
+ """Log message with level WARNING."""
1384
+ import logging
1385
+
1386
+ logging.getLogger(__name__).warning(msg, *args, **kwargs)
common/checkpoint_util.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional, Dict
2
+ import os
3
+
4
+ class TopKCheckpointManager:
5
+ def __init__(self,
6
+ save_dir,
7
+ monitor_key: str,
8
+ mode='min',
9
+ k=1,
10
+ format_str='epoch={epoch:03d}-train_loss={train_loss:.3f}.ckpt'
11
+ ):
12
+ assert mode in ['max', 'min']
13
+ assert k >= 0
14
+
15
+ self.save_dir = save_dir
16
+ self.monitor_key = monitor_key
17
+ self.mode = mode
18
+ self.k = k
19
+ self.format_str = format_str
20
+ self.path_value_map = dict()
21
+
22
+ def get_ckpt_path(self, data: Dict[str, float]) -> Optional[str]:
23
+ if self.k == 0:
24
+ return None
25
+
26
+ value = data[self.monitor_key]
27
+ ckpt_path = os.path.join(
28
+ self.save_dir, self.format_str.format(**data))
29
+
30
+ if len(self.path_value_map) < self.k:
31
+ # under-capacity
32
+ self.path_value_map[ckpt_path] = value
33
+ return ckpt_path
34
+
35
+ # at capacity
36
+ sorted_map = sorted(self.path_value_map.items(), key=lambda x: x[1])
37
+ min_path, min_value = sorted_map[0]
38
+ max_path, max_value = sorted_map[-1]
39
+
40
+ delete_path = None
41
+ if self.mode == 'max':
42
+ if value > min_value:
43
+ delete_path = min_path
44
+ else:
45
+ if value < max_value:
46
+ delete_path = max_path
47
+
48
+ if delete_path is None:
49
+ return None
50
+ else:
51
+ del self.path_value_map[delete_path]
52
+ self.path_value_map[ckpt_path] = value
53
+
54
+ if not os.path.exists(self.save_dir):
55
+ os.mkdir(self.save_dir)
56
+
57
+ if os.path.exists(delete_path):
58
+ os.remove(delete_path)
59
+ return ckpt_path
common/compute_all_loss.py ADDED
@@ -0,0 +1,483 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import numpy as np
3
+ import torch.nn.functional as F
4
+ import random
5
+ from einops import rearrange, reduce
6
+ import cv2
7
+ import concurrent.futures
8
+ from diffusion_policy.policy.base_image_policy import BaseImagePolicy
9
+ from diffusion_policy.common.pytorch_util import dict_apply, replace_submodules
10
+ from diffusion_policy.model.common.slice import slice_episode
11
+
12
+
13
+ def unflatten_dataset_dict(flat_dict, delimiter='/'):
14
+ result = {}
15
+ for compound_key, value in flat_dict.items():
16
+ keys = compound_key.split(delimiter)
17
+ current = result
18
+ for key in keys[:-1]:
19
+ if key not in current:
20
+ current[key] = {}
21
+ current = current[key]
22
+ current[keys[-1]] = value
23
+
24
+ return result
25
+
26
+
27
+ def decode_image(data):
28
+ return cv2.imdecode(data, 1)
29
+
30
+
31
+ def compute_all_traj_loss(replay_buffer=None, model:BaseImagePolicy=None, ref_model:BaseImagePolicy=None, stride=1):
32
+ if replay_buffer is None:
33
+ return np.zeros([1])
34
+ else:
35
+ data = replay_buffer.data
36
+ meta_data = replay_buffer.meta
37
+ observations_1 = np.array(data['obs'], dtype=np.float32)
38
+ actions_1 = np.array(data['action'], dtype=np.float32)
39
+ observations_2 = np.array(data['obs_2'], dtype=np.float32)
40
+ actions_2 = np.array(data['action_2'], dtype=np.float32)
41
+
42
+ total_size = len(observations_1)
43
+
44
+ # Calculate 25% of the data size
45
+ sample_size = int(total_size * 0.25)
46
+
47
+ # Generate random indices for sampling
48
+ indices = np.random.choice(total_size, size=sample_size, replace=False)
49
+
50
+ # Extract 25% of the data using the indices
51
+ observations_1 = observations_1[indices]
52
+ actions_1 = actions_1[indices]
53
+ observations_2 = observations_2[indices]
54
+ actions_2 = actions_2[indices]
55
+
56
+ for param in ref_model.parameters():
57
+ param.requires_grad = False
58
+
59
+ ref_model = ref_model.to(model.device)
60
+
61
+ # Normalize data
62
+ batch_1 = {
63
+ 'obs': observations_1,
64
+ 'action': actions_1,
65
+ }
66
+
67
+ batch_2 = {
68
+ 'obs': observations_2,
69
+ 'action': actions_2,
70
+ }
71
+ nbatch_1 = model.normalizer.normalize(batch_1)
72
+ nbatch_2 = model.normalizer.normalize(batch_2)
73
+ obs_1, obs_2 = nbatch_1['obs'], nbatch_2['obs']
74
+ actions_1, actions_2 = nbatch_1['action'], nbatch_2['action']
75
+
76
+ # Slice trajectories
77
+ obs_1 = slice_episode(obs_1, horizon=model.horizon, stride=stride)
78
+ action_1 = slice_episode(actions_1, horizon=model.horizon, stride=stride)
79
+ obs_2 = slice_episode(obs_2, horizon=model.horizon, stride=stride)
80
+ action_2 = slice_episode(actions_2, horizon=model.horizon, stride=stride)
81
+
82
+
83
+ bsz = obs_1[0].shape[0]
84
+ timesteps = torch.randint(0, model.noise_scheduler.config.num_train_timesteps, (bsz,), device=model.device).long()
85
+
86
+ # Pre-allocate loss
87
+ traj_loss_1, traj_loss_2 = 0, 0
88
+
89
+ # Helper function to compute loss for a single trajectory
90
+ def compute_traj_loss(obs_slices, action_slices, timestep, model, ref_policy):
91
+ total_loss = 0
92
+
93
+ for idx, (obs_slide, action_slide) in enumerate(zip(obs_slices, action_slices)):
94
+ gamma_factors = model.gamma ** (idx * model.horizon + torch.arange(model.horizon, device=model.device))
95
+ if model.obs_as_cond:
96
+ cond = obs_slide[:, :model.n_obs_steps, :]
97
+ cond = cond.detach().to(model.device)
98
+ # cond.detach().to(model.device)
99
+ trajectory = action_slide[:, -model.n_action_steps:] if model.pred_action_steps_only else action_slide
100
+ else:
101
+ cond = None
102
+ trajectory = np.concatenate([action_slide, obs_slide], axis=-1)
103
+
104
+ condition_mask = model.mask_generator(trajectory.shape).to(model.device)
105
+ loss_mask = (~condition_mask).float()
106
+
107
+ trajectory = torch.tensor(trajectory, device=model.device, dtype=torch.float32)
108
+ noise = torch.randn(trajectory.shape, device=model.device)
109
+
110
+ # Disable gradient computation
111
+ with torch.no_grad():
112
+ noisy_trajectory = model.noise_scheduler.add_noise(trajectory, noise, timestep)
113
+ noisy_trajectory[condition_mask] = trajectory[condition_mask]
114
+
115
+ pred_ref = ref_policy(noisy_trajectory, timestep, cond)
116
+ pred = model.model(noisy_trajectory, timestep, cond)
117
+
118
+ pred_type = model.noise_scheduler.config.prediction_type
119
+ if pred_type == 'epsilon':
120
+ target = noise
121
+ elif pred_type == 'sample':
122
+ target = trajectory
123
+ else:
124
+ raise ValueError(f"Unsupported prediction type {pred_type}")
125
+
126
+ loss = F.mse_loss(pred, target, reduction='none')
127
+ loss_ref = F.mse_loss(pred_ref, target, reduction='none')
128
+ loss = loss * loss_mask.type(loss.dtype)
129
+ loss_ref = loss_ref * loss_mask.type(loss.dtype)
130
+ loss = reduce(loss, 'b t ... -> b t (...)', 'mean')
131
+ loss_ref = reduce(loss_ref, 'b t ... -> b t (...)', 'mean')
132
+
133
+ slice_loss = torch.sum((loss - loss_ref), dim=-1)
134
+ total_loss += torch.sum(slice_loss * gamma_factors)
135
+ # Explicitly delete unused variables to release GPU memory
136
+ del trajectory, noise, noisy_trajectory, pred_ref, pred, loss_mask, condition_mask
137
+ torch.cuda.empty_cache()
138
+
139
+ return total_loss.detach()
140
+
141
+ # Compute loss for trajectory 1
142
+ traj_loss_1 = compute_traj_loss(obs_1, action_1, timesteps, model, ref_model)
143
+ # Compute loss for trajectory 2
144
+ traj_loss_2 = compute_traj_loss(obs_2, action_2, timesteps, model, ref_model)
145
+
146
+ # Average the losses
147
+ loss = (traj_loss_1 + traj_loss_2) / 2
148
+
149
+ return torch.mean(loss)
150
+
151
+
152
+ def compute_all_traj_loss_realrobot(replay_buffer=None, model=None, ref_model=None, stride=1, sample_size = 20, batch_size=10, gc_every_n_batches=12):
153
+ import gc
154
+ gc.collect()
155
+ torch.cuda.empty_cache()
156
+
157
+ if replay_buffer is None:
158
+ return np.zeros([1])
159
+ else:
160
+ assert sample_size >= batch_size, "data_size should be greater than or equal to batch_size"
161
+ if hasattr(torch.cuda, 'set_per_process_memory_fraction'):
162
+ torch.cuda.set_per_process_memory_fraction(0.8)
163
+
164
+ data = replay_buffer.data
165
+ data = unflatten_dataset_dict(flat_dict=data)
166
+ data_size = len(data['action'])
167
+ indicis = np.random.choice(data_size, size=sample_size, replace=False)
168
+
169
+ observations_1 = data['obs']
170
+ actions_1 = np.array(data['action'][indicis], dtype=np.float32)
171
+ observations_2 = data['obs_2']
172
+ actions_2 = np.array(data['action_2'][indicis], dtype=np.float32)
173
+ compress_len_1 = data['compress_len'][indicis]
174
+ compress_len_2 = data['compress_len_2'][indicis]
175
+ camera_keys = observations_1['images'].keys()
176
+ qpos_keys = [key for key in observations_1.keys() if key != 'images']
177
+ del data
178
+
179
+ for key in camera_keys:
180
+ img_data_1 = observations_1['images'][key][indicis]
181
+ img_data_2 = observations_2['images'][key][indicis]
182
+ total_images = img_data_1.shape[0]
183
+
184
+ img_batch_size = min(batch_size, total_images)
185
+ decompressed_images_1 = []
186
+
187
+ for batch_idx in range(0, total_images, img_batch_size):
188
+ end_idx = min(batch_idx + img_batch_size, total_images)
189
+ batch_decompressed = []
190
+
191
+ for k in range(batch_idx, end_idx):
192
+ image = img_data_1[k, :, :int(compress_len_1[k, 0])].copy()
193
+ with concurrent.futures.ThreadPoolExecutor() as executor:
194
+ results = executor.map(decode_image, image)
195
+ decompressed_images = list(results)
196
+ batch_decompressed.append(decompressed_images)
197
+
198
+ batch_decompressed = np.array(batch_decompressed)
199
+ batch_decompressed = np.einsum('b k h w c -> b k c h w', batch_decompressed)
200
+ decompressed_images_1.append(torch.from_numpy(batch_decompressed / 255.0).float())
201
+
202
+ del batch_decompressed
203
+ torch.cuda.empty_cache()
204
+
205
+ if batch_idx % (img_batch_size * gc_every_n_batches) == 0:
206
+ gc.collect()
207
+
208
+ observations_1[key] = torch.cat(decompressed_images_1, dim=0)
209
+ del observations_1['images']
210
+ del decompressed_images_1
211
+
212
+ decompressed_images_2 = []
213
+
214
+ for batch_idx in range(0, total_images, img_batch_size):
215
+ end_idx = min(batch_idx + img_batch_size, total_images)
216
+ batch_decompressed = []
217
+
218
+ for k in range(batch_idx, end_idx):
219
+ image = img_data_2[k, :, :int(compress_len_2[k, 0])].copy()
220
+ with concurrent.futures.ThreadPoolExecutor() as executor:
221
+ results = executor.map(decode_image, image)
222
+ decompressed_images = list(results)
223
+ batch_decompressed.append(decompressed_images)
224
+
225
+ batch_decompressed = np.array(batch_decompressed)
226
+ batch_decompressed = np.einsum('b k h w c -> b k c h w', batch_decompressed)
227
+ decompressed_images_2.append(torch.from_numpy(batch_decompressed / 255.0).float())
228
+
229
+ del batch_decompressed
230
+ torch.cuda.empty_cache()
231
+
232
+ if batch_idx % (img_batch_size * gc_every_n_batches) == 0:
233
+ gc.collect()
234
+
235
+ observations_2[key] = torch.cat(decompressed_images_2, dim=0)
236
+ del observations_2['images']
237
+ del decompressed_images_2
238
+
239
+ del img_data_1, img_data_2
240
+ gc.collect()
241
+ torch.cuda.empty_cache()
242
+
243
+ for key in qpos_keys:
244
+ observations_1[key] = torch.from_numpy(observations_1[key]).float()
245
+ observations_2[key] = torch.from_numpy(observations_2[key]).float()
246
+
247
+ observations_1[key] = observations_1[key].cpu()
248
+ observations_2[key] = observations_2[key].cpu()
249
+
250
+ gc.collect()
251
+ torch.cuda.empty_cache()
252
+
253
+ for param in ref_model.parameters():
254
+ param.requires_grad = False
255
+
256
+ device = model.device
257
+ ref_model = ref_model.to(device)
258
+
259
+ with torch.no_grad():
260
+ obs_1 = model.normalizer.normalize(observations_1)
261
+ action_1 = model.normalizer['action'].normalize(actions_1)
262
+ obs_2 = model.normalizer.normalize(observations_2)
263
+ action_2 = model.normalizer['action'].normalize(actions_2)
264
+
265
+ start_1 = random.randint(0, model.n_obs_steps)
266
+ start_2 = random.randint(0, model.n_obs_steps)
267
+
268
+ with torch.no_grad():
269
+ obs_1 = {key: slice_episode(obs_1[key], horizon=model.horizon, stride=stride, start=start_1) for key in obs_1.keys()}
270
+ action_1 = slice_episode(action_1, horizon=model.horizon, stride=stride, start=start_1)
271
+ obs_2 = {key: slice_episode(obs_2[key], horizon=model.horizon, stride=stride, start=start_2) for key in obs_2.keys()}
272
+ action_2 = slice_episode(action_2, horizon=model.horizon, stride=stride, start=start_2)
273
+
274
+ del observations_1, observations_2, actions_1, actions_2
275
+ gc.collect()
276
+ torch.cuda.empty_cache()
277
+
278
+ def compute_traj_image_loss_batched(obs_slices, action_slices, model, ref_model, batch_size):
279
+ with torch.no_grad():
280
+ To = model.n_obs_steps
281
+ horizon = model.horizon
282
+ total_samples = action_slices.shape[0]
283
+ total_loss = torch.zeros(total_samples, device='cpu')
284
+
285
+ num_batches = (total_samples + batch_size - 1) // batch_size
286
+ device = model.device
287
+
288
+ for batch_idx in range(num_batches):
289
+
290
+ if batch_idx > 0 and batch_idx % gc_every_n_batches == 0:
291
+ gc.collect()
292
+ torch.cuda.empty_cache()
293
+
294
+ start_idx = batch_idx * batch_size
295
+ end_idx = min(start_idx + batch_size, total_samples)
296
+ current_batch_size = end_idx - start_idx
297
+
298
+ batch_timesteps = torch.randint(0, model.noise_scheduler.config.num_train_timesteps,
299
+ (current_batch_size,), device=device).long()
300
+
301
+ batch_action_slices = action_slices[start_idx:end_idx].to(device)
302
+ batch_obs_slices = {key: obs_slices[key][start_idx:end_idx].to(device) for key in obs_slices.keys()}
303
+
304
+ batch_total_loss = torch.zeros(current_batch_size, device=device)
305
+
306
+ for idx in range(current_batch_size):
307
+ action_slide = batch_action_slices[idx:idx+1]
308
+ obs_slide = {key: batch_obs_slices[key][idx:idx+1] for key in batch_obs_slices.keys()}
309
+ sample_timesteps = batch_timesteps[idx:idx+1]
310
+
311
+ local_cond = None
312
+ global_cond = None
313
+ global_cond_ref = None
314
+
315
+ if model.obs_as_global_cond:
316
+ this_nobs = dict_apply(obs_slide,
317
+ lambda x: x[:,:To,...].reshape(-1, *x.shape[2:]))
318
+ nobs_features = model.obs_encoder(this_nobs)
319
+ nobs_features_ref = ref_model.obs_encoder(this_nobs)
320
+
321
+ global_cond = nobs_features.reshape(1, -1)
322
+ global_cond_ref = nobs_features_ref.reshape(1, -1)
323
+ trajectory = action_slide
324
+ else:
325
+ this_nobs = dict_apply(obs_slide,
326
+ lambda x: x.reshape(-1, *x.shape[2:]))
327
+ nobs_features = model.obs_encoder(this_nobs)
328
+ nobs_features_ref = ref_model.obs_encoder(this_nobs)
329
+
330
+ nobs_features = nobs_features.reshape(1, horizon, -1)
331
+ nobs_features_ref = nobs_features_ref.reshape(1, horizon, -1)
332
+
333
+ trajectory = torch.cat([action_slide, nobs_features], dim=-1)
334
+ trajectory_ref = torch.cat([action_slide, nobs_features_ref], dim=-1)
335
+
336
+ condition_mask = model.mask_generator(trajectory.shape).to(device)
337
+ loss_mask = (~condition_mask).float()
338
+
339
+ noise = torch.randn(trajectory.shape, device=device)
340
+
341
+ noisy_trajectory = model.noise_scheduler.add_noise(trajectory, noise, sample_timesteps)
342
+ noisy_trajectory[condition_mask] = trajectory[condition_mask]
343
+
344
+ if not model.obs_as_global_cond:
345
+ noisy_trajectory_ref = model.noise_scheduler.add_noise(trajectory_ref, noise, sample_timesteps)
346
+ noisy_trajectory_ref[condition_mask] = trajectory_ref[condition_mask]
347
+ else:
348
+ noisy_trajectory_ref = noisy_trajectory.clone()
349
+
350
+ pred = model.model(noisy_trajectory, sample_timesteps,
351
+ local_cond=local_cond, global_cond=global_cond)
352
+ pred_ref = ref_model.model(noisy_trajectory_ref if not model.obs_as_global_cond else noisy_trajectory,
353
+ sample_timesteps, local_cond=local_cond, global_cond=global_cond_ref)
354
+
355
+ pred_type = model.noise_scheduler.config.prediction_type
356
+ if pred_type == 'epsilon':
357
+ target = noise
358
+ elif pred_type == 'sample':
359
+ target = trajectory
360
+ else:
361
+ raise ValueError(f"Unsupported prediction type {pred_type}")
362
+
363
+ loss = F.mse_loss(pred, target, reduction='none')
364
+ loss_ref = F.mse_loss(pred_ref, target, reduction='none')
365
+
366
+ loss = loss * loss_mask
367
+ loss_ref = loss_ref * loss_mask
368
+ loss = reduce(loss, 'b t ... -> b t (...)', 'mean')
369
+ loss_ref = reduce(loss_ref, 'b t ... -> b t (...)', 'mean')
370
+
371
+ slice_loss = torch.sum(loss_ref - loss, dim=1)
372
+ batch_total_loss[idx] = slice_loss.squeeze()
373
+
374
+ del trajectory, noise, noisy_trajectory, pred, pred_ref
375
+ if not model.obs_as_global_cond:
376
+ del trajectory_ref, noisy_trajectory_ref
377
+ del nobs_features, nobs_features_ref, this_nobs
378
+
379
+ total_loss[start_idx:end_idx] = batch_total_loss.cpu()
380
+
381
+ del batch_action_slices, batch_obs_slices, batch_timesteps, batch_total_loss
382
+ torch.cuda.empty_cache()
383
+
384
+ return total_loss
385
+
386
+ with torch.no_grad():
387
+ total_samples = action_1.shape[0]
388
+ traj_loss_1 = compute_traj_image_loss_batched(obs_1, action_1, model, ref_model, batch_size)
389
+
390
+ if traj_loss_1.device.type != 'cpu':
391
+ traj_loss_1 = traj_loss_1.cpu()
392
+
393
+ del obs_1, action_1
394
+ gc.collect()
395
+ torch.cuda.empty_cache()
396
+
397
+ total_samples = action_2.shape[0]
398
+ traj_loss_2 = compute_traj_image_loss_batched(obs_2, action_2, model, ref_model, batch_size)
399
+
400
+ if traj_loss_2.device.type != 'cpu':
401
+ traj_loss_2 = traj_loss_2.cpu()
402
+
403
+ loss = (traj_loss_1 + traj_loss_2) / 2
404
+ final_loss = torch.mean(loss)
405
+
406
+ del obs_2, action_2, traj_loss_1, traj_loss_2, loss
407
+ gc.collect()
408
+ torch.cuda.empty_cache()
409
+
410
+ return final_loss
411
+
412
+
413
+ def compute_all_bet_traj_loss(replay_buffer=None, model=None, stride=1):
414
+ if replay_buffer is None:
415
+ return np.zeros([1])
416
+ else:
417
+ data = replay_buffer.data
418
+ meta_data = replay_buffer.meta
419
+ observations_1 = np.array(data['obs'], dtype=np.float32)
420
+ actions_1 = np.array(data['action'], dtype=np.float32)
421
+ observations_2 = np.array(data['obs_2'], dtype=np.float32)
422
+ actions_2 = np.array(data['action_2'], dtype=np.float32)
423
+ length_1 = torch.tensor(meta_data['length'], device=model.device)
424
+ length_2 = torch.tensor(meta_data['length_2'], device=model.device)
425
+
426
+ # Normalize data
427
+ batch_1 = {
428
+ 'obs': observations_1,
429
+ 'action': actions_1,
430
+ }
431
+
432
+ batch_2 = {
433
+ 'obs': observations_2,
434
+ 'action': actions_2,
435
+ }
436
+ nbatch_1 = model.normalizer.normalize(batch_1)
437
+ nbatch_2 = model.normalizer.normalize(batch_2)
438
+ obs_1, obs_2 = nbatch_1['obs'], nbatch_2['obs']
439
+ actions_1, actions_2 = nbatch_1['action'], nbatch_2['action']
440
+
441
+ # Slice trajectories
442
+ obs_1 = slice_episode(obs_1, horizon=model.horizon, stride=stride)
443
+ action_1 = slice_episode(actions_1, horizon=model.horizon, stride=stride)
444
+ obs_2 = slice_episode(obs_2, horizon=model.horizon, stride=stride)
445
+ action_2 = slice_episode(actions_2, horizon=model.horizon, stride=stride)
446
+
447
+ # Pre-allocate loss
448
+ traj_loss_1, traj_loss_2 = 0, 0
449
+
450
+ # Helper function to compute loss for a single trajectory
451
+ def compute_traj_loss(obs_slices, action_slices, model, length, stride):
452
+ total_loss = 0
453
+
454
+ for idx, (obs_slide, action_slide) in enumerate(zip(obs_slices, action_slices)):
455
+ gamma_factors = model.gamma ** (idx * model.horizon)
456
+ obs_slide[:, model.n_obs_steps:, :] = -2
457
+
458
+ enc_obs = model.obs_encoding_net(obs_slide)
459
+ latent = model.action_ae.encode_into_latent(action_slide, enc_obs)
460
+
461
+ loss = model.get_pred_loss(
462
+ obs_rep=enc_obs.clone(),
463
+ target_latents=latent,
464
+ )
465
+
466
+ mask = (model.horizon + (idx - 1)*stride) <= length
467
+ mask = mask.int()
468
+
469
+ total_loss += (loss * mask) * gamma_factors
470
+
471
+ total_loss = torch.sum(total_loss, dim=-1)
472
+
473
+ return total_loss.detach()
474
+
475
+ # Compute loss for trajectory 1
476
+ traj_loss_1 = compute_traj_loss(obs_1, action_1, model, length_1, stride)
477
+ # Compute loss for trajectory 2
478
+ traj_loss_2 = compute_traj_loss(obs_2, action_2, model, length_2, stride)
479
+
480
+ # Average the losses
481
+ loss = (traj_loss_1 + traj_loss_2) / 2
482
+
483
+ return torch.mean(loss)
common/cv2_util.py ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Tuple
2
+ import math
3
+ import cv2
4
+ import numpy as np
5
+
6
+ def draw_reticle(img, u, v, label_color):
7
+ """
8
+ Draws a reticle (cross-hair) on the image at the given position on top of
9
+ the original image.
10
+ @param img (In/Out) uint8 3 channel image
11
+ @param u X coordinate (width)
12
+ @param v Y coordinate (height)
13
+ @param label_color tuple of 3 ints for RGB color used for drawing.
14
+ """
15
+ # Cast to int.
16
+ u = int(u)
17
+ v = int(v)
18
+
19
+ white = (255, 255, 255)
20
+ cv2.circle(img, (u, v), 10, label_color, 1)
21
+ cv2.circle(img, (u, v), 11, white, 1)
22
+ cv2.circle(img, (u, v), 12, label_color, 1)
23
+ cv2.line(img, (u, v + 1), (u, v + 3), white, 1)
24
+ cv2.line(img, (u + 1, v), (u + 3, v), white, 1)
25
+ cv2.line(img, (u, v - 1), (u, v - 3), white, 1)
26
+ cv2.line(img, (u - 1, v), (u - 3, v), white, 1)
27
+
28
+
29
+ def draw_text(
30
+ img,
31
+ *,
32
+ text,
33
+ uv_top_left,
34
+ color=(255, 255, 255),
35
+ fontScale=0.5,
36
+ thickness=1,
37
+ fontFace=cv2.FONT_HERSHEY_SIMPLEX,
38
+ outline_color=(0, 0, 0),
39
+ line_spacing=1.5,
40
+ ):
41
+ """
42
+ Draws multiline with an outline.
43
+ """
44
+ assert isinstance(text, str)
45
+
46
+ uv_top_left = np.array(uv_top_left, dtype=float)
47
+ assert uv_top_left.shape == (2,)
48
+
49
+ for line in text.splitlines():
50
+ (w, h), _ = cv2.getTextSize(
51
+ text=line,
52
+ fontFace=fontFace,
53
+ fontScale=fontScale,
54
+ thickness=thickness,
55
+ )
56
+ uv_bottom_left_i = uv_top_left + [0, h]
57
+ org = tuple(uv_bottom_left_i.astype(int))
58
+
59
+ if outline_color is not None:
60
+ cv2.putText(
61
+ img,
62
+ text=line,
63
+ org=org,
64
+ fontFace=fontFace,
65
+ fontScale=fontScale,
66
+ color=outline_color,
67
+ thickness=thickness * 3,
68
+ lineType=cv2.LINE_AA,
69
+ )
70
+ cv2.putText(
71
+ img,
72
+ text=line,
73
+ org=org,
74
+ fontFace=fontFace,
75
+ fontScale=fontScale,
76
+ color=color,
77
+ thickness=thickness,
78
+ lineType=cv2.LINE_AA,
79
+ )
80
+
81
+ uv_top_left += [0, h * line_spacing]
82
+
83
+
84
+ def get_image_transform(
85
+ input_res: Tuple[int,int]=(1280,720),
86
+ output_res: Tuple[int,int]=(640,480),
87
+ bgr_to_rgb: bool=False):
88
+
89
+ iw, ih = input_res
90
+ ow, oh = output_res
91
+ rw, rh = None, None
92
+ interp_method = cv2.INTER_AREA
93
+
94
+ if (iw/ih) >= (ow/oh):
95
+ # input is wider
96
+ rh = oh
97
+ rw = math.ceil(rh / ih * iw)
98
+ if oh > ih:
99
+ interp_method = cv2.INTER_LINEAR
100
+ else:
101
+ rw = ow
102
+ rh = math.ceil(rw / iw * ih)
103
+ if ow > iw:
104
+ interp_method = cv2.INTER_LINEAR
105
+
106
+ w_slice_start = (rw - ow) // 2
107
+ w_slice = slice(w_slice_start, w_slice_start + ow)
108
+ h_slice_start = (rh - oh) // 2
109
+ h_slice = slice(h_slice_start, h_slice_start + oh)
110
+ c_slice = slice(None)
111
+ if bgr_to_rgb:
112
+ c_slice = slice(None, None, -1)
113
+
114
+ def transform(img: np.ndarray):
115
+ assert img.shape == ((ih,iw,3))
116
+ # resize
117
+ img = cv2.resize(img, (rw, rh), interpolation=interp_method)
118
+ # crop
119
+ img = img[h_slice, w_slice, c_slice]
120
+ return img
121
+ return transform
122
+
123
+ def optimal_row_cols(
124
+ n_cameras,
125
+ in_wh_ratio,
126
+ max_resolution=(1920, 1080)
127
+ ):
128
+ out_w, out_h = max_resolution
129
+ out_wh_ratio = out_w / out_h
130
+
131
+ n_rows = np.arange(n_cameras,dtype=np.int64) + 1
132
+ n_cols = np.ceil(n_cameras / n_rows).astype(np.int64)
133
+ cat_wh_ratio = in_wh_ratio * (n_cols / n_rows)
134
+ ratio_diff = np.abs(out_wh_ratio - cat_wh_ratio)
135
+ best_idx = np.argmin(ratio_diff)
136
+ best_n_row = n_rows[best_idx]
137
+ best_n_col = n_cols[best_idx]
138
+ best_cat_wh_ratio = cat_wh_ratio[best_idx]
139
+
140
+ rw, rh = None, None
141
+ if best_cat_wh_ratio >= out_wh_ratio:
142
+ # cat is wider
143
+ rw = math.floor(out_w / best_n_col)
144
+ rh = math.floor(rw / in_wh_ratio)
145
+ else:
146
+ rh = math.floor(out_h / best_n_row)
147
+ rw = math.floor(rh * in_wh_ratio)
148
+
149
+ # crop_resolution = (rw, rh)
150
+ return rw, rh, best_n_col, best_n_row
common/env_util.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+
4
+
5
+ def render_env_video(env, states, actions=None):
6
+ observations = states
7
+ imgs = list()
8
+ for i in range(len(observations)):
9
+ state = observations[i]
10
+ env.set_state(state)
11
+ if i == 0:
12
+ env.set_state(state)
13
+ img = env.render()
14
+ # draw action
15
+ if actions is not None:
16
+ action = actions[i]
17
+ coord = (action / 512 * 96).astype(np.int32)
18
+ cv2.drawMarker(img, coord,
19
+ color=(255,0,0), markerType=cv2.MARKER_CROSS,
20
+ markerSize=8, thickness=1)
21
+ imgs.append(img)
22
+ imgs = np.array(imgs)
23
+ return imgs
common/json_logger.py ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional, Callable, Any, Sequence
2
+ import os
3
+ import copy
4
+ import json
5
+ import numbers
6
+ import pandas as pd
7
+
8
+
9
+ def read_json_log(path: str,
10
+ required_keys: Sequence[str]=tuple(),
11
+ **kwargs) -> pd.DataFrame:
12
+ """
13
+ Read json-per-line file, with potentially incomplete lines.
14
+ kwargs passed to pd.read_json
15
+ """
16
+ lines = list()
17
+ with open(path, 'r') as f:
18
+ while True:
19
+ # one json per line
20
+ line = f.readline()
21
+ if len(line) == 0:
22
+ # EOF
23
+ break
24
+ elif not line.endswith('\n'):
25
+ # incomplete line
26
+ break
27
+ is_relevant = False
28
+ for k in required_keys:
29
+ if k in line:
30
+ is_relevant = True
31
+ break
32
+ if is_relevant:
33
+ lines.append(line)
34
+ if len(lines) < 1:
35
+ return pd.DataFrame()
36
+ json_buf = f'[{",".join([line for line in (line.strip() for line in lines) if line])}]'
37
+ df = pd.read_json(json_buf, **kwargs)
38
+ return df
39
+
40
+ class JsonLogger:
41
+ def __init__(self, path: str,
42
+ filter_fn: Optional[Callable[[str,Any],bool]]=None):
43
+ if filter_fn is None:
44
+ filter_fn = lambda k,v: isinstance(v, numbers.Number)
45
+
46
+ # default to append mode
47
+ self.path = path
48
+ self.filter_fn = filter_fn
49
+ self.file = None
50
+ self.last_log = None
51
+
52
+ def start(self):
53
+ # use line buffering
54
+ try:
55
+ self.file = file = open(self.path, 'r+', buffering=1)
56
+ except FileNotFoundError:
57
+ self.file = file = open(self.path, 'w+', buffering=1)
58
+
59
+ # Move the pointer (similar to a cursor in a text editor) to the end of the file
60
+ pos = file.seek(0, os.SEEK_END)
61
+
62
+ # Read each character in the file one at a time from the last
63
+ # character going backwards, searching for a newline character
64
+ # If we find a new line, exit the search
65
+ while pos > 0 and file.read(1) != "\n":
66
+ pos -= 1
67
+ file.seek(pos, os.SEEK_SET)
68
+ # now the file pointer is at one past the last '\n'
69
+ # and pos is at the last '\n'.
70
+ last_line_end = file.tell()
71
+
72
+ # find the start of second last line
73
+ pos = max(0, pos-1)
74
+ file.seek(pos, os.SEEK_SET)
75
+ while pos > 0 and file.read(1) != "\n":
76
+ pos -= 1
77
+ file.seek(pos, os.SEEK_SET)
78
+ # now the file pointer is at one past the second last '\n'
79
+ last_line_start = file.tell()
80
+
81
+ if last_line_start < last_line_end:
82
+ # has last line of json
83
+ last_line = file.readline()
84
+ self.last_log = json.loads(last_line)
85
+
86
+ # remove the last incomplete line
87
+ file.seek(last_line_end)
88
+ file.truncate()
89
+
90
+ def stop(self):
91
+ self.file.close()
92
+ self.file = None
93
+
94
+ def __enter__(self):
95
+ self.start()
96
+ return self
97
+
98
+ def __exit__(self, exc_type, exc_val, exc_tb):
99
+ self.stop()
100
+
101
+ def log(self, data: dict):
102
+ filtered_data = dict(
103
+ filter(lambda x: self.filter_fn(*x), data.items()))
104
+ # save current as last log
105
+ self.last_log = filtered_data
106
+ for k, v in filtered_data.items():
107
+ if isinstance(v, numbers.Integral):
108
+ filtered_data[k] = int(v)
109
+ elif isinstance(v, numbers.Number):
110
+ filtered_data[k] = float(v)
111
+ buf = json.dumps(filtered_data)
112
+ # ensure one line per json
113
+ buf = buf.replace('\n','') + '\n'
114
+ self.file.write(buf)
115
+
116
+ def get_last_log(self):
117
+ return copy.deepcopy(self.last_log)
common/nested_dict_util.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import functools
2
+
3
+ def nested_dict_map(f, x):
4
+ """
5
+ Map f over all leaf of nested dict x
6
+ """
7
+
8
+ if not isinstance(x, dict):
9
+ return f(x)
10
+ y = dict()
11
+ for key, value in x.items():
12
+ y[key] = nested_dict_map(f, value)
13
+ return y
14
+
15
+ def nested_dict_reduce(f, x):
16
+ """
17
+ Map f over all values of nested dict x, and reduce to a single value
18
+ """
19
+ if not isinstance(x, dict):
20
+ return x
21
+
22
+ reduced_values = list()
23
+ for value in x.values():
24
+ reduced_values.append(nested_dict_reduce(f, value))
25
+ y = functools.reduce(f, reduced_values)
26
+ return y
27
+
28
+
29
+ def nested_dict_check(f, x):
30
+ bool_dict = nested_dict_map(f, x)
31
+ result = nested_dict_reduce(lambda x, y: x and y, bool_dict)
32
+ return result
common/normalize_util.py ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from diffusion_policy.model.common.normalizer import SingleFieldLinearNormalizer
2
+ from diffusion_policy.common.pytorch_util import dict_apply, dict_apply_reduce, dict_apply_split
3
+ import numpy as np
4
+
5
+
6
+ def get_range_normalizer_from_stat(stat, output_max=1, output_min=-1, range_eps=1e-7):
7
+ # -1, 1 normalization
8
+ input_max = stat['max']
9
+ input_min = stat['min']
10
+ input_range = input_max - input_min
11
+ ignore_dim = input_range < range_eps
12
+ input_range[ignore_dim] = output_max - output_min
13
+ scale = (output_max - output_min) / input_range
14
+ offset = output_min - scale * input_min
15
+ offset[ignore_dim] = (output_max + output_min) / 2 - input_min[ignore_dim]
16
+
17
+ return SingleFieldLinearNormalizer.create_manual(
18
+ scale=scale,
19
+ offset=offset,
20
+ input_stats_dict=stat
21
+ )
22
+
23
+ def get_image_range_normalizer():
24
+ scale = np.array([2], dtype=np.float32)
25
+ offset = np.array([-1], dtype=np.float32)
26
+ stat = {
27
+ 'min': np.array([0], dtype=np.float32),
28
+ 'max': np.array([1], dtype=np.float32),
29
+ 'mean': np.array([0.5], dtype=np.float32),
30
+ 'std': np.array([np.sqrt(1/12)], dtype=np.float32)
31
+ }
32
+ return SingleFieldLinearNormalizer.create_manual(
33
+ scale=scale,
34
+ offset=offset,
35
+ input_stats_dict=stat
36
+ )
37
+
38
+ def get_identity_normalizer_from_stat(stat):
39
+ scale = np.ones_like(stat['min'])
40
+ offset = np.zeros_like(stat['min'])
41
+ return SingleFieldLinearNormalizer.create_manual(
42
+ scale=scale,
43
+ offset=offset,
44
+ input_stats_dict=stat
45
+ )
46
+
47
+ def robomimic_abs_action_normalizer_from_stat(stat, rotation_transformer):
48
+ result = dict_apply_split(
49
+ stat, lambda x: {
50
+ 'pos': x[...,:3],
51
+ 'rot': x[...,3:6],
52
+ 'gripper': x[...,6:]
53
+ })
54
+
55
+ def get_pos_param_info(stat, output_max=1, output_min=-1, range_eps=1e-7):
56
+ # -1, 1 normalization
57
+ input_max = stat['max']
58
+ input_min = stat['min']
59
+ input_range = input_max - input_min
60
+ ignore_dim = input_range < range_eps
61
+ input_range[ignore_dim] = output_max - output_min
62
+ scale = (output_max - output_min) / input_range
63
+ offset = output_min - scale * input_min
64
+ offset[ignore_dim] = (output_max + output_min) / 2 - input_min[ignore_dim]
65
+
66
+ return {'scale': scale, 'offset': offset}, stat
67
+
68
+ def get_rot_param_info(stat):
69
+ example = rotation_transformer.forward(stat['mean'])
70
+ scale = np.ones_like(example)
71
+ offset = np.zeros_like(example)
72
+ info = {
73
+ 'max': np.ones_like(example),
74
+ 'min': np.full_like(example, -1),
75
+ 'mean': np.zeros_like(example),
76
+ 'std': np.ones_like(example)
77
+ }
78
+ return {'scale': scale, 'offset': offset}, info
79
+
80
+ def get_gripper_param_info(stat):
81
+ example = stat['max']
82
+ scale = np.ones_like(example)
83
+ offset = np.zeros_like(example)
84
+ info = {
85
+ 'max': np.ones_like(example),
86
+ 'min': np.full_like(example, -1),
87
+ 'mean': np.zeros_like(example),
88
+ 'std': np.ones_like(example)
89
+ }
90
+ return {'scale': scale, 'offset': offset}, info
91
+
92
+ pos_param, pos_info = get_pos_param_info(result['pos'])
93
+ rot_param, rot_info = get_rot_param_info(result['rot'])
94
+ gripper_param, gripper_info = get_gripper_param_info(result['gripper'])
95
+
96
+ param = dict_apply_reduce(
97
+ [pos_param, rot_param, gripper_param],
98
+ lambda x: np.concatenate(x,axis=-1))
99
+ info = dict_apply_reduce(
100
+ [pos_info, rot_info, gripper_info],
101
+ lambda x: np.concatenate(x,axis=-1))
102
+
103
+ return SingleFieldLinearNormalizer.create_manual(
104
+ scale=param['scale'],
105
+ offset=param['offset'],
106
+ input_stats_dict=info
107
+ )
108
+
109
+
110
+ def robomimic_abs_action_only_normalizer_from_stat(stat):
111
+ result = dict_apply_split(
112
+ stat, lambda x: {
113
+ 'pos': x[...,:3],
114
+ 'other': x[...,3:]
115
+ })
116
+
117
+ def get_pos_param_info(stat, output_max=1, output_min=-1, range_eps=1e-7):
118
+ # -1, 1 normalization
119
+ input_max = stat['max']
120
+ input_min = stat['min']
121
+ input_range = input_max - input_min
122
+ ignore_dim = input_range < range_eps
123
+ input_range[ignore_dim] = output_max - output_min
124
+ scale = (output_max - output_min) / input_range
125
+ offset = output_min - scale * input_min
126
+ offset[ignore_dim] = (output_max + output_min) / 2 - input_min[ignore_dim]
127
+
128
+ return {'scale': scale, 'offset': offset}, stat
129
+
130
+
131
+ def get_other_param_info(stat):
132
+ example = stat['max']
133
+ scale = np.ones_like(example)
134
+ offset = np.zeros_like(example)
135
+ info = {
136
+ 'max': np.ones_like(example),
137
+ 'min': np.full_like(example, -1),
138
+ 'mean': np.zeros_like(example),
139
+ 'std': np.ones_like(example)
140
+ }
141
+ return {'scale': scale, 'offset': offset}, info
142
+
143
+ pos_param, pos_info = get_pos_param_info(result['pos'])
144
+ other_param, other_info = get_other_param_info(result['other'])
145
+
146
+ param = dict_apply_reduce(
147
+ [pos_param, other_param],
148
+ lambda x: np.concatenate(x,axis=-1))
149
+ info = dict_apply_reduce(
150
+ [pos_info, other_info],
151
+ lambda x: np.concatenate(x,axis=-1))
152
+
153
+ return SingleFieldLinearNormalizer.create_manual(
154
+ scale=param['scale'],
155
+ offset=param['offset'],
156
+ input_stats_dict=info
157
+ )
158
+
159
+
160
+ def robomimic_abs_action_only_dual_arm_normalizer_from_stat(stat):
161
+ Da = stat['max'].shape[-1]
162
+ Dah = Da // 2
163
+ result = dict_apply_split(
164
+ stat, lambda x: {
165
+ 'pos0': x[...,:3],
166
+ 'other0': x[...,3:Dah],
167
+ 'pos1': x[...,Dah:Dah+3],
168
+ 'other1': x[...,Dah+3:]
169
+ })
170
+
171
+ def get_pos_param_info(stat, output_max=1, output_min=-1, range_eps=1e-7):
172
+ # -1, 1 normalization
173
+ input_max = stat['max']
174
+ input_min = stat['min']
175
+ input_range = input_max - input_min
176
+ ignore_dim = input_range < range_eps
177
+ input_range[ignore_dim] = output_max - output_min
178
+ scale = (output_max - output_min) / input_range
179
+ offset = output_min - scale * input_min
180
+ offset[ignore_dim] = (output_max + output_min) / 2 - input_min[ignore_dim]
181
+
182
+ return {'scale': scale, 'offset': offset}, stat
183
+
184
+
185
+ def get_other_param_info(stat):
186
+ example = stat['max']
187
+ scale = np.ones_like(example)
188
+ offset = np.zeros_like(example)
189
+ info = {
190
+ 'max': np.ones_like(example),
191
+ 'min': np.full_like(example, -1),
192
+ 'mean': np.zeros_like(example),
193
+ 'std': np.ones_like(example)
194
+ }
195
+ return {'scale': scale, 'offset': offset}, info
196
+
197
+ pos0_param, pos0_info = get_pos_param_info(result['pos0'])
198
+ pos1_param, pos1_info = get_pos_param_info(result['pos1'])
199
+ other0_param, other0_info = get_other_param_info(result['other0'])
200
+ other1_param, other1_info = get_other_param_info(result['other1'])
201
+
202
+ param = dict_apply_reduce(
203
+ [pos0_param, other0_param, pos1_param, other1_param],
204
+ lambda x: np.concatenate(x,axis=-1))
205
+ info = dict_apply_reduce(
206
+ [pos0_info, other0_info, pos1_info, other1_info],
207
+ lambda x: np.concatenate(x,axis=-1))
208
+
209
+ return SingleFieldLinearNormalizer.create_manual(
210
+ scale=param['scale'],
211
+ offset=param['offset'],
212
+ input_stats_dict=info
213
+ )
214
+
215
+
216
+ def array_to_stats(arr: np.ndarray):
217
+ stat = {
218
+ 'min': np.min(arr, axis=0),
219
+ 'max': np.max(arr, axis=0),
220
+ 'mean': np.mean(arr, axis=0),
221
+ 'std': np.std(arr, axis=0)
222
+ }
223
+ return stat
common/pose_trajectory_interpolator.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Union
2
+ import numbers
3
+ import numpy as np
4
+ import scipy.interpolate as si
5
+ import scipy.spatial.transform as st
6
+
7
+ def rotation_distance(a: st.Rotation, b: st.Rotation) -> float:
8
+ return (b * a.inv()).magnitude()
9
+
10
+ def pose_distance(start_pose, end_pose):
11
+ start_pose = np.array(start_pose)
12
+ end_pose = np.array(end_pose)
13
+ start_pos = start_pose[:3]
14
+ end_pos = end_pose[:3]
15
+ start_rot = st.Rotation.from_rotvec(start_pose[3:])
16
+ end_rot = st.Rotation.from_rotvec(end_pose[3:])
17
+ pos_dist = np.linalg.norm(end_pos - start_pos)
18
+ rot_dist = rotation_distance(start_rot, end_rot)
19
+ return pos_dist, rot_dist
20
+
21
+ class PoseTrajectoryInterpolator:
22
+ def __init__(self, times: np.ndarray, poses: np.ndarray):
23
+ assert len(times) >= 1
24
+ assert len(poses) == len(times)
25
+ if not isinstance(times, np.ndarray):
26
+ times = np.array(times)
27
+ if not isinstance(poses, np.ndarray):
28
+ poses = np.array(poses)
29
+
30
+ if len(times) == 1:
31
+ # special treatment for single step interpolation
32
+ self.single_step = True
33
+ self._times = times
34
+ self._poses = poses
35
+ else:
36
+ self.single_step = False
37
+ assert np.all(times[1:] >= times[:-1])
38
+
39
+ pos = poses[:,:3]
40
+ rot = st.Rotation.from_rotvec(poses[:,3:])
41
+
42
+ self.pos_interp = si.interp1d(times, pos,
43
+ axis=0, assume_sorted=True)
44
+ self.rot_interp = st.Slerp(times, rot)
45
+
46
+ @property
47
+ def times(self) -> np.ndarray:
48
+ if self.single_step:
49
+ return self._times
50
+ else:
51
+ return self.pos_interp.x
52
+
53
+ @property
54
+ def poses(self) -> np.ndarray:
55
+ if self.single_step:
56
+ return self._poses
57
+ else:
58
+ n = len(self.times)
59
+ poses = np.zeros((n, 6))
60
+ poses[:,:3] = self.pos_interp.y
61
+ poses[:,3:] = self.rot_interp(self.times).as_rotvec()
62
+ return poses
63
+
64
+ def trim(self,
65
+ start_t: float, end_t: float
66
+ ) -> "PoseTrajectoryInterpolator":
67
+ assert start_t <= end_t
68
+ times = self.times
69
+ should_keep = (start_t < times) & (times < end_t)
70
+ keep_times = times[should_keep]
71
+ all_times = np.concatenate([[start_t], keep_times, [end_t]])
72
+ # remove duplicates, Slerp requires strictly increasing x
73
+ all_times = np.unique(all_times)
74
+ # interpolate
75
+ all_poses = self(all_times)
76
+ return PoseTrajectoryInterpolator(times=all_times, poses=all_poses)
77
+
78
+ def drive_to_waypoint(self,
79
+ pose, time, curr_time,
80
+ max_pos_speed=np.inf,
81
+ max_rot_speed=np.inf
82
+ ) -> "PoseTrajectoryInterpolator":
83
+ assert(max_pos_speed > 0)
84
+ assert(max_rot_speed > 0)
85
+ time = max(time, curr_time)
86
+
87
+ curr_pose = self(curr_time)
88
+ pos_dist, rot_dist = pose_distance(curr_pose, pose)
89
+ pos_min_duration = pos_dist / max_pos_speed
90
+ rot_min_duration = rot_dist / max_rot_speed
91
+ duration = time - curr_time
92
+ duration = max(duration, max(pos_min_duration, rot_min_duration))
93
+ assert duration >= 0
94
+ last_waypoint_time = curr_time + duration
95
+
96
+ # insert new pose
97
+ trimmed_interp = self.trim(curr_time, curr_time)
98
+ times = np.append(trimmed_interp.times, [last_waypoint_time], axis=0)
99
+ poses = np.append(trimmed_interp.poses, [pose], axis=0)
100
+
101
+ # create new interpolator
102
+ final_interp = PoseTrajectoryInterpolator(times, poses)
103
+ return final_interp
104
+
105
+ def schedule_waypoint(self,
106
+ pose, time,
107
+ max_pos_speed=np.inf,
108
+ max_rot_speed=np.inf,
109
+ curr_time=None,
110
+ last_waypoint_time=None
111
+ ) -> "PoseTrajectoryInterpolator":
112
+ assert(max_pos_speed > 0)
113
+ assert(max_rot_speed > 0)
114
+ if last_waypoint_time is not None:
115
+ assert curr_time is not None
116
+
117
+ # trim current interpolator to between curr_time and last_waypoint_time
118
+ start_time = self.times[0]
119
+ end_time = self.times[-1]
120
+ assert start_time <= end_time
121
+
122
+ if curr_time is not None:
123
+ if time <= curr_time:
124
+ # if insert time is earlier than current time
125
+ # no effect should be done to the interpolator
126
+ return self
127
+ # now, curr_time < time
128
+ start_time = max(curr_time, start_time)
129
+
130
+ if last_waypoint_time is not None:
131
+ # if last_waypoint_time is earlier than start_time
132
+ # use start_time
133
+ if time <= last_waypoint_time:
134
+ end_time = curr_time
135
+ else:
136
+ end_time = max(last_waypoint_time, curr_time)
137
+ else:
138
+ end_time = curr_time
139
+
140
+ end_time = min(end_time, time)
141
+ start_time = min(start_time, end_time)
142
+ # end time should be the latest of all times except time
143
+ # after this we can assume order (proven by zhenjia, due to the 2 min operations)
144
+
145
+ # Constraints:
146
+ # start_time <= end_time <= time (proven by zhenjia)
147
+ # curr_time <= start_time (proven by zhenjia)
148
+ # curr_time <= time (proven by zhenjia)
149
+
150
+ # time can't change
151
+ # last_waypoint_time can't change
152
+ # curr_time can't change
153
+ assert start_time <= end_time
154
+ assert end_time <= time
155
+ if last_waypoint_time is not None:
156
+ if time <= last_waypoint_time:
157
+ assert end_time == curr_time
158
+ else:
159
+ assert end_time == max(last_waypoint_time, curr_time)
160
+
161
+ if curr_time is not None:
162
+ assert curr_time <= start_time
163
+ assert curr_time <= time
164
+
165
+ trimmed_interp = self.trim(start_time, end_time)
166
+ # after this, all waypoints in trimmed_interp is within start_time and end_time
167
+ # and is earlier than time
168
+
169
+ # determine speed
170
+ duration = time - end_time
171
+ end_pose = trimmed_interp(end_time)
172
+ pos_dist, rot_dist = pose_distance(pose, end_pose)
173
+ pos_min_duration = pos_dist / max_pos_speed
174
+ rot_min_duration = rot_dist / max_rot_speed
175
+ duration = max(duration, max(pos_min_duration, rot_min_duration))
176
+ assert duration >= 0
177
+ last_waypoint_time = end_time + duration
178
+
179
+ # insert new pose
180
+ times = np.append(trimmed_interp.times, [last_waypoint_time], axis=0)
181
+ poses = np.append(trimmed_interp.poses, [pose], axis=0)
182
+
183
+ # create new interpolator
184
+ final_interp = PoseTrajectoryInterpolator(times, poses)
185
+ return final_interp
186
+
187
+
188
+ def __call__(self, t: Union[numbers.Number, np.ndarray]) -> np.ndarray:
189
+ is_single = False
190
+ if isinstance(t, numbers.Number):
191
+ is_single = True
192
+ t = np.array([t])
193
+
194
+ pose = np.zeros((len(t), 6))
195
+ if self.single_step:
196
+ pose[:] = self._poses[0]
197
+ else:
198
+ start_time = self.times[0]
199
+ end_time = self.times[-1]
200
+ t = np.clip(t, start_time, end_time)
201
+
202
+ pose = np.zeros((len(t), 6))
203
+ pose[:,:3] = self.pos_interp(t)
204
+ pose[:,3:] = self.rot_interp(t).as_rotvec()
205
+
206
+ if is_single:
207
+ pose = pose[0]
208
+ return pose
common/precise_sleep.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+
3
+ def precise_sleep(dt: float, slack_time: float=0.001, time_func=time.monotonic):
4
+ """
5
+ Use hybrid of time.sleep and spinning to minimize jitter.
6
+ Sleep dt - slack_time seconds first, then spin for the rest.
7
+ """
8
+ t_start = time_func()
9
+ if dt > slack_time:
10
+ time.sleep(dt - slack_time)
11
+ t_end = t_start + dt
12
+ while time_func() < t_end:
13
+ pass
14
+ return
15
+
16
+ def precise_wait(t_end: float, slack_time: float=0.001, time_func=time.monotonic):
17
+ t_start = time_func()
18
+ t_wait = t_end - t_start
19
+ if t_wait > 0:
20
+ t_sleep = t_wait - slack_time
21
+ if t_sleep > 0:
22
+ time.sleep(t_sleep)
23
+ while time_func() < t_end:
24
+ pass
25
+ return
common/pref_replay_buffer.py ADDED
@@ -0,0 +1,365 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Union, Dict, Optional
2
+ import os
3
+ import math
4
+ import numbers
5
+ import zarr
6
+ import numcodecs
7
+ import numpy as np
8
+ from functools import cached_property
9
+
10
+ def check_chunks_compatible(chunks: tuple, shape: tuple):
11
+ assert len(shape) == len(chunks)
12
+ for c in chunks:
13
+ assert isinstance(c, numbers.Integral)
14
+ assert c > 0
15
+
16
+ def rechunk_recompress_array(group, name,
17
+ chunks=None, chunk_length=None,
18
+ compressor=None, tmp_key='_temp'):
19
+ old_arr = group[name]
20
+ if chunks is None:
21
+ if chunk_length is not None:
22
+ chunks = (chunk_length,) + old_arr.chunks[1:]
23
+ else:
24
+ chunks = old_arr.chunks
25
+ check_chunks_compatible(chunks, old_arr.shape)
26
+
27
+ if compressor is None:
28
+ compressor = old_arr.compressor
29
+
30
+ if (chunks == old_arr.chunks) and (compressor == old_arr.compressor):
31
+ # no change
32
+ return old_arr
33
+
34
+ # rechunk recompress
35
+ group.move(name, tmp_key)
36
+ old_arr = group[tmp_key]
37
+ n_copied, n_skipped, n_bytes_copied = zarr.copy(
38
+ source=old_arr,
39
+ dest=group,
40
+ name=name,
41
+ chunks=chunks,
42
+ compressor=compressor,
43
+ )
44
+ del group[tmp_key]
45
+ arr = group[name]
46
+ return arr
47
+
48
+ def get_optimal_chunks(shape, dtype,
49
+ target_chunk_bytes=2e6,
50
+ max_chunk_length=None):
51
+ """
52
+ Common shapes
53
+ T,D
54
+ T,N,D
55
+ T,H,W,C
56
+ T,N,H,W,C
57
+ """
58
+ itemsize = np.dtype(dtype).itemsize
59
+ # reversed
60
+ rshape = list(shape[::-1])
61
+ if max_chunk_length is not None:
62
+ rshape[-1] = int(max_chunk_length)
63
+ split_idx = len(shape)-1
64
+ for i in range(len(shape)-1):
65
+ this_chunk_bytes = itemsize * np.prod(rshape[:i])
66
+ next_chunk_bytes = itemsize * np.prod(rshape[:i+1])
67
+ if this_chunk_bytes <= target_chunk_bytes \
68
+ and next_chunk_bytes > target_chunk_bytes:
69
+ split_idx = i
70
+
71
+ rchunks = rshape[:split_idx]
72
+ item_chunk_bytes = itemsize * np.prod(rshape[:split_idx])
73
+ this_max_chunk_length = rshape[split_idx]
74
+ next_chunk_length = min(this_max_chunk_length, math.ceil(
75
+ target_chunk_bytes / item_chunk_bytes))
76
+ rchunks.append(next_chunk_length)
77
+ len_diff = len(shape) - len(rchunks)
78
+ rchunks.extend([1] * len_diff)
79
+ chunks = tuple(rchunks[::-1])
80
+ # print(np.prod(chunks) * itemsize / target_chunk_bytes)
81
+ return chunks
82
+
83
+
84
+ class PrefReplayBuffer:
85
+ """
86
+ Zarr-based temporal data structure specifically for preference dataset.
87
+ Stores pairs of trajectories (observations, actions) along with votes.
88
+ """
89
+
90
+ def __init__(self, root: Union[zarr.Group, Dict[str, dict]]):
91
+ """
92
+ Initialize the preference replay buffer. Use class methods to create or load buffers.
93
+ """
94
+ assert 'data' in root
95
+ assert 'meta' in root
96
+ for key, value in root['data'].items():
97
+ assert value.shape[0] == root['meta']['votes'].shape[0]
98
+ self.root = root
99
+
100
+ # ============= create constructors ===============
101
+ @classmethod
102
+ def create_empty_zarr(cls, storage=None, root=None):
103
+ if root is None:
104
+ if storage is None:
105
+ storage = zarr.MemoryStore()
106
+ root = zarr.group(store=storage)
107
+ data = root.require_group('data', overwrite=False)
108
+ meta = root.require_group('meta', overwrite=False)
109
+
110
+ if 'votes' not in meta:
111
+ votes = meta.zeros('votes', shape=(0,), dtype=np.float32, compressor=None, overwrite=False)
112
+ if 'votes_2' not in meta:
113
+ votes_2 = meta.zeros('votes_2', shape=(0,), dtype=np.float32, compressor=None, overwrite=False)
114
+
115
+ return cls(root=root)
116
+
117
+ @classmethod
118
+ def create_empty_numpy(cls):
119
+ root = {
120
+ 'data': dict(),
121
+ 'meta': {
122
+ 'episode_ends': np.zeros((0,), dtype=np.int64),
123
+ 'votes': np.zeros((0,), dtype=np.float32),
124
+ 'votes_2': np.zeros((0,), dtype=np.float32),
125
+ 'length': np.zeros((0,), dtype=np.int64),
126
+ 'length_2': np.zeros((0,), dtype=np.int64),
127
+ 'beta_priori': np.zeros((0,), dtype=np.float32),
128
+ 'beta_priori_2': np.zeros((0,), dtype=np.float32),
129
+ }
130
+ }
131
+ return cls(root=root)
132
+
133
+ @classmethod
134
+ def create_from_group(cls, group, **kwargs):
135
+ if 'data' not in group:
136
+ # create from scratch
137
+ buffer = cls.create_empty_zarr(root=group, **kwargs)
138
+ else:
139
+ # already exists
140
+ buffer = cls(root=group, **kwargs)
141
+ return buffer
142
+
143
+ @classmethod
144
+ def create_from_path(cls, zarr_path, mode='r', **kwargs):
145
+ """
146
+ Open a Zarr file from disk for large datasets that cannot fit in memory.
147
+ """
148
+ group = zarr.open(zarr_path, mode=mode)
149
+ return cls.create_from_group(group=group, **kwargs)
150
+
151
+ # ============= Add episodes ===============
152
+ def add_pref_episode(self, data: Dict[str, np.ndarray],
153
+ meta_data: Optional[Dict[str, Union[np.ndarray, int]]] = None,
154
+ chunks: Optional[Dict[str, tuple]] = dict(),
155
+ compressors: Union[str, numcodecs.abc.Codec, dict] = dict()):
156
+ """
157
+ Add a pair of episodes (obs/action for each trajectory) along with metadata (votes).
158
+ """
159
+ assert 'obs' in data and 'obs_2' in data, "obs and obs_2 keys are required"
160
+ assert 'action' in data and 'action_2' in data, "action and action_2 keys are required"
161
+
162
+ is_zarr = isinstance(self.root, zarr.Group)
163
+ curr_len = len(self.root['meta']['votes'])
164
+ episode_length = len(data['obs'])
165
+ new_len = curr_len + 1
166
+
167
+ # Add trajectory 1
168
+ for key in ['obs', 'action']:
169
+ # Create the new shape to accommodate all time steps
170
+ value = data[key]
171
+ new_shape = (new_len,) + (episode_length,) + data[key].shape[1:] # This will set (new_len, T, dim)
172
+
173
+ if key not in self.root['data']:
174
+ # Create a new array if it doesn't exist
175
+ if is_zarr:
176
+ cks = self._resolve_array_chunks(chunks, key, data[key])
177
+ cpr = self._resolve_array_compressor(compressors, key, data[key])
178
+ arr = self.root['data'].zeros(name=key, shape=new_shape, chunks=cks, dtype=data[key].dtype, compressor=cpr)
179
+ else:
180
+ arr = np.zeros(new_shape, dtype=data[key].dtype)
181
+ self.root['data'][key] = arr
182
+ else:
183
+ arr = self.root['data'][key]
184
+ if is_zarr:
185
+ arr.resize(new_shape)
186
+ else:
187
+ arr.resize(new_shape, refcheck=False)
188
+
189
+ # Store the full sequence, adjusting the shape to match the time steps in data[key]
190
+ arr[new_len-1, -value.shape[0]:, :] = value # Now this assumes data[key] has shape (T, dim)
191
+
192
+
193
+ # Add trajectory 2 (obs_2, action_2)
194
+ for key in ['obs_2', 'action_2']:
195
+ value = data[key]
196
+ # Create the new shape to accommodate all time steps
197
+ new_shape = (new_len,) + (episode_length,) + data[key].shape[1:] # This will set (new_len, T, dim)
198
+
199
+ if key not in self.root['data']:
200
+ # Create a new array if it doesn't exist
201
+ if is_zarr:
202
+ cks = self._resolve_array_chunks(chunks, key, data[key])
203
+ cpr = self._resolve_array_compressor(compressors, key, data[key])
204
+ arr = self.root['data'].zeros(name = key, shape=new_shape, chunks=cks, dtype=data[key].dtype, compressor=cpr)
205
+ else:
206
+ arr = np.zeros(new_shape, dtype=data[key].dtype)
207
+ self.root['data'][key] = arr
208
+ else:
209
+ arr = self.root['data'][key]
210
+ if is_zarr:
211
+ arr.resize(new_shape)
212
+ else:
213
+ arr.resize(new_shape, refcheck=False)
214
+
215
+ # Store the full sequence, adjusting the shape to match the time steps in data[key]
216
+ arr[new_len-1, -value.shape[0]:, :] = value # Now this assumes data[key] has shape (T, dim)
217
+
218
+ # Add votes to meta
219
+ if meta_data:
220
+ for key in ['votes', 'votes_2']:
221
+ new_shape = (new_len,) + (1,)
222
+ if key not in self.root['meta']:
223
+ if is_zarr:
224
+ self.root['meta'].zeros(name=key, shape=new_shape, chunks=new_shape, dtype=np.float32)
225
+ else:
226
+ self.root['meta'][key] = np.zeros(new_shape, dtype=np.float32)
227
+ arr = self.root['meta'][key]
228
+ if is_zarr:
229
+ arr.resize(new_shape)
230
+ else:
231
+ arr.resize(new_shape, refcheck=False)
232
+ arr[new_len-1] = meta_data[key]
233
+
234
+ for key in ['length', 'length_2']:
235
+ new_shape = (new_len,) + (1,)
236
+ if key not in self.root['meta']:
237
+ if is_zarr:
238
+ self.root['meta'].zeros(name=key, shape=new_shape, chunks=new_shape, dtype=np.float32)
239
+ else:
240
+ self.root['meta'][key] = np.zeros(new_shape, dtype=np.float32)
241
+ arr = self.root['meta'][key]
242
+ if is_zarr:
243
+ arr.resize(new_shape)
244
+ else:
245
+ arr.resize(new_shape, refcheck=False)
246
+ arr[new_len-1] = meta_data[key]
247
+
248
+ for key in ['beta_priori', 'beta_priori_2']:
249
+ new_shape = (new_len,) + (2,)
250
+ if key not in self.root['meta']:
251
+ if is_zarr:
252
+ self.root['meta'].zeros(name=key, shape=new_shape, chunks=new_shape, dtype=np.float32)
253
+ else:
254
+ self.root['meta'][key] = np.zeros(new_shape, dtype=np.float32)
255
+ arr = self.root['meta'][key]
256
+ if is_zarr:
257
+ arr.resize(new_shape)
258
+ else:
259
+ arr.resize(new_shape, refcheck=False)
260
+ arr[new_len-1] = meta_data[key]
261
+
262
+ # ============= Get episodes ===============
263
+ def get_pref_episode(self, idx: int, copy: bool = False):
264
+ """
265
+ Get a pair of episodes by index, including observation and action sequences for both trajectories.
266
+ """
267
+ if copy:
268
+ return {
269
+ 'obs': self.root['data']['obs'][idx].copy(),
270
+ 'action': self.root['data']['action'][idx].copy(),
271
+ 'obs_2': self.root['data']['obs_2'][idx].copy(),
272
+ 'action_2': self.root['data']['action_2'][idx].copy(),
273
+ 'votes': self.root['meta']['votes'][idx].copy(),
274
+ 'votes_2': self.root['meta']['votes_2'][idx].copy(),
275
+ 'length': self.root['meta']['length'][idx].copy(),
276
+ 'length_2': self.root['meta']['length_2'][idx].copy(),
277
+ 'beta_priori': self.root['meta']['beta_priori'][idx].copy(),
278
+ 'beta_priori_2': self.root['meta']['beta_priori_2'][idx].copy(),
279
+ }
280
+ else:
281
+ return {
282
+ 'obs': self.root['data']['obs'][idx],
283
+ 'action': self.root['data']['action'][idx],
284
+ 'obs_2': self.root['data']['obs_2'][idx],
285
+ 'action_2': self.root['data']['action_2'][idx],
286
+ 'votes': self.root['meta']['votes'][idx],
287
+ 'votes_2': self.root['meta']['votes_2'][idx],
288
+ 'length': self.root['meta']['length'][idx],
289
+ 'length_2': self.root['meta']['length_2'][idx],
290
+ 'beta_priori': self.root['meta']['beta_priori'][idx],
291
+ 'beta_priori_2': self.root['meta']['beta_priori_2'][idx],
292
+ }
293
+
294
+ def get_episode_slice(self, idx):
295
+ """
296
+ Get the slice range for an episode based on the index for slicing observation and action arrays.
297
+ """
298
+ return slice(idx, idx + 1)
299
+
300
+ # ============= Save methods ===============
301
+ def save_to_store(self, store, chunks: Optional[Dict[str, tuple]] = dict(),
302
+ compressors: Union[str, numcodecs.abc.Codec, dict] = dict(),
303
+ if_exists='replace', **kwargs):
304
+ """
305
+ Save the replay buffer to the given store with optional chunking and compression.
306
+ """
307
+ root = zarr.group(store=store)
308
+ # Save data and meta with chunking and compression if provided
309
+ for key, value in self.root['data'].items():
310
+ cks = self._resolve_array_chunks(chunks, key, value)
311
+ cpr = self._resolve_array_compressor(compressors, key, value)
312
+ arr = self.root['data'][key]
313
+ zarr.copy(arr, root['data'], name=key, chunks=cks, compressor=cpr)
314
+
315
+ # Save meta
316
+ for key, value in self.root['meta'].items():
317
+ zarr.copy(value, root['meta'], name=key)
318
+
319
+ def save_to_path(self, zarr_path, chunks: Optional[Dict[str, tuple]] = dict(),
320
+ compressors: Union[str, numcodecs.abc.Codec, dict] = dict(),
321
+ if_exists='replace', **kwargs):
322
+ """
323
+ Save the replay buffer to a path.
324
+ """
325
+ store = zarr.DirectoryStore(zarr_path)
326
+ return self.save_to_store(store, chunks=chunks, compressors=compressors, if_exists=if_exists, **kwargs)
327
+
328
+ # ============= Helper methods ===============
329
+ @classmethod
330
+ def _resolve_array_chunks(cls,
331
+ chunks: Union[dict, tuple], key, array):
332
+ cks = None
333
+ if isinstance(chunks, dict):
334
+ if key in chunks:
335
+ cks = chunks[key]
336
+ elif isinstance(array, zarr.Array):
337
+ cks = array.chunks
338
+ elif isinstance(chunks, tuple):
339
+ cks = chunks
340
+ else:
341
+ raise TypeError(f"Unsupported chunks type {type(chunks)}")
342
+ # backup default
343
+ if cks is None:
344
+ cks = get_optimal_chunks(shape=array.shape, dtype=array.dtype)
345
+ # check
346
+ check_chunks_compatible(chunks=cks, shape=array.shape)
347
+ return cks
348
+
349
+ @classmethod
350
+ def _resolve_array_compressor(cls, compressors: Union[dict, str, numcodecs.abc.Codec], key, array):
351
+ # Resolves the compressor for the array
352
+ return compressors.get(key, array.compressor if isinstance(array, zarr.Array) else numcodecs.Blosc())
353
+
354
+ @property
355
+ def n_steps(self):
356
+ return len(self.root['meta']['votes'])
357
+
358
+ @property
359
+ def data(self):
360
+ return self.root['data']
361
+
362
+ @property
363
+ def meta(self):
364
+ return self.root['meta']
365
+
common/pref_sampler.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional, Dict
2
+ import numpy as np
3
+ from diffusion_policy.common.pref_replay_buffer import PrefReplayBuffer
4
+ import torch
5
+
6
+
7
+ def get_val_mask(n_episodes, val_ratio, seed=0):
8
+ val_mask = np.zeros(n_episodes, dtype=bool)
9
+ if val_ratio <= 0:
10
+ return val_mask
11
+
12
+ # have at least 1 episode for validation, and at least 1 episode for train
13
+ n_val = min(max(1, round(n_episodes * val_ratio)), n_episodes-1)
14
+ rng = np.random.default_rng(seed=seed)
15
+ val_idxs = rng.choice(n_episodes, size=n_val, replace=False)
16
+ val_mask[val_idxs] = True
17
+ return val_mask
18
+
19
+
20
+ class PrefSequenceSampler:
21
+ def __init__(self,
22
+ replay_buffer: PrefReplayBuffer,
23
+ sequence_length: int,
24
+ episode_mask: Optional[np.ndarray]=None,
25
+ keys: Optional[Dict[str, int]] = None,
26
+ ):
27
+ """
28
+ Initializes a sampler for the preference replay buffer.
29
+
30
+ Parameters:
31
+ - replay_buffer: PrefReplayBuffer instance from which to sample data.
32
+ - sequence_length: The length of sequences to sample.
33
+ - pad_before, pad_after: Padding before and after sequences (optional).
34
+ - keys: Optional dictionary to specify specific keys and limits on how much data to load.
35
+ - episode_mask: Mask indicating valid episodes for sampling.
36
+ """
37
+ super().__init__()
38
+ assert sequence_length >= 1
39
+
40
+ if keys is None:
41
+ keys = list(replay_buffer.data.keys())
42
+
43
+ # Store generated indices
44
+ self.keys = keys
45
+ self.sequence_length = sequence_length
46
+ self.replay_buffer = replay_buffer
47
+ self.episode_mask = episode_mask
48
+
49
+ def __len__(self):
50
+
51
+ return np.sum(self.episode_mask)
52
+
53
+ def sample_sequence(self, idx: int) -> Dict[str, np.ndarray]:
54
+ """
55
+ Samples the sequence of data based on the provided index (idx).
56
+
57
+ Parameters:
58
+ - idx: The index from which to sample an episode sequence.
59
+
60
+ Returns:
61
+ - A dictionary containing the sampled data for the specified keys and votes.
62
+ """
63
+ indices = np.where(self.episode_mask)[0]
64
+ result = self.replay_buffer.get_pref_episode(indices[idx])
65
+
66
+ for key in result:
67
+ value = result[key]
68
+ if isinstance(value, np.ndarray):
69
+ result[key] = torch.from_numpy(value)
70
+ elif isinstance(value, (np.float32, np.float64, float, int)):
71
+ result[key] = torch.tensor(value, dtype=torch.float32)
72
+ else:
73
+ raise TypeError(f"Unsupported type {type(value)} for key '{key}'")
74
+ return result
common/prior_utils_confidence.py ADDED
@@ -0,0 +1,723 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import collections
2
+ import numpy as np
3
+ # import gym
4
+ import torch.utils
5
+ from tqdm import trange
6
+ import torch
7
+ import torch.nn as nn
8
+ import math
9
+ # import d4rl
10
+ from tqdm import tqdm
11
+ import torch.nn.functional as F
12
+ from pathlib import Path
13
+ import copy
14
+ from torch.optim.lr_scheduler import SequentialLR, LinearLR, CosineAnnealingLR
15
+ from diffusion_policy.policy.diffusion_transformer_hybrid_image_policy import DiffusionTransformerHybridImagePolicy
16
+ from diffusion_policy.common.pytorch_util import dict_apply
17
+
18
+ import time
19
+ import logging
20
+ from datetime import datetime
21
+
22
+ Batch = collections.namedtuple(
23
+ 'Batch',
24
+ ['observations', 'actions', 'rewards', 'masks', 'next_observations'])
25
+
26
+
27
+ def gen_net(in_size=1, out_size=1, H=128, n_layers=3, activation='tanh'):
28
+ net = []
29
+ for i in range(n_layers):
30
+ net.append(nn.Linear(in_size, H))
31
+ net.append(nn.LeakyReLU())
32
+ in_size = H
33
+ net.append(nn.Linear(in_size, out_size))
34
+ if activation == 'tanh':
35
+ net.append(nn.Tanh())
36
+ elif activation == 'sig':
37
+ net.append(nn.Sigmoid())
38
+ else:
39
+ pass
40
+
41
+ return net
42
+
43
+ def get_discrete_traj(trajectory, dim=2):
44
+ discrete_traj = np.round(trajectory[:, :, :dim].astype(float)).astype(int)
45
+
46
+ unique_states_per_trajectory = []
47
+ for trajectory in discrete_traj:
48
+ # Find unique states in the current trajectory
49
+ unique_states = np.unique(trajectory, axis=0)
50
+ # Convert the trajectory to a hashable type
51
+ unique_states_per_trajectory.append(tuple(map(tuple, unique_states)))
52
+ return unique_states_per_trajectory
53
+
54
+ def init_trajectory_dict(discrete_trajectory):
55
+
56
+ trajectory_dict = {}
57
+ for trajectory in discrete_trajectory:
58
+ if trajectory in trajectory_dict:
59
+ continue
60
+ else:
61
+ trajectory_dict[trajectory] = np.array([1,1])
62
+ return trajectory_dict
63
+
64
+ def get_trajectory_dict_from_pair(traj_alpha_beta_dict, discrete_obs_1, discrete_obs_2, single_labels):
65
+ for i in range(len(single_labels)):
66
+ if single_labels[i] == 1:
67
+ traj_alpha_beta_dict[discrete_obs_1[i]][0] += 1
68
+ traj_alpha_beta_dict[discrete_obs_2[i]][1] += 1
69
+ elif single_labels[i] == -1:
70
+ traj_alpha_beta_dict[discrete_obs_1[i]][1] += 1
71
+ traj_alpha_beta_dict[discrete_obs_2[i]][0] += 1
72
+ # TODO how to treat not compaied ones?
73
+ elif single_labels[i] == 0:
74
+ traj_alpha_beta_dict[discrete_obs_1[i]][0] += 1
75
+ traj_alpha_beta_dict[discrete_obs_1[i]][1] += 1
76
+ traj_alpha_beta_dict[discrete_obs_2[i]][0] += 1
77
+ traj_alpha_beta_dict[discrete_obs_2[i]][1] += 1
78
+ return traj_alpha_beta_dict
79
+
80
+ def to_torch(x, dtype=torch.float32):
81
+ if isinstance(x, np.ndarray):
82
+ return torch.from_numpy(x).to(dtype)
83
+
84
+ def index_batch(batch, indices):
85
+ indexed = {}
86
+ for key in batch.keys():
87
+ indexed[key] = batch[key][indices, ...]
88
+ return indexed
89
+
90
+ class Dataset(object):
91
+ def __init__(self, observations: np.ndarray, actions: np.ndarray,
92
+ rewards: np.ndarray, masks: np.ndarray,
93
+ dones_float: np.ndarray, next_observations: np.ndarray,
94
+ size: int):
95
+ self.observations = observations
96
+ self.actions = actions
97
+ self.rewards = rewards
98
+ self.masks = masks
99
+ self.dones_float = dones_float
100
+ self.next_observations = next_observations
101
+ self.size = size
102
+
103
+ def sample(self, batch_size: int) -> Batch:
104
+ indx = np.random.randint(self.size, size=batch_size)
105
+ return Batch(observations=self.observations[indx],
106
+ actions=self.actions[indx],
107
+ rewards=self.rewards[indx],
108
+ masks=self.masks[indx],
109
+ next_observations=self.next_observations[indx])
110
+
111
+ class NormalComparisonModel(nn.Module):
112
+ def __init__(self, input_dim, dense_units, dropout_rate, device):
113
+ super(NormalComparisonModel, self).__init__()
114
+
115
+ # Fully connected layers for classification
116
+ self.fc1 = nn.Linear(4 * input_dim, dense_units)
117
+ self.dropout = nn.Dropout(dropout_rate)
118
+ self.fc2 = nn.Linear(dense_units, dense_units // 2)
119
+ self.output = nn.Linear(dense_units // 2, 1)
120
+
121
+ # Device setup
122
+ self.device = device
123
+ self.to(device)
124
+
125
+ def forward(self, f1, f2):
126
+ f1, f2 = f1.to(self.device), f2.to(self.device)
127
+
128
+ # Shape of f1: (N1, L1, D), f2: (N2, L2, D)
129
+ N1, L1, D = f1.shape
130
+ N2, L2, _ = f2.shape
131
+
132
+ # Expand dimensions for pairwise comparison
133
+ f1_expanded = f1.unsqueeze(1).expand(N1, N2, L1, D) # Shape: (N1, N2, L1, D)
134
+ f2_expanded = f2.unsqueeze(0).expand(N1, N2, L2, D) # Shape: (N1, N2, L2, D)
135
+
136
+ # Flatten for processing through Transformer
137
+ f1_flat = f1_expanded.reshape(-1, L1, D) # Shape: (N1*N2, L1, D)
138
+ f2_flat = f2_expanded.reshape(-1, L2, D) # Shape: (N1*N2, L2, D)
139
+
140
+ # Sequence pooling: Reduce sequence dimension
141
+ f1_pooled = f1_flat.mean(dim=1)
142
+ f2_pooled = f2_flat.mean(dim=1)
143
+
144
+ # Pairwise comparison (concatenate, subtract)
145
+ combined_features = torch.cat([
146
+ f1_pooled, # Reduced f1
147
+ f2_pooled, # Reduced f2
148
+ f1_pooled - f2_pooled, # Difference
149
+ ], dim=-1) # Shape: (N1*N2, 3*D)
150
+
151
+ # Fully connected layers
152
+ x = F.gelu(self.fc1(combined_features)) # Shape: (N1*N2, dense_units)
153
+ x = self.dropout(x)
154
+ x = F.gelu(self.fc2(x)) # Shape: (N1*N2, dense_units // 2)
155
+ x = self.output(x) # Shape: (N1*N2, 1)
156
+
157
+ # Sigmoid activation for [0, 1] output
158
+ output = torch.sigmoid(x).squeeze(-1) # Shape: (N1*N2)
159
+ output = output.view(N1, N2) # Reshape to (N1, N2)
160
+ return output
161
+
162
+ class AttentionComparisonModel(nn.Module):
163
+ def __init__(self, input_dim, dropout_rate, nhead, device):
164
+ super(AttentionComparisonModel, self).__init__()
165
+
166
+ self.attention = nn.MultiheadAttention(embed_dim=input_dim, num_heads=nhead, batch_first=True)
167
+ self.query_attn = nn.Parameter(torch.randn(1, input_dim)) # Learnable query for attention
168
+
169
+ self.fc1 = nn.Linear(input_dim, input_dim // 2)
170
+ self.dropout = nn.Dropout(dropout_rate)
171
+ self.output = nn.Linear(input_dim // 2, 1)
172
+ self.device = device
173
+ self.to(device)
174
+
175
+ def attention_pool(self, x, query):
176
+ # x: (batch_size, seq_len, input_dim)
177
+ # query: (1, input_dim)
178
+ attn_weights = torch.softmax(torch.matmul(x, query.T), dim=1) # Compute attention weights
179
+ weighted_sum = torch.sum(attn_weights * x, dim=1) # Weighted sum along sequence dimension
180
+ return weighted_sum
181
+
182
+ def forward(self, f1, f2):
183
+ f1, f2 = f1.to(self.device), f2.to(self.device)
184
+
185
+ N1, L1, D = f1.shape
186
+ N2, L2, _ = f2.shape
187
+
188
+ f1_flat = f1.unsqueeze(1).expand(N1, N2, L1, D).reshape(-1, L1, D)
189
+ f2_flat = f2.unsqueeze(0).expand(N1, N2, L2, D).reshape(-1, L2, D)
190
+
191
+ attn_out, _ = self.attention(f1_flat, f2_flat, f2_flat)
192
+
193
+ attn_reduced = self.attention_pool(attn_out, self.query_attn)
194
+
195
+ x = F.gelu(self.fc1(attn_reduced))
196
+ x = self.dropout(x)
197
+ x = self.output(x)
198
+ output = torch.sigmoid(x)
199
+ output = output.view(N1, N2)
200
+ return output
201
+
202
+ def one_to_one_forward(self, f1, f2):
203
+ f1, f2 = f1.to(self.device), f2.to(self.device)
204
+
205
+ attn_out, _ = self.attention(f1, f2, f2)
206
+
207
+ # Apply self-attention weighted compression
208
+ attn_reduced = self.attention_pool(attn_out, self.query_attn)
209
+
210
+ x = F.gelu(self.fc1(attn_reduced))
211
+ x = self.dropout(x)
212
+ x = self.output(x)
213
+ output = torch.sigmoid(x)
214
+ return output
215
+
216
+
217
+ class SinusoidalPosEmb(nn.Module):
218
+ def __init__(self, dim):
219
+ super().__init__()
220
+ self.dim = dim
221
+
222
+ def forward(self, x):
223
+ device = x.device
224
+ half_dim = self.dim // 2
225
+ emb = math.log(10000) / (half_dim - 1)
226
+ emb = torch.exp(torch.arange(half_dim, device=device) * -emb)
227
+ emb = x[:, None] * emb[None, :]
228
+ emb = torch.cat((emb.sin(), emb.cos()), dim=-1)
229
+ return emb
230
+
231
+ class TransformerEncModel(nn.Module):
232
+ def __init__(self, data_dim, embedding_dim, nhead, num_encoder_layers, device):
233
+ super(TransformerEncModel, self).__init__()
234
+ self.device = device
235
+ self.embedding = nn.Linear(data_dim, embedding_dim)
236
+ # self.pos_encoder = self.create_positional_encoding(seq_length, embedding_dim)
237
+ self.pos_emb = SinusoidalPosEmb(embedding_dim)
238
+ encoder_layers = nn.TransformerEncoderLayer(d_model=embedding_dim, nhead=nhead, batch_first=True)
239
+ self.transformer_encoder = nn.TransformerEncoder(encoder_layers, num_layers=num_encoder_layers)
240
+
241
+ def forward(self, x):
242
+ traj_len = x.shape[1]
243
+ x = self.embedding(x) # Map input to embedding dimension
244
+ pos = self.pos_emb(
245
+ torch.arange(traj_len, device=self.device))[None,]
246
+ x += pos # Add positional encoding
247
+ output = self.transformer_encoder(x)
248
+ return output
249
+
250
+ class CausalTransformerBetaModel(nn.Module):
251
+ def __init__(self, data_dim, embedding_dim, nhead, num_encoder_layers, output_dim, device):
252
+ super(CausalTransformerBetaModel, self).__init__()
253
+ self.device = device
254
+ self.embedding = nn.Linear(data_dim, embedding_dim)
255
+ # self.pos_encoder = self.create_positional_encoding(seq_length, embedding_dim)
256
+ self.pos_emb = SinusoidalPosEmb(embedding_dim)
257
+ encoder_layers = nn.TransformerEncoderLayer(d_model=embedding_dim, nhead=nhead, batch_first=True)
258
+ self.transformer_encoder = nn.TransformerEncoder(encoder_layers, num_layers=num_encoder_layers)
259
+ self.output_layer = nn.Linear(embedding_dim, output_dim)
260
+ self.softplus = nn.Softplus()
261
+
262
+ def generate_square_subsequent_mask(self, sz):
263
+ mask = torch.triu(torch.ones(sz, sz), diagonal=1)
264
+ return mask.masked_fill(mask==1, float('-inf'))
265
+
266
+ def forward(self, x):
267
+ traj_len = x.shape[1]
268
+ x = self.embedding(x) # Map input to embedding dimension
269
+ pos = self.pos_emb(
270
+ torch.arange(traj_len, device=self.device))[None,]
271
+ x += pos # Add positional encoding
272
+ mask = self.generate_square_subsequent_mask(traj_len).to(x.device)
273
+ output = self.transformer_encoder(x, mask=mask)
274
+ output = self.output_layer(output)
275
+ # TODO mean or last one
276
+ # output = output.mean(dim=1) # Aggregate across the sequence dimension
277
+ output = output[:, -1, :] # Take the output from the last timestep
278
+ output = self.softplus(output)
279
+ return output
280
+
281
+ class BetaNetwork(nn.Module):
282
+ def __init__(self, data, device=torch.device('cuda'), data_size = 500):
283
+ super(BetaNetwork, self).__init__()
284
+
285
+ act_data = np.concatenate((data['action'], data['action_2']), axis=0)
286
+ obs_data = np.concatenate((data['obs'], data['obs_2']), axis=0)
287
+ votes_data = np.concatenate((data['votes'], data['votes_2']), axis=0)
288
+
289
+ if data_size <= obs_data.shape[0]:
290
+ indices = np.random.randint(0, obs_data.shape[0], size=data_size)
291
+ obs_data = obs_data[indices, ...]
292
+ act_data = act_data[indices, ...]
293
+ votes_data = votes_data[indices, ...]
294
+
295
+ act_data = torch.from_numpy(act_data).float().to(device)
296
+ obs_data = torch.from_numpy(obs_data).float().to(device)
297
+ self.votes_data = torch.from_numpy(votes_data).to(device)
298
+ self.lr = None
299
+ self.device = device
300
+ self.data = torch.concat((obs_data, act_data), dim=-1)
301
+
302
+ class BetaModel(nn.Module):
303
+ def __init__(self, obs_data, act_data, device=torch.device('cuda')):
304
+ super(BetaModel, self).__init__()
305
+
306
+ self.enc_model = TransformerEncModel(
307
+ data_dim = act_data.shape[-1] + obs_data.shape[-1],
308
+ embedding_dim = 256,
309
+ nhead = 4,
310
+ num_encoder_layers = 2,
311
+ device = device
312
+ ).to(device)
313
+
314
+ self.comp_model = AttentionComparisonModel(
315
+ input_dim = 256,
316
+ dropout_rate = 0.3,
317
+ nhead = 16,
318
+ device = device
319
+ ).to(device)
320
+
321
+ self.data = torch.concat((obs_data, act_data), dim=-1)
322
+
323
+ def forward(self, x):
324
+ batch_f = self.enc_model(x)
325
+ all_data_f = self.enc_model(self.data)
326
+ bias = all_data_f.mean()
327
+ std = all_data_f.std()
328
+ all_data_f = (all_data_f - bias) / std
329
+ batch_f = (batch_f - bias) / std
330
+ output = self.comp_model(batch_f, all_data_f)
331
+ return output
332
+
333
+ def one_to_one_forward(self, x, y):
334
+ x_f = self.enc_model(x)
335
+ y_f = self.enc_model(y)
336
+ all_data_f = self.enc_model(self.data)
337
+ bias = all_data_f.mean()
338
+ std = all_data_f.std()
339
+ x_f = (x_f - bias) / std
340
+ y_f = (y_f - bias) / std
341
+ output = self.comp_model.one_to_one_forward(x_f, y_f)
342
+ return output
343
+
344
+
345
+
346
+ self.model = BetaModel(obs_data, act_data, device)
347
+ self.opt = None #, weight_decay=1.0e-4
348
+ self.scheduler = None #torch.optim.lr_scheduler.ReduceLROnPlateau(self.opt, mode='min', patience=100, verbose=True)
349
+ self.ref_model: BetaModel
350
+
351
+ def get_alpha_beta(self, x):
352
+ batch_comp = self.model(x).detach()
353
+ alpha = torch.sum(batch_comp, dim=-1)
354
+ beta = torch.sum(1 - batch_comp, dim=-1)
355
+ # alpha = torch.sum(torch.where(batch_comp > 0.5, torch.tensor(1.0, device=self.device), torch.tensor(0.0, device=self.device)), dim=-1)
356
+ # beta = torch.sum(torch.where(batch_comp < 0.5, torch.tensor(1.0, device=self.device), torch.tensor(0.0, device=self.device)), dim=-1)
357
+
358
+ return alpha.detach(), beta.detach()
359
+
360
+ def fit_data(self, save_dir=None, load_dir=None, num_epochs=1, warm_up_epochs=0, batch_size=1, lr=1.0e-5):
361
+ if load_dir is None:
362
+ interval = math.ceil(self.data.shape[0] / batch_size)
363
+ total_steps = num_epochs * interval
364
+ warm_up_steps = warm_up_epochs * interval
365
+ main_steps = total_steps - warm_up_steps
366
+
367
+ # Learning rate schedulers
368
+ self.lr = lr
369
+ self.opt = torch.optim.Adam(self.model.parameters(), lr=self.lr, weight_decay=1e-5)
370
+ warm_up_scheduler = LinearLR(self.opt, start_factor=1e-8, end_factor=1.0, total_iters=warm_up_steps)
371
+ cosine_scheduler = CosineAnnealingLR(self.opt, T_max=main_steps)
372
+ self.scheduler = SequentialLR(self.opt, schedulers=[warm_up_scheduler, cosine_scheduler], milestones=[warm_up_steps])
373
+
374
+ logger = logging.getLogger()
375
+ time.sleep(0.5)
376
+ stage_time = datetime.now()
377
+ logger.info(f'Beta Model Training Start: {stage_time} seconds')
378
+
379
+
380
+ for epoch in range(num_epochs):
381
+ beta_loss_all = []
382
+ batch_shuffled_idx = np.random.permutation(self.data.shape[0])
383
+
384
+ time.sleep(0.5)
385
+ stage_time_last = datetime.now()
386
+ logger.info(f'Epoch {epoch + 1} Start: {stage_time_last} seconds')
387
+
388
+
389
+ for i in tqdm(range(interval)):
390
+
391
+ start_pt = i * batch_size
392
+ end_pt = min((i + 1) * batch_size, self.data.shape[0])
393
+ local_idx = batch_shuffled_idx[start_pt:end_pt]
394
+ batch = self.data[local_idx, ...]
395
+ batch_votes = self.votes_data[local_idx, ...]
396
+
397
+ comp = torch.sigmoid(batch_votes - self.votes_data.T)
398
+ pred_comp = self.model(batch)
399
+
400
+ beta_loss = torch.mean((comp - pred_comp) ** 2)
401
+
402
+ beta_loss_all.append(beta_loss)
403
+
404
+ self.opt.zero_grad()
405
+ beta_loss.backward()
406
+ self.opt.step()
407
+ self.scheduler.step() # Update LR after each optimizer step
408
+
409
+ beta_loss_all = torch.stack(beta_loss_all, dim=0)
410
+ print("iteration:", epoch + 1)
411
+ print("mean_beta_loss_all:", torch.mean(beta_loss_all).item())
412
+
413
+ time.sleep(0.5)
414
+ stage_time_last = datetime.now()
415
+ logger.info(f'Epoch {epoch + 1} End: {stage_time_last} seconds')
416
+
417
+ if save_dir is not None and (((epoch + 1) % 50 == 0) or ((epoch + 1) == num_epochs)):
418
+ tmp_save_dir = Path(save_dir) / f'itr_{epoch + 1}'
419
+ tmp_save_dir.mkdir(parents=True, exist_ok=True)
420
+ model_file = tmp_save_dir / 'beta_model.pth'
421
+ self.save_model(model_file)
422
+ self.ref_model = copy.deepcopy(self.model)
423
+ else:
424
+ self.load_model(load_dir)
425
+ self.ref_model = copy.deepcopy(self.model)
426
+
427
+ def online_update(self, dataset, num_epochs=1, warm_up_epochs=0, batch_size=1, lr = 1.0e-6):
428
+ interval = math.ceil(dataset["obs"].shape[0] / batch_size)
429
+ total_steps = num_epochs * interval
430
+ warm_up_steps = warm_up_epochs * interval
431
+ main_steps = total_steps - warm_up_steps
432
+
433
+ # Learning rate schedulers
434
+ self.lr = lr
435
+ self.opt = torch.optim.Adam(self.model.parameters(), lr=self.lr, weight_decay=1e-5)
436
+ warm_up_scheduler = LinearLR(self.opt, start_factor=1e-8, end_factor=1.0, total_iters=warm_up_steps)
437
+ cosine_scheduler = CosineAnnealingLR(self.opt, T_max=main_steps)
438
+ self.scheduler = SequentialLR(self.opt, schedulers=[warm_up_scheduler, cosine_scheduler], milestones=[warm_up_steps])
439
+
440
+ logger = logging.getLogger()
441
+ time.sleep(0.5)
442
+ stage_time = datetime.now()
443
+ logger.info(f'Beta Model Training Start: {stage_time} seconds')
444
+
445
+ for epoch in range(num_epochs):
446
+ beta_loss_all = []
447
+ batch_shuffled_idx = np.random.permutation(dataset["obs"].shape[0])
448
+
449
+ time.sleep(0.5)
450
+ stage_time_last = datetime.now()
451
+ logger.info(f'Epoch {epoch + 1} Start: {stage_time_last} seconds')
452
+
453
+ for i in tqdm(range(interval)):
454
+
455
+ start_pt = i * batch_size
456
+ end_pt = min((i + 1) * batch_size, dataset["obs"].shape[0])
457
+ batch = index_batch(dataset, batch_shuffled_idx[start_pt:end_pt])
458
+
459
+ obs_1 = batch['obs'] # batch_size * traj_len * obs_dim
460
+ act_1 = batch['action'] # batch_size * traj_len * action_dim
461
+ obs_2 = batch['obs_2']
462
+ act_2 = batch['action_2']
463
+ s_a_1 = np.concatenate([obs_1, act_1], axis=-1)
464
+ s_a_2 = np.concatenate([obs_2, act_2], axis=-1)
465
+
466
+ votes_1 = torch.from_numpy(batch['votes']).to(self.device)
467
+ votes_2 = torch.from_numpy(batch['votes_2']).to(self.device)
468
+
469
+ # threshold = 1e-3
470
+ # diff = torch.abs(votes_1 - votes_2)
471
+ # condition_1 = (votes_1 > votes_2) & (diff >= threshold) # votes_1 > votes_2 and diff >= threshold
472
+ # condition_2 = (votes_1 < votes_2) & (diff >= threshold) # votes_1 < votes_2 and diff >= threshold
473
+
474
+ # comp_1 = torch.where(condition_1, torch.tensor(1.0, device=self.device), torch.tensor(0.0, device=self.device))
475
+ # comp_1 = torch.squeeze(comp_1, dim=-1)
476
+ # comp_2 = torch.where(condition_2, torch.tensor(1.0, device=self.device), torch.tensor(0.0, device=self.device))
477
+ # comp_2 = torch.squeeze(comp_2, dim=-1)
478
+
479
+ comp_1 = torch.sigmoid(votes_1 - votes_2)
480
+ comp_2 = torch.sigmoid(votes_2 - votes_1)
481
+
482
+ pred_comp_1 = self.model.one_to_one_forward(torch.from_numpy(s_a_1).float().to(self.device), torch.from_numpy(s_a_2).float().to(self.device))
483
+ pred_comp_2 = self.model.one_to_one_forward(torch.from_numpy(s_a_2).float().to(self.device), torch.from_numpy(s_a_1).float().to(self.device))
484
+ # ref_comp_1 = self.ref_model.one_to_one_forward(torch.from_numpy(s_a_1).float().to(self.device), torch.from_numpy(s_a_2).float().to(self.device)).detach()
485
+ # ref_comp_2 = self.ref_model.one_to_one_forward(torch.from_numpy(s_a_2).float().to(self.device), torch.from_numpy(s_a_1).float().to(self.device)).detach()
486
+
487
+ beta_loss = (torch.mean((comp_1 - pred_comp_1) ** 2) + torch.mean((comp_2 - pred_comp_2) ** 2)) / 2
488
+
489
+ beta_loss_all.append(beta_loss)
490
+
491
+ self.opt.zero_grad()
492
+ beta_loss.backward()
493
+ self.opt.step()
494
+ self.scheduler.step() # Update LR after each optimizer step
495
+
496
+
497
+ time.sleep(0.5)
498
+ stage_time_now = datetime.now()
499
+ logger.info(f'Epoch {epoch + 1} Spending time:{stage_time_now - stage_time_last} End: {stage_time_now} seconds')
500
+
501
+ beta_loss_all = torch.stack(beta_loss_all, dim=0)
502
+ print("iteration:", epoch + 1)
503
+ print("mean_beta_loss_all:", torch.mean(beta_loss_all).item())
504
+
505
+ def save_model(self, filepath):
506
+ torch.save(self.state_dict(), filepath)
507
+
508
+ def load_model(self, filepath):
509
+ self.load_state_dict(torch.load(filepath, map_location=self.device))
510
+
511
+
512
+ class BetaImageNetwork(nn.Module):
513
+ def __init__(self, data, policy: DiffusionTransformerHybridImagePolicy, device=torch.device('cuda'), data_size = 500, ):
514
+ super(BetaNetwork, self).__init__()
515
+
516
+ act_data = np.concatenate((data['action'], data['action_2']), axis=0)
517
+ votes_data = np.concatenate((data['votes'], data['votes_2']), axis=0)
518
+ obs_data = {key:np.concatenate((data['obs'][key], data['obs_2'][key]), axis=0) for key in data['obs']}
519
+ if data_size <= act_data.shape[0]:
520
+ indices = np.random.randint(0, act_data.shape[0], size=data_size)
521
+ obs_data = {key:obs_data[key][indices, ...]for key in obs_data.keys()}
522
+ act_data = act_data[indices, ...]
523
+ votes_data = votes_data[indices, ...]
524
+
525
+ act_data = torch.from_numpy(act_data).float().to(device)
526
+ obs_data = {key: torch.from_numpy(obs_data[key]).float().to(device) for key in obs_data.keys()}
527
+
528
+ obs_encoder = policy.nets['policy'].nets['encoder'].nets['obs']
529
+
530
+ obs_data = policy.normalizer.normalize(obs_data)
531
+ act_data = policy.normalizer.normalize(act_data)
532
+
533
+ this_nobs = dict_apply(obs_data,
534
+ lambda x: x.reshape(-1,*x.shape[2:]))
535
+ nobs_features = obs_encoder(this_nobs)
536
+ nobs_features = nobs_features.reshape(data_size, act_data.shape[1], -1)
537
+ nobs_features.detach()
538
+
539
+
540
+ self.votes_data = torch.from_numpy(votes_data).to(device)
541
+ self.lr = None
542
+ self.device = device
543
+ self.data = torch.concat((act_data, nobs_features), dim=-1)
544
+
545
+ class BetaModel(nn.Module):
546
+ def __init__(self, obs_data, act_data, device=torch.device('cuda')):
547
+ super(BetaModel, self).__init__()
548
+
549
+ self.enc_model = TransformerEncModel(
550
+ data_dim = act_data.shape[-1] + obs_data.shape[-1],
551
+ embedding_dim = 256,
552
+ nhead = 4,
553
+ num_encoder_layers = 2,
554
+ device = device
555
+ ).to(device)
556
+
557
+ self.comp_model = AttentionComparisonModel(
558
+ input_dim = 256,
559
+ dropout_rate = 0.3,
560
+ nhead = 16,
561
+ device = device
562
+ ).to(device)
563
+
564
+ self.data = torch.concat((obs_data, act_data), dim=-1)
565
+
566
+ def forward(self, x):
567
+ batch_f = self.enc_model(x)
568
+ all_data_f = self.enc_model(self.data)
569
+ bias = all_data_f.mean()
570
+ std = all_data_f.std()
571
+ all_data_f = (all_data_f - bias) / std
572
+ batch_f = (batch_f - bias) / std
573
+ output = self.comp_model(batch_f, all_data_f)
574
+ return output
575
+
576
+ def one_to_one_forward(self, x, y):
577
+ x_f = self.enc_model(x)
578
+ y_f = self.enc_model(y)
579
+ all_data_f = self.enc_model(self.data)
580
+ bias = all_data_f.mean()
581
+ std = all_data_f.std()
582
+ x_f = (x_f - bias) / std
583
+ y_f = (y_f - bias) / std
584
+ output = self.comp_model.one_to_one_forward(x_f, y_f)
585
+ return output
586
+
587
+
588
+
589
+ self.model = BetaModel(nobs_features, act_data, device)
590
+ self.opt = None #, weight_decay=1.0e-4
591
+ self.scheduler = None #torch.optim.lr_scheduler.ReduceLROnPlateau(self.opt, mode='min', patience=100, verbose=True)
592
+ self.ref_model: BetaModel
593
+
594
+ def get_alpha_beta(self, x):
595
+ batch_comp = self.model(x).detach()
596
+ alpha = torch.sum(batch_comp, dim=-1)
597
+ beta = torch.sum(1 - batch_comp, dim=-1)
598
+ # alpha = torch.sum(torch.where(batch_comp > 0.5, torch.tensor(1.0, device=self.device), torch.tensor(0.0, device=self.device)), dim=-1)
599
+ # beta = torch.sum(torch.where(batch_comp < 0.5, torch.tensor(1.0, device=self.device), torch.tensor(0.0, device=self.device)), dim=-1)
600
+
601
+ return alpha.detach(), beta.detach()
602
+
603
+ # def fit_data(self, save_dir=None, load_dir=None, num_epochs=1, warm_up_epochs=0, batch_size=1, lr=1.0e-5):
604
+ # if load_dir is None:
605
+ # interval = math.ceil(self.data.shape[0] / batch_size)
606
+ # total_steps = num_epochs * interval
607
+ # warm_up_steps = warm_up_epochs * interval
608
+ # main_steps = total_steps - warm_up_steps
609
+
610
+ # # Learning rate schedulers
611
+ # self.lr = lr
612
+ # self.opt = torch.optim.Adam(self.model.parameters(), lr=self.lr, weight_decay=1e-5)
613
+ # warm_up_scheduler = LinearLR(self.opt, start_factor=1e-8, end_factor=1.0, total_iters=warm_up_steps)
614
+ # cosine_scheduler = CosineAnnealingLR(self.opt, T_max=main_steps)
615
+ # self.scheduler = SequentialLR(self.opt, schedulers=[warm_up_scheduler, cosine_scheduler], milestones=[warm_up_steps])
616
+
617
+ # for epoch in range(num_epochs):
618
+ # beta_loss_all = []
619
+
620
+ # batch_shuffled_idx = np.random.permutation(self.data.shape[0])
621
+ # for i in tqdm(range(interval)):
622
+
623
+ # start_pt = i * batch_size
624
+ # end_pt = min((i + 1) * batch_size, self.data.shape[0])
625
+ # local_idx = batch_shuffled_idx[start_pt:end_pt]
626
+ # batch = self.data[local_idx, ...]
627
+ # batch_votes = self.votes_data[local_idx, ...]
628
+
629
+ # comp = torch.sigmoid(batch_votes - self.votes_data.T)
630
+ # pred_comp = self.model(batch)
631
+
632
+ # beta_loss = torch.mean((comp - pred_comp) ** 2)
633
+
634
+ # beta_loss_all.append(beta_loss)
635
+
636
+ # self.opt.zero_grad()
637
+ # beta_loss.backward()
638
+ # self.opt.step()
639
+ # self.scheduler.step() # Update LR after each optimizer step
640
+
641
+ # beta_loss_all = torch.stack(beta_loss_all, dim=0)
642
+ # print("iteration:", epoch + 1)
643
+ # print("mean_beta_loss_all:", torch.mean(beta_loss_all).item())
644
+
645
+ # if save_dir is not None and (((epoch + 1) % 50 == 0) or ((epoch + 1) == num_epochs)):
646
+ # tmp_save_dir = Path(save_dir) / f'itr_{epoch + 1}'
647
+ # tmp_save_dir.mkdir(parents=True, exist_ok=True)
648
+ # model_file = tmp_save_dir / 'beta_model.pth'
649
+ # self.save_model(model_file)
650
+ # self.ref_model = copy.deepcopy(self.model)
651
+ # else:
652
+ # self.load_model(load_dir)
653
+ # self.ref_model = copy.deepcopy(self.model)
654
+
655
+ def online_update(self, dataset, num_epochs=1, warm_up_epochs=0, batch_size=1, lr = 1.0e-6):
656
+ interval = math.ceil(dataset["obs"].shape[0] / batch_size)
657
+ total_steps = num_epochs * interval
658
+ warm_up_steps = warm_up_epochs * interval
659
+ main_steps = total_steps - warm_up_steps
660
+
661
+ # Learning rate schedulers
662
+ self.lr = lr
663
+ self.opt = torch.optim.Adam(self.model.parameters(), lr=self.lr, weight_decay=1e-5)
664
+ warm_up_scheduler = LinearLR(self.opt, start_factor=1e-8, end_factor=1.0, total_iters=warm_up_steps)
665
+ cosine_scheduler = CosineAnnealingLR(self.opt, T_max=main_steps)
666
+ self.scheduler = SequentialLR(self.opt, schedulers=[warm_up_scheduler, cosine_scheduler], milestones=[warm_up_steps])
667
+
668
+ for epoch in range(num_epochs):
669
+ beta_loss_all = []
670
+
671
+ batch_shuffled_idx = np.random.permutation(dataset["obs"].shape[0])
672
+ for i in tqdm(range(interval)):
673
+
674
+ start_pt = i * batch_size
675
+ end_pt = min((i + 1) * batch_size, dataset["obs"].shape[0])
676
+ batch = index_batch(dataset, batch_shuffled_idx[start_pt:end_pt])
677
+
678
+ obs_1 = batch['obs'] # batch_size * traj_len * obs_dim
679
+ act_1 = batch['action'] # batch_size * traj_len * action_dim
680
+ obs_2 = batch['obs_2']
681
+ act_2 = batch['action_2']
682
+ s_a_1 = np.concatenate([obs_1, act_1], axis=-1)
683
+ s_a_2 = np.concatenate([obs_2, act_2], axis=-1)
684
+
685
+ votes_1 = torch.from_numpy(batch['votes']).to(self.device)
686
+ votes_2 = torch.from_numpy(batch['votes_2']).to(self.device)
687
+
688
+ # threshold = 1e-3
689
+ # diff = torch.abs(votes_1 - votes_2)
690
+ # condition_1 = (votes_1 > votes_2) & (diff >= threshold) # votes_1 > votes_2 and diff >= threshold
691
+ # condition_2 = (votes_1 < votes_2) & (diff >= threshold) # votes_1 < votes_2 and diff >= threshold
692
+
693
+ # comp_1 = torch.where(condition_1, torch.tensor(1.0, device=self.device), torch.tensor(0.0, device=self.device))
694
+ # comp_1 = torch.squeeze(comp_1, dim=-1)
695
+ # comp_2 = torch.where(condition_2, torch.tensor(1.0, device=self.device), torch.tensor(0.0, device=self.device))
696
+ # comp_2 = torch.squeeze(comp_2, dim=-1)
697
+
698
+ comp_1 = torch.sigmoid(votes_1 - votes_2)
699
+ comp_2 = torch.sigmoid(votes_2 - votes_1)
700
+
701
+ pred_comp_1 = self.model.one_to_one_forward(torch.from_numpy(s_a_1).float().to(self.device), torch.from_numpy(s_a_2).float().to(self.device))
702
+ pred_comp_2 = self.model.one_to_one_forward(torch.from_numpy(s_a_2).float().to(self.device), torch.from_numpy(s_a_1).float().to(self.device))
703
+ # ref_comp_1 = self.ref_model.one_to_one_forward(torch.from_numpy(s_a_1).float().to(self.device), torch.from_numpy(s_a_2).float().to(self.device)).detach()
704
+ # ref_comp_2 = self.ref_model.one_to_one_forward(torch.from_numpy(s_a_2).float().to(self.device), torch.from_numpy(s_a_1).float().to(self.device)).detach()
705
+
706
+ beta_loss = (torch.mean((comp_1 - pred_comp_1) ** 2) + torch.mean((comp_2 - pred_comp_2) ** 2)) / 2
707
+
708
+ beta_loss_all.append(beta_loss)
709
+
710
+ self.opt.zero_grad()
711
+ beta_loss.backward()
712
+ self.opt.step()
713
+ self.scheduler.step() # Update LR after each optimizer step
714
+
715
+ beta_loss_all = torch.stack(beta_loss_all, dim=0)
716
+ print("iteration:", epoch + 1)
717
+ print("mean_beta_loss_all:", torch.mean(beta_loss_all).item())
718
+
719
+ def save_model(self, filepath):
720
+ torch.save(self.state_dict(), filepath)
721
+
722
+ def load_model(self, filepath):
723
+ self.load_state_dict(torch.load(filepath, map_location=self.device))
common/pymunk_override.py ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ----------------------------------------------------------------------------
2
+ # pymunk
3
+ # Copyright (c) 2007-2016 Victor Blomqvist
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+ # ----------------------------------------------------------------------------
23
+
24
+ """This submodule contains helper functions to help with quick prototyping
25
+ using pymunk together with pygame.
26
+
27
+ Intended to help with debugging and prototyping, not for actual production use
28
+ in a full application. The methods contained in this module is opinionated
29
+ about your coordinate system and not in any way optimized.
30
+ """
31
+
32
+ __docformat__ = "reStructuredText"
33
+
34
+ __all__ = [
35
+ "DrawOptions",
36
+ "get_mouse_pos",
37
+ "to_pygame",
38
+ "from_pygame",
39
+ "lighten",
40
+ "positive_y_is_up",
41
+ ]
42
+
43
+ from typing import List, Sequence, Tuple
44
+
45
+ import pygame
46
+
47
+ import numpy as np
48
+
49
+ import pymunk
50
+ from pymunk.space_debug_draw_options import SpaceDebugColor
51
+ from pymunk.vec2d import Vec2d
52
+
53
+ positive_y_is_up: bool = False
54
+ """Make increasing values of y point upwards.
55
+
56
+ When True::
57
+
58
+ y
59
+ ^
60
+ | . (3, 3)
61
+ |
62
+ | . (2, 2)
63
+ |
64
+ +------ > x
65
+
66
+ When False::
67
+
68
+ +------ > x
69
+ |
70
+ | . (2, 2)
71
+ |
72
+ | . (3, 3)
73
+ v
74
+ y
75
+
76
+ """
77
+
78
+
79
+ class DrawOptions(pymunk.SpaceDebugDrawOptions):
80
+ def __init__(self, surface: pygame.Surface) -> None:
81
+ """Draw a pymunk.Space on a pygame.Surface object.
82
+
83
+ Typical usage::
84
+
85
+ >>> import pymunk
86
+ >>> surface = pygame.Surface((10,10))
87
+ >>> space = pymunk.Space()
88
+ >>> options = pymunk.pygame_util.DrawOptions(surface)
89
+ >>> space.debug_draw(options)
90
+
91
+ You can control the color of a shape by setting shape.color to the color
92
+ you want it drawn in::
93
+
94
+ >>> c = pymunk.Circle(None, 10)
95
+ >>> c.color = pygame.Color("pink")
96
+
97
+ See pygame_util.demo.py for a full example
98
+
99
+ Since pygame uses a coordinate system where y points down (in contrast
100
+ to many other cases), you either have to make the physics simulation
101
+ with Pymunk also behave in that way, or flip everything when you draw.
102
+
103
+ The easiest is probably to just make the simulation behave the same
104
+ way as Pygame does. In that way all coordinates used are in the same
105
+ orientation and easy to reason about::
106
+
107
+ >>> space = pymunk.Space()
108
+ >>> space.gravity = (0, -1000)
109
+ >>> body = pymunk.Body()
110
+ >>> body.position = (0, 0) # will be positioned in the top left corner
111
+ >>> space.debug_draw(options)
112
+
113
+ To flip the drawing its possible to set the module property
114
+ :py:data:`positive_y_is_up` to True. Then the pygame drawing will flip
115
+ the simulation upside down before drawing::
116
+
117
+ >>> positive_y_is_up = True
118
+ >>> body = pymunk.Body()
119
+ >>> body.position = (0, 0)
120
+ >>> # Body will be position in bottom left corner
121
+
122
+ :Parameters:
123
+ surface : pygame.Surface
124
+ Surface that the objects will be drawn on
125
+ """
126
+ self.surface = surface
127
+ super(DrawOptions, self).__init__()
128
+
129
+ def draw_circle(
130
+ self,
131
+ pos: Vec2d,
132
+ angle: float,
133
+ radius: float,
134
+ outline_color: SpaceDebugColor,
135
+ fill_color: SpaceDebugColor,
136
+ ) -> None:
137
+ p = to_pygame(pos, self.surface)
138
+
139
+ pygame.draw.circle(self.surface, fill_color.as_int(), p, round(radius), 0)
140
+ pygame.draw.circle(self.surface, light_color(fill_color).as_int(), p, round(radius-4), 0)
141
+
142
+ circle_edge = pos + Vec2d(radius, 0).rotated(angle)
143
+ p2 = to_pygame(circle_edge, self.surface)
144
+ line_r = 2 if radius > 20 else 1
145
+ # pygame.draw.lines(self.surface, outline_color.as_int(), False, [p, p2], line_r)
146
+
147
+ def draw_segment(self, a: Vec2d, b: Vec2d, color: SpaceDebugColor) -> None:
148
+ p1 = to_pygame(a, self.surface)
149
+ p2 = to_pygame(b, self.surface)
150
+
151
+ pygame.draw.aalines(self.surface, color.as_int(), False, [p1, p2])
152
+
153
+ def draw_fat_segment(
154
+ self,
155
+ a: Tuple[float, float],
156
+ b: Tuple[float, float],
157
+ radius: float,
158
+ outline_color: SpaceDebugColor,
159
+ fill_color: SpaceDebugColor,
160
+ ) -> None:
161
+ p1 = to_pygame(a, self.surface)
162
+ p2 = to_pygame(b, self.surface)
163
+
164
+ r = round(max(1, radius * 2))
165
+ pygame.draw.lines(self.surface, fill_color.as_int(), False, [p1, p2], r)
166
+ if r > 2:
167
+ orthog = [abs(p2[1] - p1[1]), abs(p2[0] - p1[0])]
168
+ if orthog[0] == 0 and orthog[1] == 0:
169
+ return
170
+ scale = radius / (orthog[0] * orthog[0] + orthog[1] * orthog[1]) ** 0.5
171
+ orthog[0] = round(orthog[0] * scale)
172
+ orthog[1] = round(orthog[1] * scale)
173
+ points = [
174
+ (p1[0] - orthog[0], p1[1] - orthog[1]),
175
+ (p1[0] + orthog[0], p1[1] + orthog[1]),
176
+ (p2[0] + orthog[0], p2[1] + orthog[1]),
177
+ (p2[0] - orthog[0], p2[1] - orthog[1]),
178
+ ]
179
+ pygame.draw.polygon(self.surface, fill_color.as_int(), points)
180
+ pygame.draw.circle(
181
+ self.surface,
182
+ fill_color.as_int(),
183
+ (round(p1[0]), round(p1[1])),
184
+ round(radius),
185
+ )
186
+ pygame.draw.circle(
187
+ self.surface,
188
+ fill_color.as_int(),
189
+ (round(p2[0]), round(p2[1])),
190
+ round(radius),
191
+ )
192
+
193
+ def draw_polygon(
194
+ self,
195
+ verts: Sequence[Tuple[float, float]],
196
+ radius: float,
197
+ outline_color: SpaceDebugColor,
198
+ fill_color: SpaceDebugColor,
199
+ ) -> None:
200
+ ps = [to_pygame(v, self.surface) for v in verts]
201
+ ps += [ps[0]]
202
+
203
+ radius = 2
204
+ pygame.draw.polygon(self.surface, light_color(fill_color).as_int(), ps)
205
+
206
+ if radius > 0:
207
+ for i in range(len(verts)):
208
+ a = verts[i]
209
+ b = verts[(i + 1) % len(verts)]
210
+ self.draw_fat_segment(a, b, radius, fill_color, fill_color)
211
+
212
+ def draw_dot(
213
+ self, size: float, pos: Tuple[float, float], color: SpaceDebugColor
214
+ ) -> None:
215
+ p = to_pygame(pos, self.surface)
216
+ pygame.draw.circle(self.surface, color.as_int(), p, round(size), 0)
217
+
218
+
219
+ def get_mouse_pos(surface: pygame.Surface) -> Tuple[int, int]:
220
+ """Get position of the mouse pointer in pymunk coordinates."""
221
+ p = pygame.mouse.get_pos()
222
+ return from_pygame(p, surface)
223
+
224
+
225
+ def to_pygame(p: Tuple[float, float], surface: pygame.Surface) -> Tuple[int, int]:
226
+ """Convenience method to convert pymunk coordinates to pygame surface
227
+ local coordinates.
228
+
229
+ Note that in case positive_y_is_up is False, this function won't actually do
230
+ anything except converting the point to integers.
231
+ """
232
+ if positive_y_is_up:
233
+ return round(p[0]), surface.get_height() - round(p[1])
234
+ else:
235
+ return round(p[0]), round(p[1])
236
+
237
+
238
+ def from_pygame(p: Tuple[float, float], surface: pygame.Surface) -> Tuple[int, int]:
239
+ """Convenience method to convert pygame surface local coordinates to
240
+ pymunk coordinates
241
+ """
242
+ return to_pygame(p, surface)
243
+
244
+
245
+ def light_color(color: SpaceDebugColor):
246
+ color = np.minimum(1.2 * np.float32([color.r, color.g, color.b, color.a]), np.float32([255]))
247
+ color = SpaceDebugColor(r=color[0], g=color[1], b=color[2], a=color[3])
248
+ return color
common/pymunk_util.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pygame
2
+ import pymunk
3
+ import pymunk.pygame_util
4
+ import numpy as np
5
+
6
+ COLLTYPE_DEFAULT = 0
7
+ COLLTYPE_MOUSE = 1
8
+ COLLTYPE_BALL = 2
9
+
10
+ def get_body_type(static=False):
11
+ body_type = pymunk.Body.DYNAMIC
12
+ if static:
13
+ body_type = pymunk.Body.STATIC
14
+ return body_type
15
+
16
+
17
+ def create_rectangle(space,
18
+ pos_x,pos_y,width,height,
19
+ density=3,static=False):
20
+ body = pymunk.Body(body_type=get_body_type(static))
21
+ body.position = (pos_x,pos_y)
22
+ shape = pymunk.Poly.create_box(body,(width,height))
23
+ shape.density = density
24
+ space.add(body,shape)
25
+ return body, shape
26
+
27
+
28
+ def create_rectangle_bb(space,
29
+ left, bottom, right, top,
30
+ **kwargs):
31
+ pos_x = (left + right) / 2
32
+ pos_y = (top + bottom) / 2
33
+ height = top - bottom
34
+ width = right - left
35
+ return create_rectangle(space, pos_x, pos_y, width, height, **kwargs)
36
+
37
+ def create_circle(space, pos_x, pos_y, radius, density=3, static=False):
38
+ body = pymunk.Body(body_type=get_body_type(static))
39
+ body.position = (pos_x, pos_y)
40
+ shape = pymunk.Circle(body, radius=radius)
41
+ shape.density = density
42
+ shape.collision_type = COLLTYPE_BALL
43
+ space.add(body, shape)
44
+ return body, shape
45
+
46
+ def get_body_state(body):
47
+ state = np.zeros(6, dtype=np.float32)
48
+ state[:2] = body.position
49
+ state[2] = body.angle
50
+ state[3:5] = body.velocity
51
+ state[5] = body.angular_velocity
52
+ return state
common/pytorch_util.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Dict, Callable, List
2
+ import collections
3
+ import torch
4
+ import torch.nn as nn
5
+
6
+ def dict_apply(
7
+ x: Dict[str, torch.Tensor],
8
+ func: Callable[[torch.Tensor], torch.Tensor]
9
+ ) -> Dict[str, torch.Tensor]:
10
+ result = dict()
11
+ for key, value in x.items():
12
+ if isinstance(value, dict):
13
+ result[key] = dict_apply(value, func)
14
+ else:
15
+ result[key] = func(value)
16
+ return result
17
+
18
+ def pad_remaining_dims(x, target):
19
+ assert x.shape == target.shape[:len(x.shape)]
20
+ return x.reshape(x.shape + (1,)*(len(target.shape) - len(x.shape)))
21
+
22
+ def dict_apply_split(
23
+ x: Dict[str, torch.Tensor],
24
+ split_func: Callable[[torch.Tensor], Dict[str, torch.Tensor]]
25
+ ) -> Dict[str, torch.Tensor]:
26
+ results = collections.defaultdict(dict)
27
+ for key, value in x.items():
28
+ result = split_func(value)
29
+ for k, v in result.items():
30
+ results[k][key] = v
31
+ return results
32
+
33
+ def dict_apply_reduce(
34
+ x: List[Dict[str, torch.Tensor]],
35
+ reduce_func: Callable[[List[torch.Tensor]], torch.Tensor]
36
+ ) -> Dict[str, torch.Tensor]:
37
+ result = dict()
38
+ for key in x[0].keys():
39
+ result[key] = reduce_func([x_[key] for x_ in x])
40
+ return result
41
+
42
+
43
+ def replace_submodules(
44
+ root_module: nn.Module,
45
+ predicate: Callable[[nn.Module], bool],
46
+ func: Callable[[nn.Module], nn.Module]) -> nn.Module:
47
+ """
48
+ predicate: Return true if the module is to be replaced.
49
+ func: Return new module to use.
50
+ """
51
+ if predicate(root_module):
52
+ return func(root_module)
53
+
54
+ bn_list = [k.split('.') for k, m
55
+ in root_module.named_modules(remove_duplicate=True)
56
+ if predicate(m)]
57
+ for *parent, k in bn_list:
58
+ parent_module = root_module
59
+ if len(parent) > 0:
60
+ parent_module = root_module.get_submodule('.'.join(parent))
61
+ if isinstance(parent_module, nn.Sequential):
62
+ src_module = parent_module[int(k)]
63
+ else:
64
+ src_module = getattr(parent_module, k)
65
+ tgt_module = func(src_module)
66
+ if isinstance(parent_module, nn.Sequential):
67
+ parent_module[int(k)] = tgt_module
68
+ else:
69
+ setattr(parent_module, k, tgt_module)
70
+ # verify that all BN are replaced
71
+ bn_list = [k.split('.') for k, m
72
+ in root_module.named_modules(remove_duplicate=True)
73
+ if predicate(m)]
74
+ assert len(bn_list) == 0
75
+ return root_module
76
+
77
+ def optimizer_to(optimizer, device):
78
+ for state in optimizer.state.values():
79
+ for k, v in state.items():
80
+ if isinstance(v, torch.Tensor):
81
+ state[k] = v.to(device=device)
82
+ return optimizer
common/replay_buffer.py ADDED
@@ -0,0 +1,615 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Union, Dict, Optional
2
+ import os
3
+ import math
4
+ import numbers
5
+ import zarr
6
+ import numcodecs
7
+ import numpy as np
8
+ from functools import cached_property
9
+
10
+ def check_chunks_compatible(chunks: tuple, shape: tuple):
11
+ assert len(shape) == len(chunks)
12
+ for c in chunks:
13
+ assert isinstance(c, numbers.Integral)
14
+ assert c > 0
15
+
16
+ def rechunk_recompress_array(group, name,
17
+ chunks=None, chunk_length=None,
18
+ compressor=None, tmp_key='_temp'):
19
+ old_arr = group[name]
20
+ if chunks is None:
21
+ if chunk_length is not None:
22
+ chunks = (chunk_length,) + old_arr.chunks[1:]
23
+ else:
24
+ chunks = old_arr.chunks
25
+ check_chunks_compatible(chunks, old_arr.shape)
26
+
27
+ if compressor is None:
28
+ compressor = old_arr.compressor
29
+
30
+ if (chunks == old_arr.chunks) and (compressor == old_arr.compressor):
31
+ # no change
32
+ return old_arr
33
+
34
+ # rechunk recompress
35
+ group.move(name, tmp_key)
36
+ old_arr = group[tmp_key]
37
+ n_copied, n_skipped, n_bytes_copied = zarr.copy(
38
+ source=old_arr,
39
+ dest=group,
40
+ name=name,
41
+ chunks=chunks,
42
+ compressor=compressor,
43
+ )
44
+ del group[tmp_key]
45
+ arr = group[name]
46
+ return arr
47
+
48
+ def get_optimal_chunks(shape, dtype,
49
+ target_chunk_bytes=2e6,
50
+ max_chunk_length=None):
51
+ """
52
+ Common shapes
53
+ T,D
54
+ T,N,D
55
+ T,H,W,C
56
+ T,N,H,W,C
57
+ """
58
+ itemsize = np.dtype(dtype).itemsize
59
+ # reversed
60
+ rshape = list(shape[::-1])
61
+ if max_chunk_length is not None:
62
+ rshape[-1] = int(max_chunk_length)
63
+ split_idx = len(shape)-1
64
+ for i in range(len(shape)-1):
65
+ this_chunk_bytes = itemsize * np.prod(rshape[:i])
66
+ next_chunk_bytes = itemsize * np.prod(rshape[:i+1])
67
+ if this_chunk_bytes <= target_chunk_bytes \
68
+ and next_chunk_bytes > target_chunk_bytes:
69
+ split_idx = i
70
+
71
+ rchunks = rshape[:split_idx]
72
+ item_chunk_bytes = itemsize * np.prod(rshape[:split_idx])
73
+ this_max_chunk_length = rshape[split_idx]
74
+ next_chunk_length = min(this_max_chunk_length, math.ceil(
75
+ target_chunk_bytes / item_chunk_bytes))
76
+ rchunks.append(next_chunk_length)
77
+ len_diff = len(shape) - len(rchunks)
78
+ rchunks.extend([1] * len_diff)
79
+ chunks = tuple(rchunks[::-1])
80
+ # print(np.prod(chunks) * itemsize / target_chunk_bytes)
81
+ return chunks
82
+
83
+
84
+ class ReplayBuffer:
85
+ """
86
+ Zarr-based temporal datastructure.
87
+ Assumes first dimension to be time. Only chunk in time dimension.
88
+ """
89
+ def __init__(self,
90
+ root: Union[zarr.Group,
91
+ Dict[str,dict]]):
92
+ """
93
+ Dummy constructor. Use copy_from* and create_from* class methods instead.
94
+ """
95
+ assert('data' in root)
96
+ assert('meta' in root)
97
+ assert('episode_ends' in root['meta'])
98
+ for key, value in root['data'].items():
99
+ assert(value.shape[0] == root['meta']['episode_ends'][-1])
100
+ self.root = root
101
+
102
+ # ============= create constructors ===============
103
+ @classmethod
104
+ def create_empty_zarr(cls, storage=None, root=None):
105
+ if root is None:
106
+ if storage is None:
107
+ storage = zarr.MemoryStore()
108
+ root = zarr.group(store=storage)
109
+ data = root.require_group('data', overwrite=False)
110
+ meta = root.require_group('meta', overwrite=False)
111
+ if 'episode_ends' not in meta:
112
+ episode_ends = meta.zeros('episode_ends', shape=(0,), dtype=np.int64,
113
+ compressor=None, overwrite=False)
114
+ return cls(root=root)
115
+
116
+ @classmethod
117
+ def create_empty_numpy(cls):
118
+ root = {
119
+ 'data': dict(),
120
+ 'meta': {
121
+ 'episode_ends': np.zeros((0,), dtype=np.int64)
122
+ }
123
+ }
124
+ return cls(root=root)
125
+
126
+ @classmethod
127
+ def create_from_group(cls, group, **kwargs):
128
+ if 'data' not in group:
129
+ # create from stratch
130
+ buffer = cls.create_empty_zarr(root=group, **kwargs)
131
+ else:
132
+ # already exist
133
+ buffer = cls(root=group, **kwargs)
134
+ return buffer
135
+
136
+ @classmethod
137
+ def create_from_path(cls, zarr_path, mode='r', **kwargs):
138
+ """
139
+ Open a on-disk zarr directly (for dataset larger than memory).
140
+ Slower.
141
+ """
142
+ group = zarr.open(os.path.expanduser(zarr_path), mode)
143
+ return cls.create_from_group(group, **kwargs)
144
+
145
+ # ============= copy constructors ===============
146
+ @classmethod
147
+ def copy_from_store(cls, src_store, store=None, keys=None,
148
+ chunks: Dict[str,tuple]=dict(),
149
+ compressors: Union[dict, str, numcodecs.abc.Codec]=dict(),
150
+ if_exists='replace',
151
+ **kwargs):
152
+ """
153
+ Load to memory.
154
+ """
155
+ src_root = zarr.group(src_store)
156
+ root = None
157
+ if store is None:
158
+ # numpy backend
159
+ meta = dict()
160
+ for key, value in src_root['meta'].items():
161
+ if len(value.shape) == 0:
162
+ meta[key] = np.array(value)
163
+ else:
164
+ meta[key] = value[:]
165
+
166
+ if keys is None:
167
+ keys = src_root['data'].keys()
168
+ data = dict()
169
+ for key in keys:
170
+ arr = src_root['data'][key]
171
+ data[key] = arr[:]
172
+
173
+ root = {
174
+ 'meta': meta,
175
+ 'data': data
176
+ }
177
+ else:
178
+ root = zarr.group(store=store)
179
+ # copy without recompression
180
+ n_copied, n_skipped, n_bytes_copied = zarr.copy_store(source=src_store, dest=store,
181
+ source_path='/meta', dest_path='/meta', if_exists=if_exists)
182
+ data_group = root.create_group('data', overwrite=True)
183
+ if keys is None:
184
+ keys = src_root['data'].keys()
185
+ for key in keys:
186
+ value = src_root['data'][key]
187
+ cks = cls._resolve_array_chunks(
188
+ chunks=chunks, key=key, array=value)
189
+ cpr = cls._resolve_array_compressor(
190
+ compressors=compressors, key=key, array=value)
191
+ if cks == value.chunks and cpr == value.compressor:
192
+ # copy without recompression
193
+ this_path = '/data/' + key
194
+ n_copied, n_skipped, n_bytes_copied = zarr.copy_store(
195
+ source=src_store, dest=store,
196
+ source_path=this_path, dest_path=this_path,
197
+ if_exists=if_exists
198
+ )
199
+ else:
200
+ # copy with recompression
201
+ n_copied, n_skipped, n_bytes_copied = zarr.copy(
202
+ source=value, dest=data_group, name=key,
203
+ chunks=cks, compressor=cpr, if_exists=if_exists
204
+ )
205
+ buffer = cls(root=root)
206
+ return buffer
207
+
208
+ @classmethod
209
+ def copy_from_path(cls, zarr_path, backend=None, store=None, keys=None,
210
+ chunks: Dict[str,tuple]=dict(),
211
+ compressors: Union[dict, str, numcodecs.abc.Codec]=dict(),
212
+ if_exists='replace',
213
+ **kwargs):
214
+ """
215
+ Copy a on-disk zarr to in-memory compressed.
216
+ Recommended
217
+ """
218
+ if backend == 'numpy':
219
+ print('backend argument is deprecated!')
220
+ store = None
221
+ group = zarr.open(os.path.expanduser(zarr_path), 'r')
222
+ return cls.copy_from_store(src_store=group.store, store=store,
223
+ keys=keys, chunks=chunks, compressors=compressors,
224
+ if_exists=if_exists, **kwargs)
225
+
226
+ # ============= save methods ===============
227
+ def save_to_store(self, store,
228
+ chunks: Optional[Dict[str,tuple]]=dict(),
229
+ compressors: Union[str, numcodecs.abc.Codec, dict]=dict(),
230
+ if_exists='replace',
231
+ **kwargs):
232
+
233
+ root = zarr.group(store)
234
+ if self.backend == 'zarr':
235
+ # recompression free copy
236
+ n_copied, n_skipped, n_bytes_copied = zarr.copy_store(
237
+ source=self.root.store, dest=store,
238
+ source_path='/meta', dest_path='/meta', if_exists=if_exists)
239
+ else:
240
+ meta_group = root.create_group('meta', overwrite=True)
241
+ # save meta, no chunking
242
+ for key, value in self.root['meta'].items():
243
+ _ = meta_group.array(
244
+ name=key,
245
+ data=value,
246
+ shape=value.shape,
247
+ chunks=value.shape)
248
+
249
+ # save data, chunk
250
+ data_group = root.create_group('data', overwrite=True)
251
+ for key, value in self.root['data'].items():
252
+ cks = self._resolve_array_chunks(
253
+ chunks=chunks, key=key, array=value)
254
+ cpr = self._resolve_array_compressor(
255
+ compressors=compressors, key=key, array=value)
256
+ if isinstance(value, zarr.Array):
257
+ if cks == value.chunks and cpr == value.compressor:
258
+ # copy without recompression
259
+ this_path = '/data/' + key
260
+ n_copied, n_skipped, n_bytes_copied = zarr.copy_store(
261
+ source=self.root.store, dest=store,
262
+ source_path=this_path, dest_path=this_path, if_exists=if_exists)
263
+ else:
264
+ # copy with recompression
265
+ n_copied, n_skipped, n_bytes_copied = zarr.copy(
266
+ source=value, dest=data_group, name=key,
267
+ chunks=cks, compressor=cpr, if_exists=if_exists
268
+ )
269
+ else:
270
+ # numpy
271
+ _ = data_group.array(
272
+ name=key,
273
+ data=value,
274
+ chunks=cks,
275
+ compressor=cpr
276
+ )
277
+ return store
278
+
279
+ def save_to_path(self, zarr_path,
280
+ chunks: Optional[Dict[str,tuple]]=dict(),
281
+ compressors: Union[str, numcodecs.abc.Codec, dict]=dict(),
282
+ if_exists='replace',
283
+ **kwargs):
284
+ store = zarr.DirectoryStore(os.path.expanduser(zarr_path))
285
+ return self.save_to_store(store, chunks=chunks,
286
+ compressors=compressors, if_exists=if_exists, **kwargs)
287
+
288
+ @staticmethod
289
+ def resolve_compressor(compressor='default'):
290
+ if compressor == 'default':
291
+ compressor = numcodecs.Blosc(cname='lz4', clevel=5,
292
+ shuffle=numcodecs.Blosc.NOSHUFFLE)
293
+ elif compressor == 'disk':
294
+ compressor = numcodecs.Blosc('zstd', clevel=5,
295
+ shuffle=numcodecs.Blosc.BITSHUFFLE)
296
+ return compressor
297
+
298
+ @classmethod
299
+ def _resolve_array_compressor(cls,
300
+ compressors: Union[dict, str, numcodecs.abc.Codec], key, array):
301
+ # allows compressor to be explicitly set to None
302
+ cpr = 'nil'
303
+ if isinstance(compressors, dict):
304
+ if key in compressors:
305
+ cpr = cls.resolve_compressor(compressors[key])
306
+ elif isinstance(array, zarr.Array):
307
+ cpr = array.compressor
308
+ else:
309
+ cpr = cls.resolve_compressor(compressors)
310
+ # backup default
311
+ if cpr == 'nil':
312
+ cpr = cls.resolve_compressor('default')
313
+ return cpr
314
+
315
+ @classmethod
316
+ def _resolve_array_chunks(cls,
317
+ chunks: Union[dict, tuple], key, array):
318
+ cks = None
319
+ if isinstance(chunks, dict):
320
+ if key in chunks:
321
+ cks = chunks[key]
322
+ elif isinstance(array, zarr.Array):
323
+ cks = array.chunks
324
+ elif isinstance(chunks, tuple):
325
+ cks = chunks
326
+ else:
327
+ raise TypeError(f"Unsupported chunks type {type(chunks)}")
328
+ # backup default
329
+ if cks is None:
330
+ cks = get_optimal_chunks(shape=array.shape, dtype=array.dtype)
331
+ # check
332
+ check_chunks_compatible(chunks=cks, shape=array.shape)
333
+ return cks
334
+
335
+ # ============= properties =================
336
+ @cached_property
337
+ def data(self):
338
+ return self.root['data']
339
+
340
+ @cached_property
341
+ def meta(self):
342
+ return self.root['meta']
343
+
344
+ def update_meta(self, data):
345
+ # sanitize data
346
+ np_data = dict()
347
+ for key, value in data.items():
348
+ if isinstance(value, np.ndarray):
349
+ np_data[key] = value
350
+ else:
351
+ arr = np.array(value)
352
+ if arr.dtype == object:
353
+ raise TypeError(f"Invalid value type {type(value)}")
354
+ np_data[key] = arr
355
+
356
+ meta_group = self.meta
357
+ if self.backend == 'zarr':
358
+ for key, value in np_data.items():
359
+ _ = meta_group.array(
360
+ name=key,
361
+ data=value,
362
+ shape=value.shape,
363
+ chunks=value.shape,
364
+ overwrite=True)
365
+ else:
366
+ meta_group.update(np_data)
367
+
368
+ return meta_group
369
+
370
+ @property
371
+ def episode_ends(self):
372
+ return self.meta['episode_ends']
373
+
374
+ def get_episode_idxs(self):
375
+ import numba
376
+ numba.jit(nopython=True)
377
+ def _get_episode_idxs(episode_ends):
378
+ result = np.zeros((episode_ends[-1],), dtype=np.int64)
379
+ for i in range(len(episode_ends)):
380
+ start = 0
381
+ if i > 0:
382
+ start = episode_ends[i-1]
383
+ end = episode_ends[i]
384
+ for idx in range(start, end):
385
+ result[idx] = i
386
+ return result
387
+ return _get_episode_idxs(self.episode_ends)
388
+
389
+
390
+ @property
391
+ def backend(self):
392
+ backend = 'numpy'
393
+ if isinstance(self.root, zarr.Group):
394
+ backend = 'zarr'
395
+ return backend
396
+
397
+ # =========== dict-like API ==============
398
+ def __repr__(self) -> str:
399
+ if self.backend == 'zarr':
400
+ return str(self.root.tree())
401
+ else:
402
+ return super().__repr__()
403
+
404
+ def keys(self):
405
+ return self.data.keys()
406
+
407
+ def values(self):
408
+ return self.data.values()
409
+
410
+ def items(self):
411
+ return self.data.items()
412
+
413
+ def __getitem__(self, key):
414
+ return self.data[key]
415
+
416
+ def __contains__(self, key):
417
+ return key in self.data
418
+
419
+ # =========== our API ==============
420
+ @property
421
+ def n_steps(self):
422
+ if len(self.episode_ends) == 0:
423
+ return 0
424
+ return self.episode_ends[-1]
425
+
426
+ @property
427
+ def n_episodes(self):
428
+ return len(self.episode_ends)
429
+
430
+ @property
431
+ def chunk_size(self):
432
+ if self.backend == 'zarr':
433
+ return next(iter(self.data.arrays()))[-1].chunks[0]
434
+ return None
435
+
436
+ @property
437
+ def episode_lengths(self):
438
+ ends = self.episode_ends[:]
439
+ ends = np.insert(ends, 0, 0)
440
+ lengths = np.diff(ends)
441
+ return lengths
442
+
443
+ def add_episode(self,
444
+ data: Dict[str, np.ndarray],
445
+ meta_data: Optional[Dict[str, Union[np.ndarray, int]]] = None,
446
+ chunks: Optional[Dict[str, tuple]] = dict(),
447
+ compressors: Union[str, numcodecs.abc.Codec, dict] = dict()):
448
+ assert(len(data) > 0)
449
+ is_zarr = (self.backend == 'zarr')
450
+
451
+ curr_len = self.n_steps
452
+ episode_length = None
453
+ for key, value in data.items():
454
+ assert(len(value.shape) >= 1)
455
+ if episode_length is None:
456
+ episode_length = len(value)
457
+ else:
458
+ assert(episode_length == len(value))
459
+ new_len = curr_len + episode_length
460
+
461
+ for key, value in data.items():
462
+ new_shape = (new_len,) + value.shape[1:]
463
+ # create array
464
+ if key not in self.data:
465
+ if is_zarr:
466
+ cks = self._resolve_array_chunks(
467
+ chunks=chunks, key=key, array=value)
468
+ cpr = self._resolve_array_compressor(
469
+ compressors=compressors, key=key, array=value)
470
+ arr = self.data.zeros(name=key,
471
+ shape=new_shape,
472
+ chunks=cks,
473
+ dtype=value.dtype,
474
+ compressor=cpr)
475
+ else:
476
+ # copy data to prevent modify
477
+ arr = np.zeros(shape=new_shape, dtype=value.dtype)
478
+ self.data[key] = arr
479
+ else:
480
+ arr = self.data[key]
481
+ assert(value.shape[1:] == arr.shape[1:])
482
+ # same method for both zarr and numpy
483
+ if is_zarr:
484
+ arr.resize(new_shape)
485
+ else:
486
+ arr.resize(new_shape, refcheck=False)
487
+ # copy data
488
+ arr[-value.shape[0]:] = value
489
+
490
+ # append to episode ends
491
+ episode_ends = self.episode_ends
492
+ if is_zarr:
493
+ episode_ends.resize(episode_ends.shape[0] + 1)
494
+ else:
495
+ episode_ends.resize(episode_ends.shape[0] + 1, refcheck=False)
496
+ episode_ends[-1] = new_len
497
+
498
+ # add meta data, e.g., votes
499
+ if meta_data is not None:
500
+ for key, value in meta_data.items():
501
+ if key in self.meta:
502
+ meta_arr = self.meta[key]
503
+ if is_zarr:
504
+ meta_arr.resize(meta_arr.shape[0] + 1)
505
+ else:
506
+ meta_arr.resize(meta_arr.shape[0] + 1, refcheck=False)
507
+ meta_arr[-1] = value
508
+
509
+ # rechunk
510
+ if is_zarr:
511
+ if episode_ends.chunks[0] < episode_ends.shape[0]:
512
+ rechunk_recompress_array(self.meta, 'episode_ends',
513
+ chunk_length=int(episode_ends.shape[0] * 1.5))
514
+
515
+ def drop_episode(self):
516
+ is_zarr = (self.backend == 'zarr')
517
+ episode_ends = self.episode_ends[:].copy()
518
+ assert(len(episode_ends) > 0)
519
+ start_idx = 0
520
+ if len(episode_ends) > 1:
521
+ start_idx = episode_ends[-2]
522
+ for key, value in self.data.items():
523
+ new_shape = (start_idx,) + value.shape[1:]
524
+ if is_zarr:
525
+ value.resize(new_shape)
526
+ else:
527
+ value.resize(new_shape, refcheck=False)
528
+ if is_zarr:
529
+ self.episode_ends.resize(len(episode_ends)-1)
530
+ else:
531
+ self.episode_ends.resize(len(episode_ends)-1, refcheck=False)
532
+
533
+ def pop_episode(self):
534
+ assert(self.n_episodes > 0)
535
+ episode = self.get_episode(self.n_episodes-1, copy=True)
536
+ self.drop_episode()
537
+ return episode
538
+
539
+ def extend(self, data):
540
+ self.add_episode(data)
541
+
542
+ def get_episode(self, idx, keys=None, copy=False):
543
+ idx = list(range(len(self.episode_ends)))[idx]
544
+ start_idx = 0
545
+ if idx > 0:
546
+ start_idx = self.episode_ends[idx-1]
547
+ end_idx = self.episode_ends[idx]
548
+ # result = self.get_steps_slice(start_idx, end_idx, copy=copy)
549
+
550
+ if keys is None:
551
+ keys = self.keys()
552
+
553
+ result = dict()
554
+ for key in keys:
555
+ value = self.data[key]
556
+ x = value[start_idx:end_idx]
557
+ if copy and isinstance(value, np.ndarray):
558
+ x = x.copy()
559
+ result[key] = x
560
+ return result
561
+
562
+ def get_episode_slice(self, idx):
563
+ start_idx = 0
564
+ if idx > 0:
565
+ start_idx = self.episode_ends[idx-1]
566
+ end_idx = self.episode_ends[idx]
567
+ return slice(start_idx, end_idx)
568
+
569
+ def get_steps_slice(self, start, stop, step=None, copy=False):
570
+ _slice = slice(start, stop, step)
571
+
572
+ result = dict()
573
+ for key, value in self.data.items():
574
+ x = value[_slice]
575
+ if copy and isinstance(value, np.ndarray):
576
+ x = x.copy()
577
+ result[key] = x
578
+ return result
579
+
580
+ # =========== chunking =============
581
+ def get_chunks(self) -> dict:
582
+ assert self.backend == 'zarr'
583
+ chunks = dict()
584
+ for key, value in self.data.items():
585
+ chunks[key] = value.chunks
586
+ return chunks
587
+
588
+ def set_chunks(self, chunks: dict):
589
+ assert self.backend == 'zarr'
590
+ for key, value in chunks.items():
591
+ if key in self.data:
592
+ arr = self.data[key]
593
+ if value != arr.chunks:
594
+ check_chunks_compatible(chunks=value, shape=arr.shape)
595
+ rechunk_recompress_array(self.data, key, chunks=value)
596
+
597
+ def get_compressors(self) -> dict:
598
+ assert self.backend == 'zarr'
599
+ compressors = dict()
600
+ for key, value in self.data.items():
601
+ compressors[key] = value.compressor
602
+ return compressors
603
+
604
+ def set_compressors(self, compressors: dict):
605
+ assert self.backend == 'zarr'
606
+ for key, value in compressors.items():
607
+ if key in self.data:
608
+ arr = self.data[key]
609
+ compressor = self.resolve_compressor(value)
610
+ if compressor != arr.compressor:
611
+ rechunk_recompress_array(self.data, key, compressor=compressor)
612
+
613
+ def get_votes(self) -> np.ndarray:
614
+ return self.meta['votes'][:]
615
+
common/robomimic_config_util.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from omegaconf import OmegaConf
2
+ from robomimic.config import config_factory
3
+ import robomimic.scripts.generate_paper_configs as gpc
4
+ from robomimic.scripts.generate_paper_configs import (
5
+ modify_config_for_default_image_exp,
6
+ modify_config_for_default_low_dim_exp,
7
+ modify_config_for_dataset,
8
+ )
9
+
10
+ def get_robomimic_config(
11
+ algo_name='bc_rnn',
12
+ hdf5_type='low_dim',
13
+ task_name='square',
14
+ dataset_type='ph'
15
+ ):
16
+ base_dataset_dir = '/tmp/null'
17
+ filter_key = None
18
+
19
+ # decide whether to use low-dim or image training defaults
20
+ modifier_for_obs = modify_config_for_default_image_exp
21
+ if hdf5_type in ["low_dim", "low_dim_sparse", "low_dim_dense"]:
22
+ modifier_for_obs = modify_config_for_default_low_dim_exp
23
+
24
+ algo_config_name = "bc" if algo_name == "bc_rnn" else algo_name
25
+ config = config_factory(algo_name=algo_config_name)
26
+ # turn into default config for observation modalities (e.g.: low-dim or rgb)
27
+ config = modifier_for_obs(config)
28
+ # add in config based on the dataset
29
+ config = modify_config_for_dataset(
30
+ config=config,
31
+ task_name=task_name,
32
+ dataset_type=dataset_type,
33
+ hdf5_type=hdf5_type,
34
+ base_dataset_dir=base_dataset_dir,
35
+ filter_key=filter_key,
36
+ )
37
+ # add in algo hypers based on dataset
38
+ algo_config_modifier = getattr(gpc, f'modify_{algo_name}_config_for_dataset')
39
+ config = algo_config_modifier(
40
+ config=config,
41
+ task_name=task_name,
42
+ dataset_type=dataset_type,
43
+ hdf5_type=hdf5_type,
44
+ )
45
+ return config
46
+
47
+
common/robomimic_util.py ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import copy
3
+
4
+ import h5py
5
+ import robomimic.utils.obs_utils as ObsUtils
6
+ import robomimic.utils.file_utils as FileUtils
7
+ import robomimic.utils.env_utils as EnvUtils
8
+ from scipy.spatial.transform import Rotation
9
+
10
+ from robomimic.config import config_factory
11
+
12
+
13
+ class RobomimicAbsoluteActionConverter:
14
+ def __init__(self, dataset_path, algo_name='bc'):
15
+ # default BC config
16
+ config = config_factory(algo_name=algo_name)
17
+
18
+ # read config to set up metadata for observation modalities (e.g. detecting rgb observations)
19
+ # must ran before create dataset
20
+ ObsUtils.initialize_obs_utils_with_config(config)
21
+
22
+ env_meta = FileUtils.get_env_metadata_from_dataset(dataset_path)
23
+ abs_env_meta = copy.deepcopy(env_meta)
24
+ abs_env_meta['env_kwargs']['controller_configs']['control_delta'] = False
25
+
26
+ env = EnvUtils.create_env_from_metadata(
27
+ env_meta=env_meta,
28
+ render=False,
29
+ render_offscreen=False,
30
+ use_image_obs=False,
31
+ )
32
+ assert len(env.env.robots) in (1, 2)
33
+
34
+ abs_env = EnvUtils.create_env_from_metadata(
35
+ env_meta=abs_env_meta,
36
+ render=False,
37
+ render_offscreen=False,
38
+ use_image_obs=False,
39
+ )
40
+ assert not abs_env.env.robots[0].controller.use_delta
41
+
42
+ self.env = env
43
+ self.abs_env = abs_env
44
+ self.file = h5py.File(dataset_path, 'r')
45
+
46
+ def __len__(self):
47
+ return len(self.file['data'])
48
+
49
+ def convert_actions(self,
50
+ states: np.ndarray,
51
+ actions: np.ndarray) -> np.ndarray:
52
+ """
53
+ Given state and delta action sequence
54
+ generate equivalent goal position and orientation for each step
55
+ keep the original gripper action intact.
56
+ """
57
+ # in case of multi robot
58
+ # reshape (N,14) to (N,2,7)
59
+ # or (N,7) to (N,1,7)
60
+ stacked_actions = actions.reshape(*actions.shape[:-1],-1,7)
61
+
62
+ env = self.env
63
+ # generate abs actions
64
+ action_goal_pos = np.zeros(
65
+ stacked_actions.shape[:-1]+(3,),
66
+ dtype=stacked_actions.dtype)
67
+ action_goal_ori = np.zeros(
68
+ stacked_actions.shape[:-1]+(3,),
69
+ dtype=stacked_actions.dtype)
70
+ action_gripper = stacked_actions[...,[-1]]
71
+ for i in range(len(states)):
72
+ _ = env.reset_to({'states': states[i]})
73
+
74
+ # taken from robot_env.py L#454
75
+ for idx, robot in enumerate(env.env.robots):
76
+ # run controller goal generator
77
+ robot.control(stacked_actions[i,idx], policy_step=True)
78
+
79
+ # read pos and ori from robots
80
+ controller = robot.controller
81
+ action_goal_pos[i,idx] = controller.goal_pos
82
+ action_goal_ori[i,idx] = Rotation.from_matrix(
83
+ controller.goal_ori).as_rotvec()
84
+
85
+ stacked_abs_actions = np.concatenate([
86
+ action_goal_pos,
87
+ action_goal_ori,
88
+ action_gripper
89
+ ], axis=-1)
90
+ abs_actions = stacked_abs_actions.reshape(actions.shape)
91
+ return abs_actions
92
+
93
+ def convert_idx(self, idx):
94
+ file = self.file
95
+ demo = file[f'data/demo_{idx}']
96
+ # input
97
+ states = demo['states'][:]
98
+ actions = demo['actions'][:]
99
+
100
+ # generate abs actions
101
+ abs_actions = self.convert_actions(states, actions)
102
+ return abs_actions
103
+
104
+ def convert_and_eval_idx(self, idx):
105
+ env = self.env
106
+ abs_env = self.abs_env
107
+ file = self.file
108
+ # first step have high error for some reason, not representative
109
+ eval_skip_steps = 1
110
+
111
+ demo = file[f'data/demo_{idx}']
112
+ # input
113
+ states = demo['states'][:]
114
+ actions = demo['actions'][:]
115
+
116
+ # generate abs actions
117
+ abs_actions = self.convert_actions(states, actions)
118
+
119
+ # verify
120
+ robot0_eef_pos = demo['obs']['robot0_eef_pos'][:]
121
+ robot0_eef_quat = demo['obs']['robot0_eef_quat'][:]
122
+
123
+ delta_error_info = self.evaluate_rollout_error(
124
+ env, states, actions, robot0_eef_pos, robot0_eef_quat,
125
+ metric_skip_steps=eval_skip_steps)
126
+ abs_error_info = self.evaluate_rollout_error(
127
+ abs_env, states, abs_actions, robot0_eef_pos, robot0_eef_quat,
128
+ metric_skip_steps=eval_skip_steps)
129
+
130
+ info = {
131
+ 'delta_max_error': delta_error_info,
132
+ 'abs_max_error': abs_error_info
133
+ }
134
+ return abs_actions, info
135
+
136
+ @staticmethod
137
+ def evaluate_rollout_error(env,
138
+ states, actions,
139
+ robot0_eef_pos,
140
+ robot0_eef_quat,
141
+ metric_skip_steps=1):
142
+ # first step have high error for some reason, not representative
143
+
144
+ # evaluate abs actions
145
+ rollout_next_states = list()
146
+ rollout_next_eef_pos = list()
147
+ rollout_next_eef_quat = list()
148
+ obs = env.reset_to({'states': states[0]})
149
+ for i in range(len(states)):
150
+ obs = env.reset_to({'states': states[i]})
151
+ obs, reward, done, info = env.step(actions[i])
152
+ obs = env.get_observation()
153
+ rollout_next_states.append(env.get_state()['states'])
154
+ rollout_next_eef_pos.append(obs['robot0_eef_pos'])
155
+ rollout_next_eef_quat.append(obs['robot0_eef_quat'])
156
+ rollout_next_states = np.array(rollout_next_states)
157
+ rollout_next_eef_pos = np.array(rollout_next_eef_pos)
158
+ rollout_next_eef_quat = np.array(rollout_next_eef_quat)
159
+
160
+ next_state_diff = states[1:] - rollout_next_states[:-1]
161
+ max_next_state_diff = np.max(np.abs(next_state_diff[metric_skip_steps:]))
162
+
163
+ next_eef_pos_diff = robot0_eef_pos[1:] - rollout_next_eef_pos[:-1]
164
+ next_eef_pos_dist = np.linalg.norm(next_eef_pos_diff, axis=-1)
165
+ max_next_eef_pos_dist = next_eef_pos_dist[metric_skip_steps:].max()
166
+
167
+ next_eef_rot_diff = Rotation.from_quat(robot0_eef_quat[1:]) \
168
+ * Rotation.from_quat(rollout_next_eef_quat[:-1]).inv()
169
+ next_eef_rot_dist = next_eef_rot_diff.magnitude()
170
+ max_next_eef_rot_dist = next_eef_rot_dist[metric_skip_steps:].max()
171
+
172
+ info = {
173
+ 'state': max_next_state_diff,
174
+ 'pos': max_next_eef_pos_dist,
175
+ 'rot': max_next_eef_rot_dist
176
+ }
177
+ return info
common/sampler.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+ import numpy as np
3
+ import numba
4
+ from diffusion_policy.common.replay_buffer import ReplayBuffer
5
+
6
+ import pdb
7
+
8
+ @numba.jit(nopython=True)
9
+ def create_indices(
10
+ episode_ends:np.ndarray, sequence_length:int,
11
+ episode_mask: np.ndarray,
12
+ pad_before: int=0, pad_after: int=0,
13
+ debug:bool=True) -> np.ndarray:
14
+ episode_mask.shape == episode_ends.shape
15
+ pad_before = min(max(pad_before, 0), sequence_length-1)
16
+ pad_after = min(max(pad_after, 0), sequence_length-1)
17
+
18
+ indices = list()
19
+ for i in range(len(episode_ends)):
20
+ if not episode_mask[i]:
21
+ # skip episode
22
+ continue
23
+ start_idx = 0
24
+ if i > 0:
25
+ start_idx = episode_ends[i-1]
26
+ end_idx = episode_ends[i]
27
+ episode_length = end_idx - start_idx
28
+
29
+ min_start = -pad_before
30
+ max_start = episode_length - sequence_length + pad_after
31
+
32
+ # range stops one idx before end
33
+ for idx in range(min_start, max_start+1):
34
+ buffer_start_idx = max(idx, 0) + start_idx
35
+ buffer_end_idx = min(idx+sequence_length, episode_length) + start_idx
36
+ start_offset = buffer_start_idx - (idx+start_idx)
37
+ end_offset = (idx+sequence_length+start_idx) - buffer_end_idx
38
+ sample_start_idx = 0 + start_offset
39
+ sample_end_idx = sequence_length - end_offset
40
+ if debug:
41
+ assert(start_offset >= 0)
42
+ assert(end_offset >= 0)
43
+ assert (sample_end_idx - sample_start_idx) == (buffer_end_idx - buffer_start_idx)
44
+ indices.append([
45
+ buffer_start_idx, buffer_end_idx,
46
+ sample_start_idx, sample_end_idx])
47
+ indices = np.array(indices)
48
+ return indices
49
+
50
+
51
+ def get_val_mask(n_episodes, val_ratio, seed=0):
52
+ val_mask = np.zeros(n_episodes, dtype=bool)
53
+ if val_ratio <= 0:
54
+ return val_mask
55
+
56
+ # have at least 1 episode for validation, and at least 1 episode for train
57
+ n_val = min(max(1, round(n_episodes * val_ratio)), n_episodes-1)
58
+ rng = np.random.default_rng(seed=seed)
59
+ val_idxs = rng.choice(n_episodes, size=n_val, replace=False)
60
+ val_mask[val_idxs] = True
61
+ return val_mask
62
+
63
+
64
+ def downsample_mask(mask, max_n, seed=0):
65
+ # subsample training data
66
+ train_mask = mask
67
+ if (max_n is not None) and (np.sum(train_mask) > max_n):
68
+ n_train = int(max_n)
69
+ curr_train_idxs = np.nonzero(train_mask)[0]
70
+ rng = np.random.default_rng(seed=seed)
71
+ train_idxs_idx = rng.choice(len(curr_train_idxs), size=n_train, replace=False)
72
+ train_idxs = curr_train_idxs[train_idxs_idx]
73
+ train_mask = np.zeros_like(train_mask)
74
+ train_mask[train_idxs] = True
75
+ assert np.sum(train_mask) == n_train
76
+ return train_mask
77
+
78
+ class SequenceSampler:
79
+ def __init__(self,
80
+ replay_buffer: ReplayBuffer,
81
+ sequence_length:int,
82
+ pad_before:int=0,
83
+ pad_after:int=0,
84
+ keys=None,
85
+ key_first_k=dict(),
86
+ episode_mask: Optional[np.ndarray]=None,
87
+ ):
88
+ """
89
+ key_first_k: dict str: int
90
+ Only take first k data from these keys (to improve perf)
91
+ """
92
+
93
+ super().__init__()
94
+ assert(sequence_length >= 1)
95
+ if keys is None:
96
+ keys = list(replay_buffer.keys())
97
+
98
+ episode_ends = replay_buffer.episode_ends[:]
99
+ if episode_mask is None:
100
+ episode_mask = np.ones(episode_ends.shape, dtype=bool)
101
+
102
+ if np.any(episode_mask):
103
+ indices = create_indices(episode_ends,
104
+ sequence_length=sequence_length,
105
+ pad_before=pad_before,
106
+ pad_after=pad_after,
107
+ episode_mask=episode_mask
108
+ )
109
+ else:
110
+ indices = np.zeros((0,4), dtype=np.int64)
111
+
112
+ # (buffer_start_idx, buffer_end_idx, sample_start_idx, sample_end_idx)
113
+ self.indices = indices
114
+ self.keys = list(keys) # prevent OmegaConf list performance problem
115
+ self.sequence_length = sequence_length
116
+ self.replay_buffer = replay_buffer
117
+ self.key_first_k = key_first_k
118
+
119
+ def __len__(self):
120
+ return len(self.indices)
121
+
122
+ def sample_sequence(self, idx):
123
+ buffer_start_idx, buffer_end_idx, sample_start_idx, sample_end_idx \
124
+ = self.indices[idx]
125
+ result = dict()
126
+ for key in self.keys:
127
+ input_arr = self.replay_buffer[key]
128
+ # performance optimization, avoid small allocation if possible
129
+ if key not in self.key_first_k:
130
+ sample = input_arr[buffer_start_idx:buffer_end_idx]
131
+ else:
132
+ # performance optimization, only load used obs steps
133
+ n_data = buffer_end_idx - buffer_start_idx
134
+ k_data = min(self.key_first_k[key], n_data)
135
+ # fill value with Nan to catch bugs
136
+ # the non-loaded region should never be used
137
+ sample = np.full((n_data,) + input_arr.shape[1:],
138
+ fill_value=np.nan, dtype=input_arr.dtype)
139
+ try:
140
+ sample[:k_data] = input_arr[buffer_start_idx:buffer_start_idx+k_data]
141
+ except Exception as e:
142
+ import pdb; pdb.set_trace()
143
+ data = sample
144
+ if (sample_start_idx > 0) or (sample_end_idx < self.sequence_length):
145
+ data = np.zeros(
146
+ shape=(self.sequence_length,) + input_arr.shape[1:],
147
+ dtype=input_arr.dtype)
148
+ if sample_start_idx > 0:
149
+ data[:sample_start_idx] = sample[0]
150
+ if sample_end_idx < self.sequence_length:
151
+ data[sample_end_idx:] = sample[-1]
152
+ data[sample_start_idx:sample_end_idx] = sample
153
+ result[key] = data
154
+ return result
common/timestamp_accumulator.py ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Tuple, Optional, Dict
2
+ import math
3
+ import numpy as np
4
+
5
+
6
+ def get_accumulate_timestamp_idxs(
7
+ timestamps: List[float],
8
+ start_time: float,
9
+ dt: float,
10
+ eps:float=1e-5,
11
+ next_global_idx: Optional[int]=0,
12
+ allow_negative=False
13
+ ) -> Tuple[List[int], List[int], int]:
14
+ """
15
+ For each dt window, choose the first timestamp in the window.
16
+ Assumes timestamps sorted. One timestamp might be chosen multiple times due to dropped frames.
17
+ next_global_idx should start at 0 normally, and then use the returned next_global_idx.
18
+ However, when overwiting previous values are desired, set last_global_idx to None.
19
+
20
+ Returns:
21
+ local_idxs: which index in the given timestamps array to chose from
22
+ global_idxs: the global index of each chosen timestamp
23
+ next_global_idx: used for next call.
24
+ """
25
+ local_idxs = list()
26
+ global_idxs = list()
27
+ for local_idx, ts in enumerate(timestamps):
28
+ # add eps * dt to timestamps so that when ts == start_time + k * dt
29
+ # is always recorded as kth element (avoiding floating point errors)
30
+ global_idx = math.floor((ts - start_time) / dt + eps)
31
+ if (not allow_negative) and (global_idx < 0):
32
+ continue
33
+ if next_global_idx is None:
34
+ next_global_idx = global_idx
35
+
36
+ n_repeats = max(0, global_idx - next_global_idx + 1)
37
+ for i in range(n_repeats):
38
+ local_idxs.append(local_idx)
39
+ global_idxs.append(next_global_idx + i)
40
+ next_global_idx += n_repeats
41
+ return local_idxs, global_idxs, next_global_idx
42
+
43
+
44
+ def align_timestamps(
45
+ timestamps: List[float],
46
+ target_global_idxs: List[int],
47
+ start_time: float,
48
+ dt: float,
49
+ eps:float=1e-5):
50
+ if isinstance(target_global_idxs, np.ndarray):
51
+ target_global_idxs = target_global_idxs.tolist()
52
+ assert len(target_global_idxs) > 0
53
+
54
+ local_idxs, global_idxs, _ = get_accumulate_timestamp_idxs(
55
+ timestamps=timestamps,
56
+ start_time=start_time,
57
+ dt=dt,
58
+ eps=eps,
59
+ next_global_idx=target_global_idxs[0],
60
+ allow_negative=True
61
+ )
62
+ if len(global_idxs) > len(target_global_idxs):
63
+ # if more steps available, truncate
64
+ global_idxs = global_idxs[:len(target_global_idxs)]
65
+ local_idxs = local_idxs[:len(target_global_idxs)]
66
+
67
+ if len(global_idxs) == 0:
68
+ import pdb; pdb.set_trace()
69
+
70
+ for i in range(len(target_global_idxs) - len(global_idxs)):
71
+ # if missing, repeat
72
+ local_idxs.append(len(timestamps)-1)
73
+ global_idxs.append(global_idxs[-1] + 1)
74
+ assert global_idxs == target_global_idxs
75
+ assert len(local_idxs) == len(global_idxs)
76
+ return local_idxs
77
+
78
+
79
+ class TimestampObsAccumulator:
80
+ def __init__(self,
81
+ start_time: float,
82
+ dt: float,
83
+ eps: float=1e-5):
84
+ self.start_time = start_time
85
+ self.dt = dt
86
+ self.eps = eps
87
+ self.obs_buffer = dict()
88
+ self.timestamp_buffer = None
89
+ self.next_global_idx = 0
90
+
91
+ def __len__(self):
92
+ return self.next_global_idx
93
+
94
+ @property
95
+ def data(self):
96
+ if self.timestamp_buffer is None:
97
+ return dict()
98
+ result = dict()
99
+ for key, value in self.obs_buffer.items():
100
+ result[key] = value[:len(self)]
101
+ return result
102
+
103
+ @property
104
+ def actual_timestamps(self):
105
+ if self.timestamp_buffer is None:
106
+ return np.array([])
107
+ return self.timestamp_buffer[:len(self)]
108
+
109
+ @property
110
+ def timestamps(self):
111
+ if self.timestamp_buffer is None:
112
+ return np.array([])
113
+ return self.start_time + np.arange(len(self)) * self.dt
114
+
115
+ def put(self, data: Dict[str, np.ndarray], timestamps: np.ndarray):
116
+ """
117
+ data:
118
+ key: T,*
119
+ """
120
+
121
+ local_idxs, global_idxs, self.next_global_idx = get_accumulate_timestamp_idxs(
122
+ timestamps=timestamps,
123
+ start_time=self.start_time,
124
+ dt=self.dt,
125
+ eps=self.eps,
126
+ next_global_idx=self.next_global_idx
127
+ )
128
+
129
+ if len(global_idxs) > 0:
130
+ if self.timestamp_buffer is None:
131
+ # first allocation
132
+ self.obs_buffer = dict()
133
+ for key, value in data.items():
134
+ self.obs_buffer[key] = np.zeros_like(value)
135
+ self.timestamp_buffer = np.zeros(
136
+ (len(timestamps),), dtype=np.float64)
137
+
138
+ this_max_size = global_idxs[-1] + 1
139
+ if this_max_size > len(self.timestamp_buffer):
140
+ # reallocate
141
+ new_size = max(this_max_size, len(self.timestamp_buffer) * 2)
142
+ for key in list(self.obs_buffer.keys()):
143
+ new_shape = (new_size,) + self.obs_buffer[key].shape[1:]
144
+ self.obs_buffer[key] = np.resize(self.obs_buffer[key], new_shape)
145
+ self.timestamp_buffer = np.resize(self.timestamp_buffer, (new_size))
146
+
147
+ # write data
148
+ for key, value in self.obs_buffer.items():
149
+ value[global_idxs] = data[key][local_idxs]
150
+ self.timestamp_buffer[global_idxs] = timestamps[local_idxs]
151
+
152
+
153
+ class TimestampActionAccumulator:
154
+ def __init__(self,
155
+ start_time: float,
156
+ dt: float,
157
+ eps: float=1e-5):
158
+ """
159
+ Different from Obs accumulator, the action accumulator
160
+ allows overwriting previous values.
161
+ """
162
+ self.start_time = start_time
163
+ self.dt = dt
164
+ self.eps = eps
165
+ self.action_buffer = None
166
+ self.timestamp_buffer = None
167
+ self.size = 0
168
+
169
+ def __len__(self):
170
+ return self.size
171
+
172
+ @property
173
+ def actions(self):
174
+ if self.action_buffer is None:
175
+ return np.array([])
176
+ return self.action_buffer[:len(self)]
177
+
178
+ @property
179
+ def actual_timestamps(self):
180
+ if self.timestamp_buffer is None:
181
+ return np.array([])
182
+ return self.timestamp_buffer[:len(self)]
183
+
184
+ @property
185
+ def timestamps(self):
186
+ if self.timestamp_buffer is None:
187
+ return np.array([])
188
+ return self.start_time + np.arange(len(self)) * self.dt
189
+
190
+ def put(self, actions: np.ndarray, timestamps: np.ndarray):
191
+ """
192
+ Note: timestamps is the time when the action will be issued,
193
+ not when the action will be completed (target_timestamp)
194
+ """
195
+
196
+ local_idxs, global_idxs, _ = get_accumulate_timestamp_idxs(
197
+ timestamps=timestamps,
198
+ start_time=self.start_time,
199
+ dt=self.dt,
200
+ eps=self.eps,
201
+ # allows overwriting previous actions
202
+ next_global_idx=None
203
+ )
204
+
205
+ if len(global_idxs) > 0:
206
+ if self.timestamp_buffer is None:
207
+ # first allocation
208
+ self.action_buffer = np.zeros_like(actions)
209
+ self.timestamp_buffer = np.zeros((len(actions),), dtype=np.float64)
210
+
211
+ this_max_size = global_idxs[-1] + 1
212
+ if this_max_size > len(self.timestamp_buffer):
213
+ # reallocate
214
+ new_size = max(this_max_size, len(self.timestamp_buffer) * 2)
215
+ new_shape = (new_size,) + self.action_buffer.shape[1:]
216
+ self.action_buffer = np.resize(self.action_buffer, new_shape)
217
+ self.timestamp_buffer = np.resize(self.timestamp_buffer, (new_size,))
218
+
219
+ # potentially rewrite old data (as expected)
220
+ self.action_buffer[global_idxs] = actions[local_idxs]
221
+ self.timestamp_buffer[global_idxs] = timestamps[local_idxs]
222
+ self.size = max(self.size, this_max_size)
config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_can_mh_abs.yaml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: can_lowdim_abs
4
+
5
+ name: datacollect_diffusion_unet_lowdim
6
+ _target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
7
+ checkpoint_dir: null
8
+
9
+ task_name: ${task.name}
10
+
11
+ horizon: 16
12
+ n_obs_steps: 2
13
+ n_action_steps: 8
14
+ n_latency_steps: 0
15
+ past_action_visible: False
16
+ keypoint_visible_rate: 1.0
17
+
18
+ collecting:
19
+ device: "cuda:0"
20
+ seed: 42
21
+ num_episodes: 500 # number of episodes to collect
22
+ render_image: True
23
+
24
+
25
+ hydra:
26
+ job:
27
+ override_dirname: ${name}
28
+ run:
29
+ dir: logs/collect_data/${task_name}/${name}_${task_name}
config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_kitchen_abs.yaml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: kitchen_lowdim_abs
4
+
5
+ name: datacollect_diffusion_unet_lowdim
6
+ _target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
7
+ checkpoint_dir: null
8
+
9
+ task_name: ${task.name}
10
+
11
+ horizon: 16
12
+ n_obs_steps: 2
13
+ n_action_steps: 8
14
+ n_latency_steps: 0
15
+ past_action_visible: False
16
+ keypoint_visible_rate: 1.0
17
+
18
+ collecting:
19
+ device: "cuda:0"
20
+ seed: 42
21
+ num_episodes: 500 # number of episodes to collect
22
+ render_image: True
23
+
24
+
25
+ hydra:
26
+ job:
27
+ override_dirname: ${name}
28
+ run:
29
+ dir: logs/collect_data/${task_name}/${name}_${task_name}
config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_lift_mh_abs.yaml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: lift_lowdim_abs
4
+
5
+ name: datacollect_diffusion_unet_lowdim
6
+ _target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
7
+ checkpoint_dir: null
8
+
9
+ task_name: ${task.name}
10
+
11
+ horizon: 16
12
+ n_obs_steps: 2
13
+ n_action_steps: 8
14
+ n_latency_steps: 0
15
+ past_action_visible: False
16
+ keypoint_visible_rate: 1.0
17
+
18
+ collecting:
19
+ device: "cuda:0"
20
+ seed: 42
21
+ num_episodes: 500 # number of episodes to collect
22
+ render_image: True
23
+
24
+
25
+ hydra:
26
+ job:
27
+ override_dirname: ${name}
28
+ run:
29
+ dir: logs/collect_data/${task_name}/${name}_${task_name}
config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_pusht.yaml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: pusht_lowdim
4
+
5
+ name: datacollect_diffusion_unet_lowdim
6
+ _target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
7
+ checkpoint_dir: null
8
+
9
+ task_name: ${task.name}
10
+
11
+ horizon: 16
12
+ n_obs_steps: 2
13
+ n_action_steps: 8
14
+ n_latency_steps: 0
15
+ past_action_visible: False
16
+ keypoint_visible_rate: 1.0
17
+
18
+ collecting:
19
+ device: "cuda:0"
20
+ seed: 42
21
+ num_episodes: 500 # number of episodes to collect
22
+ render_image: True
23
+
24
+
25
+ hydra:
26
+ job:
27
+ override_dirname: ${name}
28
+ run:
29
+ dir: logs/collect_data/${task_name}/${name}_${task_name}
config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_square_mh_abs.yaml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: square_lowdim_abs
4
+
5
+ name: datacollect_diffusion_unet_lowdim
6
+ _target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
7
+ checkpoint_dir: null
8
+
9
+ task_name: ${task.name}
10
+
11
+ horizon: 16
12
+ n_obs_steps: 2
13
+ n_action_steps: 8
14
+ n_latency_steps: 0
15
+ past_action_visible: False
16
+ keypoint_visible_rate: 1.0
17
+
18
+ collecting:
19
+ device: "cuda:0"
20
+ seed: 42
21
+ num_episodes: 500 # number of episodes to collect
22
+ render_image: True
23
+
24
+
25
+ hydra:
26
+ job:
27
+ override_dirname: ${name}
28
+ run:
29
+ dir: logs/collect_data/${task_name}/${name}_${task_name}
config/data_collect/diffusion_policy_cnn/datacollect_diffusion_policy_cnn_lowdim_transport_mh_abs.yaml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: transport_lowdim_abs
4
+
5
+ name: datacollect_diffusion_unet_lowdim
6
+ _target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
7
+ checkpoint_dir: null
8
+
9
+ task_name: ${task.name}
10
+
11
+ horizon: 16
12
+ n_obs_steps: 2
13
+ n_action_steps: 8
14
+ n_latency_steps: 0
15
+ past_action_visible: False
16
+ keypoint_visible_rate: 1.0
17
+
18
+ collecting:
19
+ device: "cuda:0"
20
+ seed: 42
21
+ num_episodes: 500 # number of episodes to collect
22
+ render_image: True
23
+
24
+
25
+ hydra:
26
+ job:
27
+ override_dirname: ${name}
28
+ run:
29
+ dir: logs/collect_data/${task_name}/${name}_${task_name}
config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_can_mh_abs.yaml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: can_lowdim_abs
4
+
5
+ name: datacollect_diffusion_transformer_lowdim
6
+ _target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
7
+ checkpoint_dir: null
8
+
9
+ task_name: ${task.name}
10
+
11
+ horizon: 10
12
+ n_obs_steps: 2
13
+ n_action_steps: 8
14
+ n_latency_steps: 0
15
+ past_action_visible: False
16
+ keypoint_visible_rate: 1.0
17
+
18
+ collecting:
19
+ device: "cuda:0"
20
+ seed: 42
21
+ num_episodes: 500 # number of episodes to collect
22
+ render_image: True
23
+
24
+
25
+ hydra:
26
+ job:
27
+ override_dirname: ${name}
28
+ run:
29
+ dir: logs/collect_data/${task_name}/${name}_${task_name}
config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_kitchen_abs.yaml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: kitchen_lowdim_abs
4
+
5
+ name: datacollect_diffusion_transformer_lowdim
6
+ _target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
7
+ checkpoint_dir: null
8
+
9
+ task_name: ${task.name}
10
+
11
+ horizon: 16
12
+ n_obs_steps: 4
13
+ n_action_steps: 8
14
+ n_latency_steps: 0
15
+ past_action_visible: False
16
+ keypoint_visible_rate: 1.0
17
+
18
+ collecting:
19
+ device: "cuda:0"
20
+ seed: 42
21
+ num_episodes: 500 # number of episodes to collect
22
+ render_image: True
23
+
24
+
25
+ hydra:
26
+ job:
27
+ override_dirname: ${name}
28
+ run:
29
+ dir: logs/collect_data/${task_name}/${name}_${task_name}
config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_lift_mh_abs.yaml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: lift_lowdim_abs
4
+
5
+ name: datacollect_diffusion_transformer_lowdim
6
+ _target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
7
+ checkpoint_dir: null
8
+
9
+ task_name: ${task.name}
10
+
11
+ horizon: 10
12
+ n_obs_steps: 2
13
+ n_action_steps: 8
14
+ n_latency_steps: 0
15
+ past_action_visible: False
16
+ keypoint_visible_rate: 1.0
17
+
18
+ collecting:
19
+ device: "cuda:0"
20
+ seed: 42
21
+ num_episodes: 500 # number of episodes to collect
22
+ render_image: True
23
+
24
+
25
+ hydra:
26
+ job:
27
+ override_dirname: ${name}
28
+ run:
29
+ dir: logs/collect_data/${task_name}/${name}_${task_name}
config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_pusht.yaml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: pusht_lowdim
4
+
5
+ name: datacollect_diffusion_transformer_lowdim
6
+ _target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
7
+ checkpoint_dir: null
8
+
9
+ task_name: ${task.name}
10
+
11
+ horizon: 16
12
+ n_obs_steps: 2
13
+ n_action_steps: 8
14
+ n_latency_steps: 0
15
+ past_action_visible: False
16
+ keypoint_visible_rate: 1.0
17
+
18
+ collecting:
19
+ device: "cuda:0"
20
+ seed: 42
21
+ num_episodes: 500 # number of episodes to collect
22
+ render_image: True
23
+
24
+
25
+ hydra:
26
+ job:
27
+ override_dirname: ${name}
28
+ run:
29
+ dir: logs/collect_data/${task_name}/${name}_${task_name}
config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_square_mh_abs.yaml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: square_lowdim_abs
4
+
5
+ name: datacollect_diffusion_transformer_lowdim
6
+ _target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
7
+ checkpoint_dir: null
8
+
9
+ task_name: ${task.name}
10
+
11
+ horizon: 10
12
+ n_obs_steps: 2
13
+ n_action_steps: 8
14
+ n_latency_steps: 0
15
+ past_action_visible: False
16
+ keypoint_visible_rate: 1.0
17
+
18
+ collecting:
19
+ device: "cuda:0"
20
+ seed: 42
21
+ num_episodes: 500 # number of episodes to collect
22
+ render_image: True
23
+
24
+
25
+ hydra:
26
+ job:
27
+ override_dirname: ${name}
28
+ run:
29
+ dir: logs/collect_data/${task_name}/${name}_${task_name}
config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_tool_hang_ph_abs.yaml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: tool_hang_lowdim_abs
4
+
5
+ name: datacollect_diffusion_transformer_lowdim
6
+ _target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
7
+ checkpoint_dir: null
8
+
9
+ task_name: ${task.name}
10
+
11
+ horizon: 10
12
+ n_obs_steps: 2
13
+ n_action_steps: 8
14
+ n_latency_steps: 0
15
+ past_action_visible: False
16
+ keypoint_visible_rate: 1.0
17
+
18
+ collecting:
19
+ device: "cuda:0"
20
+ seed: 42
21
+ num_episodes: 500 # number of episodes to collect
22
+ render_image: True
23
+
24
+
25
+ hydra:
26
+ job:
27
+ override_dirname: ${name}
28
+ run:
29
+ dir: logs/collect_data/${task_name}/${name}_${task_name}
config/data_collect/diffusion_policy_transformer/datacollect_diffusion_policy_transformer_lowdim_transport_mh_abs.yaml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: transport_lowdim_abs
4
+
5
+ name: datacollect_diffusion_transformer_lowdim
6
+ _target_: diffusion_policy.workspace.datacollect_diffusion_lowdim_workspace.DatacollectDiffusionLowdimWorkspace
7
+ checkpoint_dir: null
8
+
9
+ task_name: ${task.name}
10
+
11
+ horizon: 10
12
+ n_obs_steps: 2
13
+ n_action_steps: 8
14
+ n_latency_steps: 0
15
+ past_action_visible: False
16
+ keypoint_visible_rate: 1.0
17
+
18
+ collecting:
19
+ device: "cuda:0"
20
+ seed: 42
21
+ num_episodes: 500 # number of episodes to collect
22
+ render_image: True
23
+
24
+
25
+ hydra:
26
+ job:
27
+ override_dirname: ${name}
28
+ run:
29
+ dir: logs/collect_data/${task_name}/${name}_${task_name}
config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_can_mh_abs.yaml ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: can_lowdim_abs_pbrl
4
+
5
+ name: train_pbrl_diffusion_transformer
6
+
7
+ _target_: diffusion_policy.workspace.train_pbrl_diffusion_transformer_lowdim_offline_workspace.PbrlDiffusionTransformerLowdimWorkspace
8
+ checkpoint_dir: logs/pretrain/can_lowdim/train_diffusion_transformer/checkpoints/epoch=0040-test_mean_score=0.380.ckpt
9
+
10
+ obs_dim: ${task.obs_dim}
11
+ action_dim: ${task.action_dim}
12
+ keypoint_dim: ${task.keypoint_dim}
13
+ task_name: ${task.name}
14
+
15
+ gamma: 0.999 # discount factor in MDP
16
+ horizon: 10
17
+ n_obs_steps: 2
18
+ n_action_steps: 8
19
+ n_latency_steps: 0
20
+ past_action_visible: False
21
+ keypoint_visible_rate: 1.0
22
+ obs_as_cond: True
23
+ pred_action_steps_only: False
24
+
25
+ policy:
26
+ _target_: diffusion_policy.policy.cpl_diffusion_transformer_lowdim_policy.CplDiffusionTransformerLowdimPolicy
27
+
28
+ model:
29
+ _target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
30
+ input_dim: ${eval:'${action_dim} if ${obs_as_cond} else ${obs_dim} + ${action_dim}'}
31
+ output_dim: ${policy.model.input_dim}
32
+ horizon: ${horizon}
33
+ n_obs_steps: ${n_obs_steps}
34
+ cond_dim: ${eval:'${obs_dim} if ${obs_as_cond} else 0'}
35
+
36
+ n_layer: 8
37
+ n_head: 4
38
+ n_emb: 256
39
+ p_drop_emb: 0.0
40
+ p_drop_attn: 0.3
41
+
42
+ causal_attn: True
43
+ time_as_cond: True # if false, use BERT like encoder only arch, time as input
44
+ obs_as_cond: ${obs_as_cond}
45
+ n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
46
+
47
+ noise_scheduler:
48
+ _target_: diffusers.schedulers.scheduling_ddpm.DDPMScheduler
49
+ num_train_timesteps: 100
50
+ beta_start: 0.0001
51
+ beta_end: 0.02
52
+ beta_schedule: squaredcos_cap_v2
53
+ variance_type: fixed_small # Yilun's paper uses fixed_small_log instead, but easy to cause Nan
54
+ clip_sample: True # required when predict_epsilon=False
55
+ prediction_type: epsilon # or sample
56
+
57
+ horizon: ${horizon}
58
+ obs_dim: ${obs_dim}
59
+ action_dim: ${action_dim}
60
+ n_action_steps: ${n_action_steps}
61
+ n_obs_steps: ${n_obs_steps}
62
+ num_inference_steps: 100
63
+ obs_as_cond: ${obs_as_cond}
64
+ pred_action_steps_only: ${pred_action_steps_only}
65
+
66
+ beta: 0.1
67
+ bias_reg: 0.25
68
+ ignore_equal_pref: False
69
+ clip_margin: 1.0
70
+ smooth_label: 0.0
71
+ confidence_weight: False
72
+ cw_temperature: 0.03
73
+ unclip_win: False
74
+
75
+ ema:
76
+ _target_: diffusion_policy.model.diffusion.ema_model.EMAModel
77
+ update_after_step: 0
78
+ inv_gamma: 1.0 # TODO: UAPA paper use : 2.0
79
+ power: 0.75 # TODO: UAPA paper use : 0.9
80
+ min_value: 0.0
81
+ max_value: 0.9999
82
+
83
+ dataloader:
84
+ batch_size: 128
85
+ num_workers: 1
86
+ shuffle: True
87
+ pin_memory: True
88
+ persistent_workers: False
89
+
90
+
91
+ optimizer:
92
+ learning_rate: 1.0e-4 # 1.0e-4
93
+ weight_decay: 1.0e-3
94
+ betas: [0.9, 0.95]
95
+
96
+ training:
97
+ device: "cuda:0"
98
+ seed: 42
99
+ debug: False
100
+ resume: True
101
+ # optimization
102
+ lr_scheduler: cosine
103
+ # Transformer needs LR warmup
104
+ lr_warmup_steps: 0
105
+ num_epochs: 500
106
+ gradient_accumulate_every: 1
107
+ use_ema: True
108
+ # training loop control
109
+ # in epochs
110
+ rollout_every: 25
111
+ checkpoint_every: 25
112
+ val_every: 1
113
+ sample_every: 5
114
+ # steps per epoch
115
+ max_train_steps: null
116
+ max_val_steps: null
117
+ # misc
118
+ tqdm_interval_sec: 1.0
119
+
120
+ cpl_loss_type: 'cplkl' # 'cpl'
121
+ use_expert_data_1: False
122
+ use_expert_data_2: False
123
+ n_epoch_sft: 0
124
+ sft_type: "pos"
125
+ stride_ratio: 0.5
126
+ pseudo_preference: False
127
+
128
+ dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
129
+ dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
130
+ preference_learning:
131
+ num_rounds: 1
132
+ equal_threshold: 0.05
133
+
134
+ logging:
135
+ project: IL-DP-2026
136
+ resume: False
137
+ mode: online
138
+ name: ${name}_${training.seed}
139
+ tags: ["${task_name}"]
140
+ id: null
141
+ group: ${name}
142
+
143
+ checkpoint:
144
+ topk:
145
+ monitor_key: test_mean_score
146
+ mode: max
147
+ k: 3
148
+ format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
149
+ save_last_ckpt: True
150
+ save_last_snapshot: False
151
+
152
+ multi_run:
153
+ run_dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
154
+ wandb_name_base: ${name}_${task_name}
155
+
156
+ hydra:
157
+ job:
158
+ override_dirname: ${name}
159
+ run:
160
+ dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
161
+ sweep:
162
+ dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
163
+ subdir: ${hydra.job.num}
config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_lift_mh_abs.yaml ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: lift_lowdim_abs_pbrl
4
+
5
+ name: train_pbrl_diffusion_transformer
6
+
7
+ _target_: diffusion_policy.workspace.train_pbrl_diffusion_transformer_lowdim_offline_workspace.PbrlDiffusionTransformerLowdimWorkspace
8
+ checkpoint_dir: logs/pretrain/lift_lowdim/train_diffusion_transformer/checkpoints/epoch=0015-test_mean_score=0.400.ckpt
9
+
10
+ obs_dim: ${task.obs_dim}
11
+ action_dim: ${task.action_dim}
12
+ keypoint_dim: ${task.keypoint_dim}
13
+ task_name: ${task.name}
14
+
15
+ gamma: 0.999 # discount factor in MDP
16
+ horizon: 10
17
+ n_obs_steps: 2
18
+ n_action_steps: 8
19
+ n_latency_steps: 0
20
+ past_action_visible: False
21
+ keypoint_visible_rate: 1.0
22
+ obs_as_cond: True
23
+ pred_action_steps_only: False
24
+
25
+ policy:
26
+ _target_: diffusion_policy.policy.cpl_diffusion_transformer_lowdim_policy.CplDiffusionTransformerLowdimPolicy
27
+
28
+ model:
29
+ _target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
30
+ input_dim: ${eval:'${action_dim} if ${obs_as_cond} else ${obs_dim} + ${action_dim}'}
31
+ output_dim: ${policy.model.input_dim}
32
+ horizon: ${horizon}
33
+ n_obs_steps: ${n_obs_steps}
34
+ cond_dim: ${eval:'${obs_dim} if ${obs_as_cond} else 0'}
35
+
36
+ n_layer: 8
37
+ n_head: 4
38
+ n_emb: 256
39
+ p_drop_emb: 0.0
40
+ p_drop_attn: 0.3
41
+
42
+ causal_attn: True
43
+ time_as_cond: True # if false, use BERT like encoder only arch, time as input
44
+ obs_as_cond: ${obs_as_cond}
45
+ n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
46
+
47
+ noise_scheduler:
48
+ _target_: diffusers.schedulers.scheduling_ddpm.DDPMScheduler
49
+ num_train_timesteps: 100
50
+ beta_start: 0.0001
51
+ beta_end: 0.02
52
+ beta_schedule: squaredcos_cap_v2
53
+ variance_type: fixed_small # Yilun's paper uses fixed_small_log instead, but easy to cause Nan
54
+ clip_sample: True # required when predict_epsilon=False
55
+ prediction_type: epsilon # or sample
56
+
57
+ horizon: ${horizon}
58
+ obs_dim: ${obs_dim}
59
+ action_dim: ${action_dim}
60
+ n_action_steps: ${n_action_steps}
61
+ n_obs_steps: ${n_obs_steps}
62
+ num_inference_steps: 100
63
+ obs_as_cond: ${obs_as_cond}
64
+ pred_action_steps_only: ${pred_action_steps_only}
65
+
66
+ beta: 0.1
67
+ bias_reg: 0.25
68
+ ignore_equal_pref: False
69
+ clip_margin: 1.0
70
+ smooth_label: 0.0
71
+ confidence_weight: False
72
+ cw_temperature: 0.03
73
+ unclip_win: False
74
+
75
+ ema:
76
+ _target_: diffusion_policy.model.diffusion.ema_model.EMAModel
77
+ update_after_step: 0
78
+ inv_gamma: 1.0 # TODO: UAPA paper use : 2.0
79
+ power: 0.75 # TODO: UAPA paper use : 0.9
80
+ min_value: 0.0
81
+ max_value: 0.9999
82
+
83
+ dataloader:
84
+ batch_size: 128
85
+ num_workers: 1
86
+ shuffle: True
87
+ pin_memory: True
88
+ persistent_workers: False
89
+
90
+
91
+ optimizer:
92
+ learning_rate: 1.0e-4 # 1.0e-4
93
+ weight_decay: 1.0e-3
94
+ betas: [0.9, 0.95]
95
+
96
+ training:
97
+ device: "cuda:0"
98
+ seed: 42
99
+ debug: False
100
+ resume: True
101
+ # optimization
102
+ lr_scheduler: cosine
103
+ # Transformer needs LR warmup
104
+ lr_warmup_steps: 0
105
+ num_epochs: 500
106
+ gradient_accumulate_every: 1
107
+ use_ema: True
108
+ # training loop control
109
+ # in epochs
110
+ rollout_every: 25
111
+ checkpoint_every: 25
112
+ val_every: 1
113
+ sample_every: 5
114
+ # steps per epoch
115
+ max_train_steps: null
116
+ max_val_steps: null
117
+ # misc
118
+ tqdm_interval_sec: 1.0
119
+
120
+ cpl_loss_type: 'cplkl' # 'cpl'
121
+ use_expert_data_1: False
122
+ use_expert_data_2: False
123
+ n_epoch_sft: 0
124
+ sft_type: "pos"
125
+ stride_ratio: 0.5
126
+ pseudo_preference: False
127
+
128
+ dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
129
+ dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
130
+ preference_learning:
131
+ num_rounds: 1
132
+ equal_threshold: 0.05
133
+
134
+ logging:
135
+ project: IL-DP-2026
136
+ resume: False
137
+ mode: online
138
+ name: ${name}_${training.seed}
139
+ tags: ["${task_name}"]
140
+ id: null
141
+ group: ${name}
142
+
143
+ checkpoint:
144
+ topk:
145
+ monitor_key: test_mean_score
146
+ mode: max
147
+ k: 3
148
+ format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
149
+ save_last_ckpt: True
150
+ save_last_snapshot: False
151
+
152
+ multi_run:
153
+ run_dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
154
+ wandb_name_base: ${name}_${task_name}
155
+
156
+ hydra:
157
+ job:
158
+ override_dirname: ${name}
159
+ run:
160
+ dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
161
+ sweep:
162
+ dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
163
+ subdir: ${hydra.job.num}
config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_square_mh_abs.yaml ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: square_lowdim_abs_pbrl
4
+
5
+ name: train_pbrl_diffusion_transformer
6
+
7
+ _target_: diffusion_policy.workspace.train_pbrl_diffusion_transformer_lowdim_offline_workspace.PbrlDiffusionTransformerLowdimWorkspace
8
+ checkpoint_dir: logs/pretrain/square_lowdim/train_diffusion_transformer/checkpoints/epoch=0450-test_mean_score=0.520.ckpt
9
+
10
+ obs_dim: ${task.obs_dim}
11
+ action_dim: ${task.action_dim}
12
+ keypoint_dim: ${task.keypoint_dim}
13
+ task_name: ${task.name}
14
+
15
+ gamma: 0.999 # discount factor in MDP
16
+ horizon: 10
17
+ n_obs_steps: 2
18
+ n_action_steps: 8
19
+ n_latency_steps: 0
20
+ past_action_visible: False
21
+ keypoint_visible_rate: 1.0
22
+ obs_as_cond: True
23
+ pred_action_steps_only: False
24
+
25
+ policy:
26
+ _target_: diffusion_policy.policy.cpl_diffusion_transformer_lowdim_policy.CplDiffusionTransformerLowdimPolicy
27
+
28
+ model:
29
+ _target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
30
+ input_dim: ${eval:'${action_dim} if ${obs_as_cond} else ${obs_dim} + ${action_dim}'}
31
+ output_dim: ${policy.model.input_dim}
32
+ horizon: ${horizon}
33
+ n_obs_steps: ${n_obs_steps}
34
+ cond_dim: ${eval:'${obs_dim} if ${obs_as_cond} else 0'}
35
+
36
+ n_layer: 8
37
+ n_head: 4
38
+ n_emb: 256
39
+ p_drop_emb: 0.0
40
+ p_drop_attn: 0.3
41
+
42
+ causal_attn: True
43
+ time_as_cond: True # if false, use BERT like encoder only arch, time as input
44
+ obs_as_cond: ${obs_as_cond}
45
+ n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
46
+
47
+ noise_scheduler:
48
+ _target_: diffusers.schedulers.scheduling_ddpm.DDPMScheduler
49
+ num_train_timesteps: 100
50
+ beta_start: 0.0001
51
+ beta_end: 0.02
52
+ beta_schedule: squaredcos_cap_v2
53
+ variance_type: fixed_small # Yilun's paper uses fixed_small_log instead, but easy to cause Nan
54
+ clip_sample: True # required when predict_epsilon=False
55
+ prediction_type: epsilon # or sample
56
+
57
+ horizon: ${horizon}
58
+ obs_dim: ${obs_dim}
59
+ action_dim: ${action_dim}
60
+ n_action_steps: ${n_action_steps}
61
+ n_obs_steps: ${n_obs_steps}
62
+ num_inference_steps: 100
63
+ obs_as_cond: ${obs_as_cond}
64
+ pred_action_steps_only: ${pred_action_steps_only}
65
+
66
+ beta: 0.1
67
+ bias_reg: 0.25
68
+ ignore_equal_pref: False
69
+ clip_margin: 1.0
70
+ smooth_label: 0.0
71
+ confidence_weight: False
72
+ cw_temperature: 0.03
73
+ unclip_win: False
74
+
75
+ ema:
76
+ _target_: diffusion_policy.model.diffusion.ema_model.EMAModel
77
+ update_after_step: 0
78
+ inv_gamma: 1.0 # TODO: UAPA paper use : 2.0
79
+ power: 0.75 # TODO: UAPA paper use : 0.9
80
+ min_value: 0.0
81
+ max_value: 0.9999
82
+
83
+ dataloader:
84
+ batch_size: 128
85
+ num_workers: 1
86
+ shuffle: True
87
+ pin_memory: True
88
+ persistent_workers: False
89
+
90
+
91
+ optimizer:
92
+ learning_rate: 1.0e-4 # 1.0e-4
93
+ weight_decay: 1.0e-3
94
+ betas: [0.9, 0.95]
95
+
96
+ training:
97
+ device: "cuda:0"
98
+ seed: 42
99
+ debug: False
100
+ resume: True
101
+ # optimization
102
+ lr_scheduler: cosine
103
+ # Transformer needs LR warmup
104
+ lr_warmup_steps: 0
105
+ num_epochs: 500
106
+ gradient_accumulate_every: 1
107
+ use_ema: True
108
+ # training loop control
109
+ # in epochs
110
+ rollout_every: 25
111
+ checkpoint_every: 25
112
+ val_every: 1
113
+ sample_every: 5
114
+ # steps per epoch
115
+ max_train_steps: null
116
+ max_val_steps: null
117
+ # misc
118
+ tqdm_interval_sec: 1.0
119
+
120
+ cpl_loss_type: 'cplkl' # 'cpl'
121
+ use_expert_data_1: False
122
+ use_expert_data_2: False
123
+ n_epoch_sft: 0
124
+ sft_type: "pos"
125
+ stride_ratio: 0.5
126
+ pseudo_preference: False
127
+
128
+ dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
129
+ dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
130
+ preference_learning:
131
+ num_rounds: 1
132
+ equal_threshold: 0.05
133
+
134
+ logging:
135
+ project: IL-DP-2026
136
+ resume: False
137
+ mode: online
138
+ name: ${name}_${training.seed}
139
+ tags: ["${task_name}"]
140
+ id: null
141
+ group: ${name}
142
+
143
+ checkpoint:
144
+ topk:
145
+ monitor_key: test_mean_score
146
+ mode: max
147
+ k: 3
148
+ format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
149
+ save_last_ckpt: True
150
+ save_last_snapshot: False
151
+
152
+ multi_run:
153
+ run_dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
154
+ wandb_name_base: ${name}_${task_name}
155
+
156
+ hydra:
157
+ job:
158
+ override_dirname: ${name}
159
+ run:
160
+ dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
161
+ sweep:
162
+ dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
163
+ subdir: ${hydra.job.num}
config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_tool_hang_ph_abs.yaml ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: tool_hang_lowdim_abs_pbrl
4
+
5
+ name: train_pbrl_diffusion_transformer
6
+
7
+ _target_: diffusion_policy.workspace.train_pbrl_diffusion_transformer_lowdim_offline_workspace.PbrlDiffusionTransformerLowdimWorkspace
8
+ checkpoint_dir: logs/pretrain/tool_hang_lowdim/train_diffusion_transformer/checkpoints/epoch=0500-test_mean_score=0.440.ckpt
9
+
10
+ obs_dim: ${task.obs_dim}
11
+ action_dim: ${task.action_dim}
12
+ keypoint_dim: ${task.keypoint_dim}
13
+ task_name: ${task.name}
14
+
15
+ gamma: 0.999 # discount factor in MDP
16
+ horizon: 10
17
+ n_obs_steps: 2
18
+ n_action_steps: 8
19
+ n_latency_steps: 0
20
+ past_action_visible: False
21
+ keypoint_visible_rate: 1.0
22
+ obs_as_cond: True
23
+ pred_action_steps_only: False
24
+
25
+ policy:
26
+ _target_: diffusion_policy.policy.cpl_diffusion_transformer_lowdim_policy.CplDiffusionTransformerLowdimPolicy
27
+
28
+ model:
29
+ _target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
30
+ input_dim: ${eval:'${action_dim} if ${obs_as_cond} else ${obs_dim} + ${action_dim}'}
31
+ output_dim: ${policy.model.input_dim}
32
+ horizon: ${horizon}
33
+ n_obs_steps: ${n_obs_steps}
34
+ cond_dim: ${eval:'${obs_dim} if ${obs_as_cond} else 0'}
35
+
36
+ n_layer: 8
37
+ n_head: 4
38
+ n_emb: 256
39
+ p_drop_emb: 0.0
40
+ p_drop_attn: 0.3
41
+
42
+ causal_attn: True
43
+ time_as_cond: True # if false, use BERT like encoder only arch, time as input
44
+ obs_as_cond: ${obs_as_cond}
45
+ n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
46
+
47
+ noise_scheduler:
48
+ _target_: diffusers.schedulers.scheduling_ddpm.DDPMScheduler
49
+ num_train_timesteps: 100
50
+ beta_start: 0.0001
51
+ beta_end: 0.02
52
+ beta_schedule: squaredcos_cap_v2
53
+ variance_type: fixed_small # Yilun's paper uses fixed_small_log instead, but easy to cause Nan
54
+ clip_sample: True # required when predict_epsilon=False
55
+ prediction_type: epsilon # or sample
56
+
57
+ horizon: ${horizon}
58
+ obs_dim: ${obs_dim}
59
+ action_dim: ${action_dim}
60
+ n_action_steps: ${n_action_steps}
61
+ n_obs_steps: ${n_obs_steps}
62
+ num_inference_steps: 100
63
+ obs_as_cond: ${obs_as_cond}
64
+ pred_action_steps_only: ${pred_action_steps_only}
65
+
66
+ beta: 0.1
67
+ bias_reg: 0.25
68
+ ignore_equal_pref: False
69
+ clip_margin: 1.0
70
+ smooth_label: 0.0
71
+ confidence_weight: False
72
+ cw_temperature: 0.03
73
+ unclip_win: False
74
+
75
+ ema:
76
+ _target_: diffusion_policy.model.diffusion.ema_model.EMAModel
77
+ update_after_step: 0
78
+ inv_gamma: 1.0 # TODO: UAPA paper use : 2.0
79
+ power: 0.75 # TODO: UAPA paper use : 0.9
80
+ min_value: 0.0
81
+ max_value: 0.9999
82
+
83
+ dataloader:
84
+ batch_size: 128
85
+ num_workers: 1
86
+ shuffle: True
87
+ pin_memory: True
88
+ persistent_workers: False
89
+
90
+
91
+ optimizer:
92
+ learning_rate: 1.0e-4 # 1.0e-4
93
+ weight_decay: 1.0e-3
94
+ betas: [0.9, 0.95]
95
+
96
+ training:
97
+ device: "cuda:0"
98
+ seed: 42
99
+ debug: False
100
+ resume: True
101
+ # optimization
102
+ lr_scheduler: cosine
103
+ # Transformer needs LR warmup
104
+ lr_warmup_steps: 0
105
+ num_epochs: 500
106
+ gradient_accumulate_every: 1
107
+ use_ema: True
108
+ # training loop control
109
+ # in epochs
110
+ rollout_every: 25
111
+ checkpoint_every: 25
112
+ val_every: 1
113
+ sample_every: 5
114
+ # steps per epoch
115
+ max_train_steps: null
116
+ max_val_steps: null
117
+ # misc
118
+ tqdm_interval_sec: 1.0
119
+
120
+ cpl_loss_type: 'cplkl' # 'cpl'
121
+ use_expert_data_1: False
122
+ use_expert_data_2: False
123
+ n_epoch_sft: 0
124
+ sft_type: "pos"
125
+ stride_ratio: 0.5
126
+ pseudo_preference: False
127
+
128
+ dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
129
+ dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
130
+ preference_learning:
131
+ num_rounds: 1
132
+ equal_threshold: 0.05
133
+
134
+ logging:
135
+ project: IL-DP-2026
136
+ resume: False
137
+ mode: online
138
+ name: ${name}_${training.seed}
139
+ tags: ["${task_name}"]
140
+ id: null
141
+ group: ${name}
142
+
143
+ checkpoint:
144
+ topk:
145
+ monitor_key: test_mean_score
146
+ mode: max
147
+ k: 3
148
+ format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
149
+ save_last_ckpt: True
150
+ save_last_snapshot: False
151
+
152
+ multi_run:
153
+ run_dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
154
+ wandb_name_base: ${name}_${task_name}
155
+
156
+ hydra:
157
+ job:
158
+ override_dirname: ${name}
159
+ run:
160
+ dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
161
+ sweep:
162
+ dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
163
+ subdir: ${hydra.job.num}
config/pbrl_dp_transformer/train_pbrl_diffusion_policy_transformer_lowdim_transport_mh_abs.yaml ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: transport_lowdim_abs_pbrl
4
+
5
+ name: train_pbrl_diffusion_transformer
6
+
7
+ _target_: diffusion_policy.workspace.train_pbrl_diffusion_transformer_lowdim_offline_workspace.PbrlDiffusionTransformerLowdimWorkspace
8
+ checkpoint_dir: logs/pretrain/transport_lowdim/train_diffusion_transformer/checkpoints/epoch=0450-test_mean_score=0.240.ckpt
9
+
10
+ obs_dim: ${task.obs_dim}
11
+ action_dim: ${task.action_dim}
12
+ keypoint_dim: ${task.keypoint_dim}
13
+ task_name: ${task.name}
14
+
15
+ gamma: 0.999 # discount factor in MDP
16
+ horizon: 10
17
+ n_obs_steps: 2
18
+ n_action_steps: 8
19
+ n_latency_steps: 0
20
+ past_action_visible: False
21
+ keypoint_visible_rate: 1.0
22
+ obs_as_cond: True
23
+ pred_action_steps_only: False
24
+
25
+ policy:
26
+ _target_: diffusion_policy.policy.cpl_diffusion_transformer_lowdim_policy.CplDiffusionTransformerLowdimPolicy
27
+
28
+ model:
29
+ _target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
30
+ input_dim: ${eval:'${action_dim} if ${obs_as_cond} else ${obs_dim} + ${action_dim}'}
31
+ output_dim: ${policy.model.input_dim}
32
+ horizon: ${horizon}
33
+ n_obs_steps: ${n_obs_steps}
34
+ cond_dim: ${eval:'${obs_dim} if ${obs_as_cond} else 0'}
35
+
36
+ n_layer: 8
37
+ n_head: 4
38
+ n_emb: 256
39
+ p_drop_emb: 0.0
40
+ p_drop_attn: 0.3
41
+
42
+ causal_attn: True
43
+ time_as_cond: True # if false, use BERT like encoder only arch, time as input
44
+ obs_as_cond: ${obs_as_cond}
45
+ n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
46
+
47
+ noise_scheduler:
48
+ _target_: diffusers.schedulers.scheduling_ddpm.DDPMScheduler
49
+ num_train_timesteps: 100
50
+ beta_start: 0.0001
51
+ beta_end: 0.02
52
+ beta_schedule: squaredcos_cap_v2
53
+ variance_type: fixed_small # Yilun's paper uses fixed_small_log instead, but easy to cause Nan
54
+ clip_sample: True # required when predict_epsilon=False
55
+ prediction_type: epsilon # or sample
56
+
57
+ horizon: ${horizon}
58
+ obs_dim: ${obs_dim}
59
+ action_dim: ${action_dim}
60
+ n_action_steps: ${n_action_steps}
61
+ n_obs_steps: ${n_obs_steps}
62
+ num_inference_steps: 100
63
+ obs_as_cond: ${obs_as_cond}
64
+ pred_action_steps_only: ${pred_action_steps_only}
65
+
66
+ beta: 0.1
67
+ bias_reg: 0.25
68
+ ignore_equal_pref: False
69
+ clip_margin: 1.0
70
+ smooth_label: 0.0
71
+ confidence_weight: False
72
+ cw_temperature: 0.03
73
+ unclip_win: False
74
+
75
+ ema:
76
+ _target_: diffusion_policy.model.diffusion.ema_model.EMAModel
77
+ update_after_step: 0
78
+ inv_gamma: 1.0 # TODO: UAPA paper use : 2.0
79
+ power: 0.75 # TODO: UAPA paper use : 0.9
80
+ min_value: 0.0
81
+ max_value: 0.9999
82
+
83
+ dataloader:
84
+ batch_size: 128
85
+ num_workers: 1
86
+ shuffle: True
87
+ pin_memory: True
88
+ persistent_workers: False
89
+
90
+
91
+ optimizer:
92
+ learning_rate: 1.0e-4 # 1.0e-4
93
+ weight_decay: 1.0e-3
94
+ betas: [0.9, 0.95]
95
+
96
+ training:
97
+ device: "cuda:0"
98
+ seed: 42
99
+ debug: False
100
+ resume: True
101
+ # optimization
102
+ lr_scheduler: cosine
103
+ # Transformer needs LR warmup
104
+ lr_warmup_steps: 0
105
+ num_epochs: 500
106
+ gradient_accumulate_every: 1
107
+ use_ema: True
108
+ # training loop control
109
+ # in epochs
110
+ rollout_every: 25
111
+ checkpoint_every: 25
112
+ val_every: 1
113
+ sample_every: 5
114
+ # steps per epoch
115
+ max_train_steps: null
116
+ max_val_steps: null
117
+ # misc
118
+ tqdm_interval_sec: 1.0
119
+
120
+ cpl_loss_type: 'cplkl' # 'cpl'
121
+ use_expert_data_1: False
122
+ use_expert_data_2: False
123
+ n_epoch_sft: 0
124
+ sft_type: "pos"
125
+ stride_ratio: 0.5
126
+ pseudo_preference: False
127
+
128
+ dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
129
+ dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
130
+ preference_learning:
131
+ num_rounds: 1
132
+ equal_threshold: 0.05
133
+
134
+ logging:
135
+ project: IL-DP-2026
136
+ resume: False
137
+ mode: online
138
+ name: ${name}_${training.seed}
139
+ tags: ["${task_name}"]
140
+ id: null
141
+ group: ${name}
142
+
143
+ checkpoint:
144
+ topk:
145
+ monitor_key: test_mean_score
146
+ mode: max
147
+ k: 3
148
+ format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
149
+ save_last_ckpt: True
150
+ save_last_snapshot: False
151
+
152
+ multi_run:
153
+ run_dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
154
+ wandb_name_base: ${name}_${task_name}
155
+
156
+ hydra:
157
+ job:
158
+ override_dirname: ${name}
159
+ run:
160
+ dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
161
+ sweep:
162
+ dir: data/outputs/${now:%Y.%m.%d}/${now:%H.%M.%S}_${name}_${task_name}
163
+ subdir: ${hydra.job.num}
config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_can_mh_abs.yaml ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: can_lowdim_abs_pbrl
4
+
5
+ name: train_pbrl_reflow_transformer
6
+
7
+ _target_: diffusion_policy.workspace.train_pbrl_reflow_transformer_lowdim_offline_workspace.PbrlReFlowTransformerLowdimWorkspace
8
+ checkpoint_dir: logs/pretrain/can_lowdim/reflow_transformer_ERate.98/reflow_transformer_ERate.98_42/checkpoints/epoch=0100-test_mean_score=0.320.ckpt
9
+
10
+ obs_dim: ${task.obs_dim}
11
+ action_dim: ${task.action_dim}
12
+ task_name: ${task.name}
13
+
14
+ gamma: 0.999 # discount factor in MDP
15
+ horizon: 10 # this horizon is for dataloader
16
+ n_obs_steps: 2
17
+ n_action_steps: 8
18
+ n_latency_steps: 0
19
+ past_action_visible: False
20
+
21
+ # Used during evaluation (both during training and evaluation)
22
+ test_clip_intermediate_actions: True
23
+ test_denoising_steps: 8
24
+ test_model_type: 'original' # can be "ema", "original"
25
+
26
+ policy:
27
+ _target_: diffusion_policy.policy.cpl_reflow_transformer_lowdim_policy.CplReFlowTransformerLowdimPolicy
28
+
29
+ model:
30
+ _target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
31
+ input_dim: ${action_dim}
32
+ output_dim: ${policy.model.input_dim}
33
+ horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
34
+ n_obs_steps: ${n_obs_steps}
35
+ cond_dim: ${obs_dim}
36
+
37
+ n_layer: 8
38
+ n_head: 4
39
+ n_emb: 256
40
+ p_drop_emb: 0.0
41
+ p_drop_attn: 0.3
42
+
43
+ causal_attn: True
44
+ time_as_cond: True # if false, use BERT like encoder only arch, time as input
45
+ obs_as_cond: True
46
+ n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
47
+
48
+ horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
49
+ obs_dim: ${obs_dim}
50
+ action_dim: ${action_dim}
51
+ n_action_steps: ${eval:'${n_action_steps}+${n_latency_steps}'}
52
+ n_obs_steps: ${n_obs_steps}
53
+ sample_t_type: uniform # beta, logitnormal
54
+
55
+ beta: 0.1
56
+ bias_reg: 0.25
57
+ ignore_equal_pref: False
58
+ clip_margin: null # null = disabled; recommended 10.0 for beta=0.0003125, 0.3 for beta=0.01
59
+ smooth_label: 0.0
60
+ confidence_weight: False
61
+ cw_temperature: 0.03
62
+ unclip_win: False
63
+
64
+ ema:
65
+ decay: 0.995
66
+
67
+ dataloader:
68
+ batch_size: 128
69
+ num_workers: 1
70
+ shuffle: True
71
+ pin_memory: True
72
+ persistent_workers: False
73
+
74
+
75
+ optimizer:
76
+ lr: 1e-4 # 1.0e-4
77
+ weight_decay: 1e-6
78
+
79
+ training:
80
+ device: "cuda:0"
81
+ seed: 42
82
+ debug: False
83
+ resume: True
84
+ # optimization
85
+ lr_scheduler: cosine
86
+ # Transformer needs LR warmup
87
+ lr_warmup_steps: 0
88
+ epoch_start_ema: 0
89
+ update_ema_freq: 10
90
+ num_epochs: 1000
91
+ use_ema: True
92
+ # training loop control
93
+ # in epochs
94
+ rollout_every: 25
95
+ checkpoint_every: 25
96
+ val_every: 1
97
+ max_train_steps: null
98
+ max_val_steps: null
99
+ # misc
100
+ tqdm_interval_sec: 1.0
101
+
102
+ cpl_loss_type: 'cplkl' # 'cpl'
103
+ use_expert_data_1: False
104
+ use_expert_data_2: False
105
+ n_epoch_sft: 0
106
+ sft_type: "pos"
107
+ stride_ratio: 0.5
108
+ pseudo_preference: False
109
+
110
+ dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
111
+ dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
112
+ preference_learning:
113
+ num_rounds: 1
114
+ equal_threshold: 0.05
115
+
116
+ logging:
117
+ project: IL-DP-2026
118
+ resume: False
119
+ mode: online
120
+ name: ${name}_${training.seed}
121
+ tags: ["${task_name}"]
122
+ id: null
123
+ group: ${name}
124
+
125
+ checkpoint:
126
+ topk:
127
+ monitor_key: test_mean_score
128
+ mode: max
129
+ k: 3
130
+ format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
131
+ save_last_ckpt: True
132
+ save_last_snapshot: False
133
+
134
+ multi_run:
135
+ run_dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
136
+ wandb_name_base: ${name}_${training.seed}
137
+
138
+ hydra:
139
+ job:
140
+ override_dirname: ${name}
141
+ run:
142
+ dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
143
+ sweep:
144
+ dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
145
+ subdir: ${hydra.job.num}
config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_lift_mh_abs.yaml ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: lift_lowdim_abs_pbrl
4
+
5
+ name: train_pbrl_reflow_transformer
6
+
7
+ _target_: diffusion_policy.workspace.train_pbrl_reflow_transformer_lowdim_offline_workspace.PbrlReFlowTransformerLowdimWorkspace
8
+ checkpoint_dir: logs/pretrain/lift_lowdim/reflow_transformer_ERate.98/reflow_transformer_ERate.98_42/checkpoints/epoch=0005-test_mean_score=0.400.ckpt
9
+
10
+ obs_dim: ${task.obs_dim}
11
+ action_dim: ${task.action_dim}
12
+ task_name: ${task.name}
13
+
14
+ gamma: 0.999 # discount factor in MDP
15
+ horizon: 10 # this horizon is for dataloader
16
+ n_obs_steps: 2
17
+ n_action_steps: 8
18
+ n_latency_steps: 0
19
+ past_action_visible: False
20
+
21
+ # Used during evaluation (both during training and evaluation)
22
+ test_clip_intermediate_actions: True
23
+ test_denoising_steps: 8
24
+ test_model_type: 'original' # can be "ema", "original"
25
+
26
+ policy:
27
+ _target_: diffusion_policy.policy.cpl_reflow_transformer_lowdim_policy.CplReFlowTransformerLowdimPolicy
28
+
29
+ model:
30
+ _target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
31
+ input_dim: ${action_dim}
32
+ output_dim: ${policy.model.input_dim}
33
+ horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
34
+ n_obs_steps: ${n_obs_steps}
35
+ cond_dim: ${obs_dim}
36
+
37
+ n_layer: 8
38
+ n_head: 4
39
+ n_emb: 256
40
+ p_drop_emb: 0.0
41
+ p_drop_attn: 0.3
42
+
43
+ causal_attn: True
44
+ time_as_cond: True # if false, use BERT like encoder only arch, time as input
45
+ obs_as_cond: True
46
+ n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
47
+
48
+ horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
49
+ obs_dim: ${obs_dim}
50
+ action_dim: ${action_dim}
51
+ n_action_steps: ${eval:'${n_action_steps}+${n_latency_steps}'}
52
+ n_obs_steps: ${n_obs_steps}
53
+ sample_t_type: uniform # beta, logitnormal
54
+
55
+ beta: 0.1
56
+ bias_reg: 0.25
57
+ ignore_equal_pref: False
58
+ clip_margin: null # null = disabled; recommended 10.0 for beta=0.0003125, 0.3 for beta=0.01
59
+ smooth_label: 0.0
60
+ confidence_weight: False
61
+ cw_temperature: 0.03
62
+ unclip_win: False
63
+
64
+ ema:
65
+ decay: 0.995
66
+
67
+ dataloader:
68
+ batch_size: 128
69
+ num_workers: 1
70
+ shuffle: True
71
+ pin_memory: True
72
+ persistent_workers: False
73
+
74
+
75
+ optimizer:
76
+ lr: 1e-4 # 1.0e-4
77
+ weight_decay: 1e-6
78
+
79
+ training:
80
+ device: "cuda:0"
81
+ seed: 42
82
+ debug: False
83
+ resume: True
84
+ # optimization
85
+ lr_scheduler: cosine
86
+ # Transformer needs LR warmup
87
+ lr_warmup_steps: 0
88
+ epoch_start_ema: 0
89
+ update_ema_freq: 10
90
+ num_epochs: 1000
91
+ use_ema: True
92
+ # training loop control
93
+ # in epochs
94
+ rollout_every: 25
95
+ checkpoint_every: 25
96
+ val_every: 1
97
+ max_train_steps: null
98
+ max_val_steps: null
99
+ # misc
100
+ tqdm_interval_sec: 1.0
101
+
102
+ cpl_loss_type: 'cplkl' # 'cpl'
103
+ use_expert_data_1: False
104
+ use_expert_data_2: False
105
+ n_epoch_sft: 0
106
+ sft_type: "pos"
107
+ stride_ratio: 0.5
108
+ pseudo_preference: False
109
+
110
+ dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
111
+ dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
112
+ preference_learning:
113
+ num_rounds: 1
114
+ equal_threshold: 0.05
115
+
116
+ logging:
117
+ project: IL-DP-2026
118
+ resume: False
119
+ mode: online
120
+ name: ${name}_${training.seed}
121
+ tags: ["${task_name}"]
122
+ id: null
123
+ group: ${name}
124
+
125
+ checkpoint:
126
+ topk:
127
+ monitor_key: test_mean_score
128
+ mode: max
129
+ k: 3
130
+ format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
131
+ save_last_ckpt: True
132
+ save_last_snapshot: False
133
+
134
+ multi_run:
135
+ run_dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
136
+ wandb_name_base: ${name}_${training.seed}
137
+
138
+ hydra:
139
+ job:
140
+ override_dirname: ${name}
141
+ run:
142
+ dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
143
+ sweep:
144
+ dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
145
+ subdir: ${hydra.job.num}
config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_square_mh_abs.yaml ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: square_lowdim_abs_pbrl
4
+
5
+ name: train_pbrl_reflow_transformer
6
+
7
+ _target_: diffusion_policy.workspace.train_pbrl_reflow_transformer_lowdim_offline_workspace.PbrlReFlowTransformerLowdimWorkspace
8
+ checkpoint_dir: logs/pretrain/square_lowdim/reflow_transformer_ERate.98/reflow_transformer_ERate.98_42/checkpoints/epoch=0400-test_mean_score=0.440.ckpt
9
+
10
+ obs_dim: ${task.obs_dim}
11
+ action_dim: ${task.action_dim}
12
+ task_name: ${task.name}
13
+
14
+ gamma: 0.999 # discount factor in MDP
15
+ horizon: 10 # this horizon is for dataloader
16
+ n_obs_steps: 2
17
+ n_action_steps: 8
18
+ n_latency_steps: 0
19
+ past_action_visible: False
20
+
21
+ # Used during evaluation (both during training and evaluation)
22
+ test_clip_intermediate_actions: True
23
+ test_denoising_steps: 8
24
+ test_model_type: 'original' # can be "ema", "original"
25
+
26
+ policy:
27
+ _target_: diffusion_policy.policy.cpl_reflow_transformer_lowdim_policy.CplReFlowTransformerLowdimPolicy
28
+
29
+ model:
30
+ _target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
31
+ input_dim: ${action_dim}
32
+ output_dim: ${policy.model.input_dim}
33
+ horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
34
+ n_obs_steps: ${n_obs_steps}
35
+ cond_dim: ${obs_dim}
36
+
37
+ n_layer: 8
38
+ n_head: 4
39
+ n_emb: 256
40
+ p_drop_emb: 0.0
41
+ p_drop_attn: 0.3
42
+
43
+ causal_attn: True
44
+ time_as_cond: True # if false, use BERT like encoder only arch, time as input
45
+ obs_as_cond: True
46
+ n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
47
+
48
+ horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
49
+ obs_dim: ${obs_dim}
50
+ action_dim: ${action_dim}
51
+ n_action_steps: ${eval:'${n_action_steps}+${n_latency_steps}'}
52
+ n_obs_steps: ${n_obs_steps}
53
+ sample_t_type: uniform # beta, logitnormal
54
+
55
+ beta: 0.1
56
+ bias_reg: 0.25
57
+ ignore_equal_pref: False
58
+ clip_margin: null # null = disabled; recommended 10.0 for beta=0.0003125, 0.3 for beta=0.01
59
+ smooth_label: 0.0
60
+ confidence_weight: False
61
+ cw_temperature: 0.03
62
+ unclip_win: False
63
+
64
+ ema:
65
+ decay: 0.995
66
+
67
+ dataloader:
68
+ batch_size: 128
69
+ num_workers: 1
70
+ shuffle: True
71
+ pin_memory: True
72
+ persistent_workers: False
73
+
74
+
75
+ optimizer:
76
+ lr: 1e-4 # 1.0e-4
77
+ weight_decay: 1e-6
78
+
79
+ training:
80
+ device: "cuda:0"
81
+ seed: 42
82
+ debug: False
83
+ resume: True
84
+ # optimization
85
+ lr_scheduler: cosine
86
+ # Transformer needs LR warmup
87
+ lr_warmup_steps: 0
88
+ epoch_start_ema: 0
89
+ update_ema_freq: 10
90
+ num_epochs: 1000
91
+ use_ema: True
92
+ # training loop control
93
+ # in epochs
94
+ rollout_every: 25
95
+ checkpoint_every: 25
96
+ val_every: 1
97
+ max_train_steps: null
98
+ max_val_steps: null
99
+ # misc
100
+ tqdm_interval_sec: 1.0
101
+
102
+ cpl_loss_type: 'cplkl' # 'cpl'
103
+ use_expert_data_1: False
104
+ use_expert_data_2: False
105
+ n_epoch_sft: 0
106
+ sft_type: "pos"
107
+ stride_ratio: 0.5
108
+ pseudo_preference: False
109
+
110
+ dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
111
+ dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
112
+ preference_learning:
113
+ num_rounds: 1
114
+ equal_threshold: 0.05
115
+
116
+ logging:
117
+ project: IL-DP-2026
118
+ resume: False
119
+ mode: online
120
+ name: ${name}_${training.seed}
121
+ tags: ["${task_name}"]
122
+ id: null
123
+ group: ${name}
124
+
125
+ checkpoint:
126
+ topk:
127
+ monitor_key: test_mean_score
128
+ mode: max
129
+ k: 3
130
+ format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
131
+ save_last_ckpt: True
132
+ save_last_snapshot: False
133
+
134
+ multi_run:
135
+ run_dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
136
+ wandb_name_base: ${name}_${training.seed}
137
+
138
+ hydra:
139
+ job:
140
+ override_dirname: ${name}
141
+ run:
142
+ dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
143
+ sweep:
144
+ dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
145
+ subdir: ${hydra.job.num}
config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_tool_hang_ph_abs.yaml ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: tool_hang_lowdim_abs_pbrl
4
+
5
+ name: train_pbrl_reflow_transformer
6
+
7
+ _target_: diffusion_policy.workspace.train_pbrl_reflow_transformer_lowdim_offline_workspace.PbrlReFlowTransformerLowdimWorkspace
8
+ checkpoint_dir: logs/pretrain/tool_hang_lowdim/reflow_transformer_ERate.98/reflow_transformer_ERate.98_42/checkpoints/epoch=0600-test_mean_score=0.360.ckpt
9
+
10
+ obs_dim: ${task.obs_dim}
11
+ action_dim: ${task.action_dim}
12
+ task_name: ${task.name}
13
+
14
+ gamma: 0.999 # discount factor in MDP
15
+ horizon: 16 # this horizon is for dataloader
16
+ n_obs_steps: 2
17
+ n_action_steps: 8
18
+ n_latency_steps: 0
19
+ past_action_visible: False
20
+
21
+ # Used during evaluation (both during training and evaluation)
22
+ test_clip_intermediate_actions: True
23
+ test_denoising_steps: 8
24
+ test_model_type: 'original' # can be "ema", "original"
25
+
26
+ policy:
27
+ _target_: diffusion_policy.policy.cpl_reflow_transformer_lowdim_policy.CplReFlowTransformerLowdimPolicy
28
+
29
+ model:
30
+ _target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
31
+ input_dim: ${action_dim}
32
+ output_dim: ${policy.model.input_dim}
33
+ horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
34
+ n_obs_steps: ${n_obs_steps}
35
+ cond_dim: ${obs_dim}
36
+
37
+ n_layer: 8
38
+ n_head: 4
39
+ n_emb: 256
40
+ p_drop_emb: 0.0
41
+ p_drop_attn: 0.3
42
+
43
+ causal_attn: True
44
+ time_as_cond: True # if false, use BERT like encoder only arch, time as input
45
+ obs_as_cond: True
46
+ n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
47
+
48
+ horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
49
+ obs_dim: ${obs_dim}
50
+ action_dim: ${action_dim}
51
+ n_action_steps: ${eval:'${n_action_steps}+${n_latency_steps}'}
52
+ n_obs_steps: ${n_obs_steps}
53
+ sample_t_type: uniform # beta, logitnormal
54
+
55
+ beta: 0.1
56
+ bias_reg: 0.25
57
+ ignore_equal_pref: False
58
+ clip_margin: null # null = disabled; recommended 10.0 for beta=0.0003125, 0.3 for beta=0.01
59
+ smooth_label: 0.0
60
+ confidence_weight: False
61
+ cw_temperature: 0.03
62
+ unclip_win: False
63
+
64
+ ema:
65
+ decay: 0.995
66
+
67
+ dataloader:
68
+ batch_size: 128
69
+ num_workers: 1
70
+ shuffle: True
71
+ pin_memory: True
72
+ persistent_workers: False
73
+
74
+
75
+ optimizer:
76
+ lr: 1e-4 # 1.0e-4
77
+ weight_decay: 1e-6
78
+
79
+ training:
80
+ device: "cuda:0"
81
+ seed: 42
82
+ debug: False
83
+ resume: True
84
+ # optimization
85
+ lr_scheduler: cosine
86
+ # Transformer needs LR warmup
87
+ lr_warmup_steps: 0
88
+ epoch_start_ema: 0
89
+ update_ema_freq: 10
90
+ num_epochs: 1000
91
+ use_ema: True
92
+ # training loop control
93
+ # in epochs
94
+ rollout_every: 25
95
+ checkpoint_every: 25
96
+ val_every: 1
97
+ max_train_steps: null
98
+ max_val_steps: null
99
+ # misc
100
+ tqdm_interval_sec: 1.0
101
+
102
+ cpl_loss_type: 'cplkl' # 'cpl'
103
+ use_expert_data_1: False
104
+ use_expert_data_2: False
105
+ n_epoch_sft: 0
106
+ sft_type: "pos"
107
+ stride_ratio: 0.5
108
+ pseudo_preference: False
109
+
110
+ dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
111
+ dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
112
+ preference_learning:
113
+ num_rounds: 1
114
+ equal_threshold: 0.05
115
+
116
+ logging:
117
+ project: IL-DP-2026
118
+ resume: False
119
+ mode: online
120
+ name: ${name}_${training.seed}
121
+ tags: ["${task_name}"]
122
+ id: null
123
+ group: ${name}
124
+
125
+ checkpoint:
126
+ topk:
127
+ monitor_key: test_mean_score
128
+ mode: max
129
+ k: 3
130
+ format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
131
+ save_last_ckpt: True
132
+ save_last_snapshot: False
133
+
134
+ multi_run:
135
+ run_dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
136
+ wandb_name_base: ${name}_${training.seed}
137
+
138
+ hydra:
139
+ job:
140
+ override_dirname: ${name}
141
+ run:
142
+ dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
143
+ sweep:
144
+ dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
145
+ subdir: ${hydra.job.num}
config/pbrl_reflow_transformer/train_pbrl_reflow_policy_transformer_lowdim_transport_mh_abs.yaml ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ - task: transport_lowdim_abs_pbrl
4
+
5
+ name: train_pbrl_reflow_transformer
6
+
7
+ _target_: diffusion_policy.workspace.train_pbrl_reflow_transformer_lowdim_offline_workspace.PbrlReFlowTransformerLowdimWorkspace
8
+ checkpoint_dir: logs/pretrain/transport_lowdim/reflow_transformer_ERate.98/reflow_transformer_ERate.98_42/checkpoints/epoch=0300-test_mean_score=0.400.ckpt
9
+
10
+ obs_dim: ${task.obs_dim}
11
+ action_dim: ${task.action_dim}
12
+ task_name: ${task.name}
13
+
14
+ gamma: 0.999 # discount factor in MDP
15
+ horizon: 10 # this horizon is for dataloader
16
+ n_obs_steps: 2
17
+ n_action_steps: 8
18
+ n_latency_steps: 0
19
+ past_action_visible: False
20
+
21
+ # Used during evaluation (both during training and evaluation)
22
+ test_clip_intermediate_actions: True
23
+ test_denoising_steps: 8
24
+ test_model_type: 'original' # can be "ema", "original"
25
+
26
+ policy:
27
+ _target_: diffusion_policy.policy.cpl_reflow_transformer_lowdim_policy.CplReFlowTransformerLowdimPolicy
28
+
29
+ model:
30
+ _target_: diffusion_policy.model.diffusion.transformer_for_diffusion.TransformerForDiffusion
31
+ input_dim: ${action_dim}
32
+ output_dim: ${policy.model.input_dim}
33
+ horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
34
+ n_obs_steps: ${n_obs_steps}
35
+ cond_dim: ${obs_dim}
36
+
37
+ n_layer: 8
38
+ n_head: 4
39
+ n_emb: 256
40
+ p_drop_emb: 0.0
41
+ p_drop_attn: 0.3
42
+
43
+ causal_attn: True
44
+ time_as_cond: True # if false, use BERT like encoder only arch, time as input
45
+ obs_as_cond: True
46
+ n_cond_layers: 0 # >0: use transformer encoder for cond, otherwise use MLP
47
+
48
+ horizon: ${eval:'${horizon}-${n_obs_steps}+1'}
49
+ obs_dim: ${obs_dim}
50
+ action_dim: ${action_dim}
51
+ n_action_steps: ${eval:'${n_action_steps}+${n_latency_steps}'}
52
+ n_obs_steps: ${n_obs_steps}
53
+ sample_t_type: uniform # beta, logitnormal
54
+
55
+ beta: 0.1
56
+ bias_reg: 0.25
57
+ ignore_equal_pref: False
58
+ clip_margin: null # null = disabled; recommended 10.0 for beta=0.0003125, 0.3 for beta=0.01
59
+ smooth_label: 0.0
60
+ confidence_weight: False
61
+ cw_temperature: 0.03
62
+ unclip_win: False
63
+
64
+ ema:
65
+ decay: 0.995
66
+
67
+ dataloader:
68
+ batch_size: 128
69
+ num_workers: 1
70
+ shuffle: True
71
+ pin_memory: True
72
+ persistent_workers: False
73
+
74
+
75
+ optimizer:
76
+ lr: 1e-4 # 1.0e-4
77
+ weight_decay: 1e-6
78
+
79
+ training:
80
+ device: "cuda:0"
81
+ seed: 42
82
+ debug: False
83
+ resume: True
84
+ # optimization
85
+ lr_scheduler: cosine
86
+ # Transformer needs LR warmup
87
+ lr_warmup_steps: 0
88
+ epoch_start_ema: 0
89
+ update_ema_freq: 10
90
+ num_epochs: 1000
91
+ use_ema: True
92
+ # training loop control
93
+ # in epochs
94
+ rollout_every: 25
95
+ checkpoint_every: 25
96
+ val_every: 1
97
+ max_train_steps: null
98
+ max_val_steps: null
99
+ # misc
100
+ tqdm_interval_sec: 1.0
101
+
102
+ cpl_loss_type: 'cplkl' # 'cpl'
103
+ use_expert_data_1: False
104
+ use_expert_data_2: False
105
+ n_epoch_sft: 0
106
+ sft_type: "pos"
107
+ stride_ratio: 0.5
108
+ pseudo_preference: False
109
+
110
+ dataset_1_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5.h5'
111
+ dataset_2_dir: 'data/robomimic/datasets/lift/noraml/robomimic_data_0.5_2.h5'
112
+ preference_learning:
113
+ num_rounds: 1
114
+ equal_threshold: 0.05
115
+
116
+ logging:
117
+ project: IL-DP-2026
118
+ resume: False
119
+ mode: online
120
+ name: ${name}_${training.seed}
121
+ tags: ["${task_name}"]
122
+ id: null
123
+ group: ${name}
124
+
125
+ checkpoint:
126
+ topk:
127
+ monitor_key: test_mean_score
128
+ mode: max
129
+ k: 3
130
+ format_str: 'epoch={epoch:04d}-test_mean_score={test_mean_score:.3f}.ckpt'
131
+ save_last_ckpt: True
132
+ save_last_snapshot: False
133
+
134
+ multi_run:
135
+ run_dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
136
+ wandb_name_base: ${name}_${training.seed}
137
+
138
+ hydra:
139
+ job:
140
+ override_dirname: ${name}
141
+ run:
142
+ dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
143
+ sweep:
144
+ dir: logs/reproduce/${task_name}/${logging.group}/${logging.name}
145
+ subdir: ${hydra.job.num}
config/task/aloha.yaml ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: aloha
2
+
3
+ shape_meta: &shape_meta
4
+ # acceptable types: rgb, low_dim
5
+ obs:
6
+ high_images:
7
+ shape: [3, 480, 640]
8
+ type: rgb
9
+ wrist_images:
10
+ shape: [3, 480, 640]
11
+ type: rgb
12
+ states:
13
+ shape: [13]
14
+ action:
15
+ shape: [13]
16
+
17
+ task_name: &task_name aloha
18
+ dataset_type: &dataset_type mh
19
+ dataset_path: &dataset_path data/aloha/placing_drawer_demo/placing_drawer_demo.hdf5
20
+ # abs_action: &abs_action True
21
+
22
+ env_runner:
23
+ _target_: diffusion_policy.env_runner.robomimic_image_runner.RobomimicImageRunner
24
+ dataset_path: *dataset_path
25
+ shape_meta: *shape_meta
26
+ n_train: 6
27
+ n_train_vis: 0
28
+ train_start_idx: 0
29
+ n_test: 50
30
+ n_test_vis: 0
31
+ test_start_seed: 100000
32
+ # use python's eval function as resolver, single-quoted string as argument
33
+ max_steps: ${eval:'500 if "${task.dataset_type}" == "mh" else 400'}
34
+ n_obs_steps: ${n_obs_steps}
35
+ n_action_steps: ${n_action_steps}
36
+ render_obs_key: 'agentview_image'
37
+ fps: 10
38
+ crf: 22
39
+ past_action: ${past_action_visible}
40
+ # abs_action: *abs_action
41
+ tqdm_interval_sec: 1.0
42
+ n_envs: 25
43
+ # evaluation at this config requires a 16 core 64GB instance.
44
+
45
+ dataset:
46
+ _target_: diffusion_policy.dataset.aloha_replay_image_dataset.AlohaReplayImageDataset
47
+ shape_meta: *shape_meta
48
+ dataset_path: *dataset_path
49
+ horizon: ${horizon}
50
+ pad_before: ${eval:'${n_obs_steps}-1+${n_latency_steps}'}
51
+ pad_after: ${eval:'${n_action_steps}-1'}
52
+ n_obs_steps: ${dataset_obs_steps}
53
+ # abs_action: *abs_action
54
+ # rotation_rep: 'rotation_6d'
55
+ # use_legacy_normalizer: False
56
+ use_cache: True
57
+ seed: 42
58
+ val_ratio: 0.02
config/task/blockpush_lowdim_seed.yaml ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: blockpush_lowdim_seed
2
+
3
+ obs_dim: 16
4
+ action_dim: 2
5
+ keypoint_dim: 2
6
+ obs_eef_target: True
7
+
8
+ env_runner:
9
+ _target_: diffusion_policy.env_runner.blockpush_lowdim_runner.BlockPushLowdimRunner
10
+ n_train: 6
11
+ n_train_vis: 0
12
+ train_start_seed: 0
13
+ n_test: 50
14
+ n_test_vis: 0
15
+ test_start_seed: 100000
16
+ max_steps: 350
17
+ n_obs_steps: ${n_obs_steps}
18
+ n_action_steps: ${n_action_steps}
19
+ fps: 5
20
+ past_action: ${past_action_visible}
21
+ abs_action: False
22
+ obs_eef_target: ${task.obs_eef_target}
23
+ n_envs: null
24
+
25
+ dataset:
26
+ _target_: diffusion_policy.dataset.blockpush_lowdim_dataset.BlockPushLowdimDataset
27
+ zarr_path: data/block_pushing/multimodal_push_seed.zarr
28
+ horizon: ${horizon}
29
+ pad_before: ${eval:'${n_obs_steps}-1'}
30
+ pad_after: ${eval:'${n_action_steps}-1'}
31
+ obs_eef_target: ${task.obs_eef_target}
32
+ use_manual_normalizer: False
33
+ seed: 42
34
+ val_ratio: 0.02
config/task/blockpush_lowdim_seed_abs.yaml ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: blockpush_lowdim_seed_abs
2
+
3
+ obs_dim: 16
4
+ action_dim: 2
5
+ keypoint_dim: 2
6
+ obs_eef_target: True
7
+
8
+ env_runner:
9
+ _target_: diffusion_policy.env_runner.blockpush_lowdim_runner.BlockPushLowdimRunner
10
+ n_train: 6
11
+ n_train_vis: 0
12
+ train_start_seed: 0
13
+ n_test: 50
14
+ n_test_vis: 0
15
+ test_start_seed: 100000
16
+ max_steps: 350
17
+ n_obs_steps: ${n_obs_steps}
18
+ n_action_steps: ${n_action_steps}
19
+ fps: 5
20
+ past_action: ${past_action_visible}
21
+ abs_action: True
22
+ obs_eef_target: ${task.obs_eef_target}
23
+ n_envs: null
24
+
25
+ dataset:
26
+ _target_: diffusion_policy.dataset.blockpush_lowdim_dataset.BlockPushLowdimDataset
27
+ zarr_path: data/block_pushing/multimodal_push_seed_abs.zarr
28
+ horizon: ${horizon}
29
+ pad_before: ${eval:'${n_obs_steps}-1'}
30
+ pad_after: ${eval:'${n_action_steps}-1'}
31
+ obs_eef_target: ${task.obs_eef_target}
32
+ use_manual_normalizer: False
33
+ seed: 42
34
+ val_ratio: 0.02
config/task/can_image.yaml ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: can_image
2
+
3
+ shape_meta: &shape_meta
4
+ # acceptable types: rgb, low_dim
5
+ obs:
6
+ agentview_image:
7
+ shape: [3, 84, 84]
8
+ type: rgb
9
+ robot0_eye_in_hand_image:
10
+ shape: [3, 84, 84]
11
+ type: rgb
12
+ robot0_eef_pos:
13
+ shape: [3]
14
+ # type default: low_dim
15
+ robot0_eef_quat:
16
+ shape: [4]
17
+ robot0_gripper_qpos:
18
+ shape: [2]
19
+ action:
20
+ shape: [7]
21
+
22
+ task_name: &task_name can
23
+ dataset_type: &dataset_type mh
24
+ dataset_path: &dataset_path data/robomimic/datasets/${task.task_name}/${task.dataset_type}/image.hdf5
25
+ abs_action: &abs_action False
26
+
27
+ env_runner:
28
+ _target_: diffusion_policy.env_runner.robomimic_image_runner.RobomimicImageRunner
29
+ dataset_path: *dataset_path
30
+ shape_meta: *shape_meta
31
+ # costs 1GB per env
32
+ n_train: 6
33
+ n_train_vis: 0
34
+ train_start_idx: 0
35
+ n_test: 50
36
+ n_test_vis: 0
37
+ test_start_seed: 100000
38
+ # use python's eval function as resolver, single-quoted string as argument
39
+ max_steps: ${eval:'500 if "${task.dataset_type}" == "mh" else 400'}
40
+ n_obs_steps: ${n_obs_steps}
41
+ n_action_steps: ${n_action_steps}
42
+ render_obs_key: 'agentview_image'
43
+ fps: 10
44
+ crf: 22
45
+ past_action: ${past_action_visible}
46
+ abs_action: *abs_action
47
+ tqdm_interval_sec: 1.0
48
+ n_envs: 25
49
+ # evaluation at this config requires a 16 core 64GB instance.
50
+
51
+ dataset:
52
+ _target_: diffusion_policy.dataset.robomimic_replay_image_dataset.RobomimicReplayImageDataset
53
+ shape_meta: *shape_meta
54
+ dataset_path: *dataset_path
55
+ horizon: ${horizon}
56
+ pad_before: ${eval:'${n_obs_steps}-1+${n_latency_steps}'}
57
+ pad_after: ${eval:'${n_action_steps}-1'}
58
+ n_obs_steps: ${dataset_obs_steps}
59
+ abs_action: *abs_action
60
+ rotation_rep: 'rotation_6d'
61
+ use_legacy_normalizer: False
62
+ use_cache: True
63
+ seed: 42
64
+ val_ratio: 0.02
config/task/can_image_abs.yaml ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: can_image
2
+
3
+ shape_meta: &shape_meta
4
+ # acceptable types: rgb, low_dim
5
+ obs:
6
+ agentview_image:
7
+ shape: [3, 84, 84]
8
+ type: rgb
9
+ robot0_eye_in_hand_image:
10
+ shape: [3, 84, 84]
11
+ type: rgb
12
+ robot0_eef_pos:
13
+ shape: [3]
14
+ # type default: low_dim
15
+ robot0_eef_quat:
16
+ shape: [4]
17
+ robot0_gripper_qpos:
18
+ shape: [2]
19
+ action:
20
+ shape: [10]
21
+
22
+ task_name: &task_name can
23
+ dataset_type: &dataset_type mh
24
+ dataset_path: &dataset_path data/robomimic/datasets/${task.task_name}/${task.dataset_type}/image_abs.hdf5
25
+ abs_action: &abs_action True
26
+
27
+ env_runner:
28
+ _target_: diffusion_policy.env_runner.robomimic_image_runner.RobomimicImageRunner
29
+ dataset_path: *dataset_path
30
+ shape_meta: *shape_meta
31
+ # costs 1GB per env
32
+ n_train: 6
33
+ n_train_vis: 0
34
+ train_start_idx: 0
35
+ n_test: 50
36
+ n_test_vis: 0
37
+ test_start_seed: 100000
38
+ # use python's eval function as resolver, single-quoted string as argument
39
+ max_steps: ${eval:'500 if "${task.dataset_type}" == "mh" else 400'}
40
+ n_obs_steps: ${n_obs_steps}
41
+ n_action_steps: ${n_action_steps}
42
+ render_obs_key: 'agentview_image'
43
+ fps: 10
44
+ crf: 22
45
+ past_action: ${past_action_visible}
46
+ abs_action: *abs_action
47
+ tqdm_interval_sec: 1.0
48
+ n_envs: 25
49
+ # evaluation at this config requires a 16 core 64GB instance.
50
+
51
+ dataset:
52
+ _target_: diffusion_policy.dataset.robomimic_replay_image_dataset.RobomimicReplayImageDataset
53
+ shape_meta: *shape_meta
54
+ dataset_path: *dataset_path
55
+ horizon: ${horizon}
56
+ pad_before: ${eval:'${n_obs_steps}-1+${n_latency_steps}'}
57
+ pad_after: ${eval:'${n_action_steps}-1'}
58
+ n_obs_steps: ${dataset_obs_steps}
59
+ abs_action: *abs_action
60
+ rotation_rep: 'rotation_6d'
61
+ use_legacy_normalizer: False
62
+ use_cache: True
63
+ seed: 42
64
+ val_ratio: 0.02